Stay a While and Listen
So it's been a while since I updated. Life got in the way for a bit. I was sent to CA to do some work for my former employer, Zynga. The week I got back they closed down the Seattle office. Womp womp.That was January. Then I spent all of February interviewing around. March was rife with managing my personal life as I transitioned to Amazon. I got back into the swing of things in mid April though, so that's where we'll continue...
Goals
Last I left off, I had written my control code in a high level language and was able to get decent stabilization of a single axis. I was able to command the quad to different angles, though I couldn't bring the response time lower than a second without destabilizing the entire quad from heavy oscillations.I thought that the biggest issue here would be the latency induced by running the control code over the network (50ms RTT), so I wanted to decrease that latency by moving the control computations to the quad.
I also wanted to test on a better harness. The first single axis stabilization frame I made had too much friction and didn't support the quad correctly. As a result, the simple act of the quad sitting in the harness resulted in restoring forces being applied to the quad, which would interfere with the control code and tuning gains.
These were my two goals for April and beyond. The frame I could handle easily and would just take time to construct. The computations, however... Let's just put it this way. I knew the Arduino I'd chosen was a bit wimpy, but boy was I in for a lesson in embedded hardware limitations!
Progress & Issues
I worked on the frame and porting the control code in lock step. The frame turned out nicely, as you can see here:The video was earlier work. Through minor adjustments, counter weights, and tunings, I eventually got the entire system to a balanced state. The quad can now rotate freely, and it can hold any angle at rest (thanks to the axis of rotation passing straight through the center of gravity).
Porting the code to the flight computer, on the other hand, was tedious and taught me much. I was able to get the code moved over with minimal bugs, but it still took a while, as debugging is somewhat difficult on the Arduino without cabled debuggers. JTAG and other debuggers work for some Arduino platforms. There's a bit more overhead than usual in setting them up, however, and I have yet to put energy into this.
Fortunately, I was able to make decent headway by using printf's over debug packets. It was difficult to format anything more than integers, as fancy formatters like sprintf were off limits given their excessive memory consumption. Honestly though it was a godsend compared to LED debugging, so I really couldn't complain!
After getting the kinks worked out, I eventually learned the Arduino would need a simpler form of Math in order to compute the attitude estimates. I thought the 32 bit floats (largest precision, software only) would suffice for the matrix Math I wanted to perform. To test this, I fixed the gyroscope and accelerometer inputs for the sensor fusion filter and inspected the matrices over a few iterations. I did this for both the high level impl. and the flight computer impl. On the flight computer, I could see the matrix destabilizing, losing its orthonormal property (despite renormalization efforts), all due to less precision from the 32 bit floats. A direct port clearly wasn't going to work.
I contemplated a few next steps. I could get double precision by using something akin to double-double arithmetic (here it would be float-float arithmetic to achieve double precision). Of course, since the flight computer only has 2048 bytes, using doubles would A) increase the memory footprint and B) result in slower computations. I measured, and the matrix math code for a single update of the attitude estimate already took a whole 8ms to compute.
Another solution would be to move to simpler Math. I spent a while looking at the matrix computations to see if I could be clever and factor out operations where small numbers and big numbers were being added or subtracted ((+, -) under/overflow more easily than (*, /), after all). There were no opportunities for me to do this though.
I then decided to read up on Quaternions, as it was a clear next step for simplifying the computational footprint and dealing with the reduced precision I had. I implemented a small quaternion library in Coffeescript, which I'll probably open source, and I will be testing attitude estimation with it soon before porting it to the flight computer.
I'll probably run into precision issues again after the port, so I purchased an FPU coprocessor (I literally purchased the last one. I ordered two, but SparkFun delivered one and said they wouldn't be able to ship the other as the manufacturer discontinued this line). The coprocessor has some additional memory for Math routines and will speed up floating point computations tremendously. I imagine I can implement float-float arithmetic on it to get hardware supported float ops and pseudo hardware supported double ops, all without taking up my precious reserve of 2048 bytes or taking too long to compute.
Now I know what your thinking.. "Nick, if you are having this much trouble, why not just ditch the Arduino Fio and go for something beefier, like a Raspberry Pi?" Well, I have a pi sitting on my desk and I'd love to throw it on the rig and just use its compute power right away. I don't deter easily, however, and I'd much rather continue running up against these embedded systems constraints. This project is as much a learning experience for me as it is a desire to solve a problem. I'm actually happy that I've found a reason to play with an FPU coprocessor! Though from what I've been hearing, I'll probably need to move to Kalman filters eventually, so this is probably a moot point. Still, I'd rather not back down from what seems to be a treasure trove of experience.
In Summary
At a high level, it might look like I barely moved the needle towards my final goal. However, I learned a lot of valuable lessons after hitting all of these speed bumps. Going into this project, I assumed I could get away with my initial hardware choices as things like Ardupilot existed and the internet collective wasn't very specific on whether or not an Atmel 328P would suffice. Now I know better and can more adequately anticipate computational complexity and match the required compute power to my project's needs. Also, I learned tons of new debugging techniques that are pretty exclusive to the world of embedded development.I want to continue hacking on this quaternion implementation and see if I can get everything to eventually fit onto the flight computer. I might also start hacking on the iPhone/Android app and transponder to see if I can get some simple app connectivity to the quad. This isn't necessary now but it is fun and will be required when I'm flying and can't bring a laptop with me easily.
Happy hacking! - Nick
No comments:
Post a Comment