This is the first post in my Hexapod Project series. The point of this project is to build a robot that allows me to try out a few robotics concepts. For a listing of each post in this series, click here.
In this post, I'll cover building the hexapod body, the various levels of processing needed to keep the hexapod on its feet, and some of the code I wrote to tie various levels of processing together.
Building the Body
Instead of designing the hexapod from scratch, I decided to go with a plexiglass kit for the body. This kit (and others from the same company) seem aimed at researchers and high-level hobbyists: higher quality than kids' toys, but not expensive enough to scare absolutely everyone away. I actually stumbled across this kit while looking for high-quality servos, which would play a key part in the success of the robot. My servo experience from the Single Pixel Camera project left a bad taste in my mouth (I did buy the cheapest servos I could find..), so I opted for a set of AX-12As for this project. These servos use a serial communication protocol to not only set the target angle, but also to set angle limits, torque limits, failure modes, and request the current internal status (temperature, voltage, load, speed, etc.). They are significantly more expensive than your run-of-the-mill micro servos, but I've learned a few times now that the money you save on parts you pay later in headaches (with interest!).
The fancy boxes are a nice touch.
It's like LEGOs, but for people with more money and less imagination.
The body consists of two flat panels separated by an inch or so with various mounting holes. This leaves a good bit of space in the middle for wires and other electronics. Each of the six legs consists of a coxa (hip) joint, a femur joint, and a tibia joint. This gives allows each foot to move around in all three dimensions, although with some limitations in position and angle. The kit also comes with an upper deck with a grid of mounting holes, providing even more space for accessories. I've been a little worried about shattering the plastic components, but have yet to have any problems. The range of motion of each leg is impressive, and the overall design is nicely menacing.
Sitting pretty on a wooden stand.
Controlling the Servos
The servos receive both power and control data through a three-wire cable system. They can be daisy-chained together, so in theory, all 18 of my servos can be powered and controlled with only three wires. The same site I got my kit and servos from offers an Arduino-based controller board for these kinds of servos called the Arbotix-M. It sports an ATMEGA644p processor running at 16MHz and 5V, and most importantly, has the basic accessory electronics needed to handle communication over the servo data line. The unfortunate number of data lines on the servos dedicated to bi-directional communication (one) means extra electronics, which I am happy to let someone else figure out.
Almost like the hexapod was designed to have one! (it was)
Note, hroughout this project, I'm dealing with the servos as black boxes. Not that they are particularly mysterious -- the documentation is actually pretty good -- but more that I don't want to adjust their firmware at all or consider the possibility of improving them. So the Arbotix-M controller is the first and lowest level of hardware I need to deal with in terms of programming.
Eighteen servos are a lot to deal with, so I decided to use the Arbotix-M as a kind of aggregator for servo commands and information. Other hardware can send the Arbotix-M a single command to set all of the servos to various angles, and it handles splitting up the commands and sending them to the servos one by one. It also monitors the servos and report any statistics or faults to other interested processors. The full code I wrote to run on the Arbotix-M can be found here. I set up a small test where I sent the Arbotix-M commands over FTDI and found I could update all 18 servos at around 100Hz, which was plenty fast for nice, smooth motion.
Higher Level Hardware
So far, the hexapod had nice servos and a controller that could keep the limbs moving, but nothing giving any it interesting commands. For the next two levels of processing above the Arbotix-M, I decided to go with a UDOO Quad board. The UDOO Quad is a small single-board computer with a quad-core ARM Cortex-A9 processor alongside an Arduino Due (ARM Cortex-M3), connected to a slew of peripherals including WiFi, HDMI, Ethernet, USB, Audio, and SATA. Instead of choosing this board after hours of deliberating between the numerous single-board computers out there, I went with this one because I had one sitting around in my graveyard of processors from failed projects. The Arduino Due would act as a hardware interface between the main cpu and the various hardware components (like the Arbotix-M), and the main cpu would do the heavy lifting required to get the hexapod moving around.
With the goal of only using my own software comes the goal of utilizing multiple cores in the single-board computer I have chosen. Throughout the project I've decided to use threading in C++ to achieve this.
Packets of Information
Because the quad-core cpu shares a board with the Ardinuo Due they share a dedicated serial connection (/dev/ttymxc3 in unix-land), so there's no need to worry about keeping them connected. I added a pair of wires to connect the Due to the Arbotix-M with another serial connection (Serial1 on the Due). These three processors needed a standard way of communicating data with each other. Since I wanted to allow for any processor to send data to any other processor, I needed a standardized method of packaging data and transmitting it so that it could be directed, parsed, and understood by any processor.
I created a packet class in C++ to handle this. A packet object could be created by any processor, filled with data, and sent along with a desination tagged in the header. The key properties of the packet class are as follows:
- Arbitrary data size. (As long as it is an integer number of bytes).
- Optional data padding. Some processors have difficulty with tiny packets.
- Checksum. I only programmed for a single-bit XOR checksum, but it's marginally better than nothing.
- Optional pre-buffered data. When memory is scarce, allow new packets to take the place of old ones in memory.
- Destination Tagging. Single bytes indicating the destination, which is useful for packets bouncing between multiple processors.
The buffer holds all of the relevant information for the packet, including the size, destination, and checksum. If you don't care for code examples, you can view these packets of information as just a string of bytes, each with a specific purpose. The data it contains can be any length, but the header and footer are always the same:
Each block represents a single byte.
The three processors are connected in series and use serial communication. Each monitors the serial port that connects it with the other processor(s) and decides what actions to take when a valid packet is received. For the quad-core processor, I wrote a simple serial port monitor that runs in its own thread. It both monitors incoming traffic for packets, and accepts new packets from other threads to send. The interface presented to other threads allows for either blocking or non-blocking sends and receives. The threaded serial library can be seen here. The lower level processors can't use threading (or at least, I'm not going to try), so they just periodically monitor their serial ports and pause other functions to act on incoming packets.
The whole point of this serial communication path with standardized packets is so that you can do something like send information from the quad-core processor, have the Due see that it is meant for the Arbotix-M, send it along, and have the Arbotix-M see it and act on it. Here's an example of what this kind of action looks like in code:
First, the quad-core processor initiates communication with the Due, creates a packet with some information for the Arbotix-M, then sends it through the serial port.
The Due is continually running a code like the following, which looks for valid packets and sends them on to the Arbotix-M if the destination tag is correct (this looks a lot more complicated, but that's because I haven't made as much of an effort to hide the complicated parts behind libraries and such):
Code snippets are nice and all, but at the end of the day we need to make sure the code is actually sending the correct information. When determining whether data is being sent between bits of hardware correctly, my favorite method is to just snoop on the data lines themselves and see what the ones and zeroes look like. I hooked up a digital logic analyzer to the data lines between the quad-core processor and the Due, and between the Due and the Arbotix-M to look at the data being passed around.
If you aren't familiar with this kind of view, we are looking at the voltage of each data line as a function of time. Within each horizontal band, the thin white line shows the voltage on the line. I'm using a digital analyzer, so it can only tell me if the voltage is low or high, hence the rapid switching up and down. The horizontal axis is time, and the values above all of the lines shows the timescale being plotted.
As the probe trace shows, the three processors are successfully passing information back and forth. The quad-core cpu sends a packet of ones and zeros to the Due, then a short time later the same packet of information gets sent from the Due to the Arbotix-M. Zooming in to one of the packets, we can even see the individual bytes being sent along with what they mean:
The three levels of processing can pass information back and forth, so the next step is to decide what that information should be. The quad-core processor can tell the servos where to go through this setup of bouncing packets, but the servos don't know what they should be doing. In the next post, I'll talk about using inverse kinematics and some other tricks to decide how each servo should move such that the hexapod can walk around smoothly.