Sunday, May 27, 2012

My Home Brew Robotics Project

As a home schooling parent, one of the big challenges is a lab class such as AP physics. There are suppliers like Pasco http://www.pasco.com/ or Vernier http://www.vernier.com/ that supply most of the lab bench equipment that you find in high school and college classrooms. If you go to these sites you'll see that the prices are prohibitive for working families. My goal started out to be the creation of a low-cost measurement and data-logging system equivalent to the Pasco's 850-Universal Interface or the Vernier's LabQuest2.

The way to reduce the costs for such a system is to have an easily reprogrammable control box complemented with a wide range of sensors, actuators and motors, all sharing a common serial interface. Implementing this common interface required that each peripheral device (motor, load-cell, timer, temperature-sensor, and etc.) all had to have an embedded microcontroller. But as soon as I did that, I realized I created a system with distributed intelligence; and besides measurement and data logging, it was also going to be a great platform for homebrew robotics.

Having a robotic system based on smart motors, sensors and actuators that together comprise a system of distributed intelligence is not one that I recall ever seeing built? But from a hobbyist point of view, it is a very powerful but inexpensive way to go.

Several developments come together to make this design concept work. Having smart peripheral devices (PDs) means that a lot of the control box's computational burden can be off-loaded to the individual plug-in devices; the control box (CB) turning into more of a task manager, interacting with the PDs via a small set of high level commands. This simplifies the programming that the CB has to run and reduces the complexity of the required CB hardware.

The Control Box .  PCB is 4.5" wide by 7.0" long.
The hot-swap plug-n-play serial interface is loosely based on USB, rev: 1.10. It is still a star-tiered arrangement, with levels and hubs, but I dropped most of the USB-protocol stuff. Instead of the 5V USB standard power, I went with 12V. This allows for larger motors to run off of bus-power alone. I settled on a 1.0-MHz bus clock, but still have the 1-ms time stamp sent out every frame. Using this time stamp, each of the PDs can sync their clocks to within a few microseconds of the CB's and each other. USB rev: 1.10 only gives each device one unique address, but I've given each PD four addresses, one that is unique for control and three that can be party-line address so that the CB can broadcast simultaneously to functional clusters of PDs in one command.

The CB is a micro-core combined with the serial interface's host controller. The micro-core instruction set includes machine level ones that interact directly with the host controller. This simplifies communications programming tremendously. The host controller supports 8 ports. Each port can support an additional 8-port hub for a total of 64 ports maximum. The design goal was to have a CB with sufficient I/O and short enough response time to be able to implement a robotic hand+forearm system or a four-legged walker.

The micro-core plus host controller is written in Verilog and ported to a Lattice XP FPGA part. The core itself is a stack-based engine with a machine instruction set reminiscent of the RPN language Forth. Because the machine instructions are Forth words, this micro-core can run Forth programs very fast; while rendering the compiling of Forth code source files into machine instructions a straightforward task. There is no programming software, running on a PC, needed for its operation.

Since Forth is based on a dictionary of words, part of the handshaking that goes on between a PD and the CB after plug-in is that the CB uploads, to its dictionary, from the PD, the low-level command words it needs to talk to that PD. Then, with only a few lines of source code, a homeschooler and/or their parents can have their control box programmed and up and running. Another benifit from this is that the CB's programming doesn't have to know anything a-priori about any PD being plugged into it. Anyone can make their own custom PDs, and as long as they conform to the proper programming template, whatever they make will plug in and run on the CB.

Since the PDs all have internal ram, an option that I want to add in the future is for each PD to store pre-defined tasks that can be initiated on a given time stamp; a kind of muscle memory. Complex motions can be orchestrated by pre-loading a functional cluster of actuators and sensors with predefined instructions then initiating that motion by one broadcast command from the CB. Coordination is guaranteed because all of the PD's clocks are synced to the CB's clock. This also opens the door for the ability to learn. A motion can be tried out, then modified and reloaded into a functional cluster of PDs for another try until a complex action is perfected.

The micro-core kernel is still very much a work in progress. At this point it occupies about 30-kbytes of memory. Yes, that is kilo not mega. The system clock is only 4.0-MHz, but I still can maintain a 1-ms input/output response time. The individual PDs run a reduced instruction set version of the micro-core. They are also ported to a Lattice XP part.

I've had this entire system, as described, running on my lab bench for the last year and a half. But work and finances have not let me finish until now. The plan is to get back to work on it this summer and make some more progress with it.

This is a postscript for those familiar with the Forth programming language. One of the great things about creating your own micro-controller/processor in Verilog or VHDL is that you can give it special machine instructions that will optimize it for the control functions it will be performing. In this case, I've added several features to the micro-core to optimize the CB for coordinating the multiple concurrent tasks parceled out between the various PDs plugged into it.

I've introduced a programming object I call a task. It is a C-like structure that starts with an index pointer, followed by a list of code-pointers that can be accessed via the index pointer. Next, to deal with these structures, I've extended Forth by adding two extra features. First is to add two new Forth words: t@ and t!. These are memory access words that are offset from a base address. Then I've introduced a class of words I prefix with a (t.). These designated words expect a base address stored in a special register, with all t@ and t! commands contained in the definition of these words, offset from this base address. This allows t-dot words to be re-used by any task that calls it via one of its code-pointer entries.

The other feature I've added into the Verilog code is the addition of multiple data/return stack pairs. Forth users are all aware that task switching can happen very fast by simply switching the processor from one data/return stack pair to another. To implement this feature, I've added four data/return stack pairs to the micro-core; and have added a machine level instruction to bank-switch between them. With this addition, I can task switch in three machine instructions: (1), push return address to return stack; (2), bank-switch to next data/return stack pair; (3), execute a return instruction.

One of the great unappreciated features of Forth is that it lends itself to a verbal command style. All too many Forth practitioners focus on the minimalist nature of Forth programming and have thus done a lot to give it a reputation as a write only language. But once you get away from all of the DUP SWAP */ stuff, and build up, more and more complex word definitions, a Forth program can take on a very verbal and intuitively understandable form. By having the CB upload all of the low-level command words from the individual PDs, it frees the CB to be programmed at a much higher level and in more verbal style of Forth.

So the goal for this next summer is to see if I can build some kind of extension of Forth that includes these t-dot words.  If I can do this, then I should be able to program my robot control-box by just talking to it in t.Forth sentences.  For example, I could say to my robot t.GoToEndOfHall t.TurnRight and my robot would perform that sequence of commands just as if I had typed it in on a keyboard.