Hacking the APM2 Part #2: Fun with Baud Rates
The autopilot architecture I am building involves and APM2 collecting all the sensor data and sending it over a serial connection to a Gumstix Overo running Linux, and then in return the Overo sends servo commands back to the APM2. As you can guess, this turns out to be quite a bit of data being sent at a high rate. If all the processing, filtering, and computation is being done on the Overo it is important to have a high update rate, low latency, reliable communication, and no major pauses in processing at either end.
In this architecture, the APM2 isn’t running the DCM filter. It is not logging to the SD card, it is not communicating with the ground station. It is simply collecting all the sensor data and blasting it across the data pipe to the Overo.
First let’s do a little math. I have 6 packet types (so far) of varying sizes: Pilot Input = 16 bytes, IMU = 14 bytes, GPS = 36 bytes, Barometer = 12 bytes, Analog Inputs = 10 bytes, Actuator Commands = 16 bytes. In addition, each binary packet has 2 start bytes, a packet ID, a size byte, and a 2-byte checksum — a six byte wrapper. Assuming we send one of each packet, that would be 140 bytes. Assuming we want to send a set of these packets 100 times a second, that is 14,000 bytes per second. (I realize we don’t send the gps data at 100hz, but for the sake of simplicity and margins, let’s pretend we do.) Now assume for the purposes of computing required baud rates, there are 10 bits per byte. That means we would need to run at least at 140,000 baud to handle all the traffic we need.
Many software packages (including the arduino dev system and the arduino docs) seem to think that 115,200 baud is that maximum rate possible, so this could be a problem!
The next “standard” baud rate above 115,200 is 230,400. It turns out that the APM2 FastSerial code happily let’s me specify this baud rate and my Linux based Overo does as well. I tested this with the USB console connection and it works. (Or so I thought.)
My next task was to get the communication link running on a direct UART connection. This will simplify aircraft integration, reduce cable weight, etc. However, as soon as I moved to a direct connection, the APM2 could no longer see data coming back from the Overo at 230,000 baud. What is going on here?
The standard baud rates since the beginning of time have always been 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115,200, 230,400, etc. However, when I dug into the Arduino FastSerial code and the Mega 2560 data sheet I discovered that the Arduino does things completely differently. Baud rate is defined relative the system clock divided by an integer value. When you ask for 115,200 baud, FastSerial crunches that value through a funny formula, comes up with an approximate integer divider and sets the baud based on that. So when you crunch the math, what baud rates does the Arduino actually support? It turns out you can do: 1,000,000, 500,000, 333,333, 250,000, 200,000, 166,666, 142,857, 125,000, 111,111, 100,000, 90,909, … etc.
So here’s the funny thing. The uarts at both ends have some flexibility and can successfully communicate even if the other end isn’t exactly on the correct pace. When you request 115,200 baud on the APM2, you are actually getting 111,111 baud! But the other end can usually handle a little slop and no one ever notices. The problem is that the arduino cannot do 230,400 baud — the options are either 200,000 or 250,000 baud and both of them are too far away from 230,400 to handle the timing slop. In my case, the Overo actually could handle the APM2 output, but the APM2 couldn’t read the 230,400 baud incoming data from the Overo.
!@#$@#$!@!!!!
The next question I asked myself was how hard would it be to rewrite the linux serial drivers to support different baud rates, but quickly I decided that was just a path I didn’t want to head down unless there was absolutely no other option.
Finally I started looking to see if there were any matching (or very close) baud rate options faster than 115,200. The only one I could find was 500,000 baud. But that’s crazy fast and couldn’t possibly work, right? Well if my other option is to start hacking linux serial drivers, I decided I could at least set the baud on both ends to 500,000 and see what happens.
And the answer is: IT WORKED!
So I’m running 500,000 baud between the APM2 and the Gumstix Overo.
There are questions though — can the APM2 keep up with this? Can the Overo keep up with this? What happens if the system is busy processing interrupts. Are the ring buffers big enough to keep up? Will we drop data? Are there other hidden places where this could all break down or blow up? I think the answers to those questions remain to be worked out and depend quite a bit on the loads at each end of the pipe. This gets back to the delicate balance required by embedded systems and the requirement to carefully work your way forward and understand the systems at all levels in order to achieve a solid result.
Question: if 100hz updates require about 140,000 baud, then 200hz updates would require 280,000 baud — hmmm. Does this mean it would be possible to sample the sensors at 200hz, send the data to the 1Ghz Overo at 200hz, run my kalman filter attitude estimate at 200hz, run my autopilot PID’s at 200hz, my navigation code at 200hz, and send actuator commands back at 200hz? (Assuming digital servos that can respond to PWM rates as high as 400hz.)
Update: September 4, 2012
I have reconfirmed that the Gumstix Overo cannot transmit two serial streams simultaneously. I use one serial port for the APM2 interface and one serial port for the wireless ground station interface. The issue is that if I dump a big chunk of data @ 115,200 baud from the flight computer to the ground station, no APM2 communication can happen until that transaction flushes through. The strategy I will employ for now is to stuff ground station messages into a local (user space) buffer and then trickle a few bytes out per frame to the serial driver. This forces me to pace the ground station messages so I don’t overflow my application level buffer (which would reject new messages until there is sufficient space freed up) but that is something I need to do anyway because there is finite capacity in the bandwidth to my radio modem and the radio modem itself has finite capacity which diminishes with distance. Just another reminder that with embedded systems we can’t have everything we want simultaneously, and the trick is to find a good (and delicate) balance between all the different components and needs.
Update September 7, 2012
Life is never easy! I have discovered the Overo serial drivers in the Linux 3.x kernel now work a lot harder to match the standard baud rates very closely: 115,200, 230,400, 460,800, and 921,600. None of these are a close enough match to the APM2’s list of nonstandard baud rates — 200,000, 250,000, 333,333, 500,000, and 1,000,000. This is a major annoyance!!! The Overo serial drivers in the Linux 2.6.x kernels seemed to do an actual 500,000 baud, but unfortunately that is gone. It all has to do with clock dividers and stuff.
What I have done for the moment is work on trimming some of the fat off my binary packets and I’m sending data at 115,200 baud which does (thankfully) still work! This is sufficient for my current needs so I can move forward, but it does quell any thoughts of running everything end-to-end at 200hz on this layout. But perhaps with some attention to trimming more fat and perhaps only sending core IMU data at 200hz we could still build a system that inherently has 200hz throughput where it matters.