True Story Follows
A few years ago I worked on an autopilot module for a blimp and subsequently presented a PyCon talk about it. Like any developer looking back on previous works, I think the code was horrible and the talk could have been more substantive as well. The lime green Hawaii shirt, however, I have no regrets about. Sometimes I wear it to work and people tell me that they can’t hear what I’m saying with how loud my shirt is. It is true, I have even louder shirts that my wife got me for Christmas, including some wicked tie-dye and some absurd dye-sublimation designs.
Fast forward a few years, and everyone on the streets is clinging to Go as the hottest new programming language. So when I tried picking up Go, it was somewhat challenging because I had no practical reason to learn. My project at work will be stuck as a Python project until we undergo the deliberate and painstaking effort to re-write it in a compiled language. This effort to learn the language coincided with a desire to go back to the blimp project and revive the effort to bring it to a reasonable conclusion.
The Shortcomings of Last Project
The largest pitfall of my last endeavor to create an autopilot for the blimp was that it was not easily reproducible. I bought individual sensors and had to wire them up to a separate singleboard computer, and I had put together a laser cut acryllic design to house all the pieces. Then there was corresponding nylon screws and bolts to mount evertyhing. If I ever wanted to create a multitude of autopilot modules, this would become a painstaking and problematic effort for something that was a side project and a hobby. And obviously, the end goal is to deploy thousands of autonomous blimps to rule the skies, so an unscalable solution was not a solution at all.
The other project, while capable of basic flight, was not that great, which was amplified more after working on my current project. Calculating a compass heading, for example, relied on sterile conditions where the aircraft was completely level in the skies, which is obviously not a valid assumption. Otherwise, the basic ideas of flight were reasonable.
And finally, the last project lacked a reasonable user interface. I put some stuff together with OpenCV, but it was nothing that looked professional, and nothing to help diagnose any problem going on.
The Go Re-Write
I re-wrote the entire autopilot algorithm in Go from Python for a few reasons.
My first concern was how to go about reading from sensors. This pretty much required C++ usage, so I could either write everything in C++ or just use a language that was easily compatible. Go is advertised as a language that can do this, and the final implementation proved that this was relatively straightforward.
Another concern which was later validated is that the autopilot would be a multi-threaded environment. I didn’t want some sort of whacky scenario where the serial port blocked for a prolonged period for whatever reason and subsequently prevented the autopilot from controlling the aircraft. The only scenario in which some sort of crash would be desirable is if Kraft foods hired me to paint a 30 foot blimp like the Kool-Aid guy and have the blimp ram full speed into and through a brittle wall. Go is a reasonable language choice for an application with multiple concurrent processing tasks, and while it’s fairly obvious that motor control, computing waypoints, reading from sensors, and reading from user input could significantly benefit from executing independent of each other, it turned out that my autopilot implementation was inherently asynchronous in virtually all aspects, and combined with the long lived nature of the program, Go proved to be an excellent choice.
The last reason to use Go was for the simple matter mentioned above that I need to learn Go, in part because I want to, and in part because it’s being used frequently at work so I should at a minimum be able to intelligently understand Go code and ideally be able to write my next project using it.
And finally, another point in Go’s favor was that it would be faster and use less resources. I’m not necessarily concerned with either, but faster code is always good and in this case may be able to enable better navigating with higher frequency computation. Additionally, if I can use less compute resources, I can use less battery and therefore prolong flight time.
Requirements and Features
In its current state, the blimp is not yet 100% complete, but the bullets enumerated below represent either a completed feature, an implemented but untested feature, or a feature with a small enough scope that it’s safe for me to promise its completion.
- A client laptop is assumed to work in conjunction with the autopilot.
- Blimp will take off vertically and hover until waypoints are specified
- Communication range between laptop and blimp is a function of the XBee modules used (the ones I’m using can work about a mile away if there’s line of sight communication)
- Feedback from the blimp, such as target heading and output sent to the aircraft’s servos and motors, is passed back to the client; this proved invaluable for testing and diagnosing problems.
- Waypoints are specified in real-time by clicking on a map on the user interface (it’s assumed the client laptop has internets)
- Waypoints can either be consumed as the aircraft reaches them or the aircraft can simply loop through all waypoints continuously
- In the event of lost communication, the blimp will immediately shut off its motors and pitch downward
- The compass needs to be re-calibrated whenever the geographic region changes; we can’t all live in the Bay Area. Calibration starts and stops with the press of a button on the user interface, and the autopilot module just needs to be rotated in all directions until a reasonable heading is established.
- When not actively flying, the user interface has buttons to individually test pitch, yaw, thrust intensity, and the thrust vector
- PWM min and max ranges (which can vary by servo or electronic speed controller or can have different ranges based on aircraft nuances) can be adjusted on the client side
- Blimp will cruise at 5 miles per hour to minimize power consumption; blimp will slow as it approaches a waypoint.
- Blimp will fly 25 meters above ground level
Major Parts List
Mistakes in Current Implementation
It is true. Even The Lobbdawg makes mistakes. From the initial flight test, there were a few noticeable problems.
Forward speed is embarrassingly miscalculated depending on the quadrant the azimuth of the blimp lands in. This caused speed to be represented as a negative value, which then caused the blimp to compensate and try to increase speed. And now, the blimp would fly too fast, like the gazelle.
The algorithm for hovering was based entirely off of incorrect assumptions. The idea was that if the pitch of the blimp is 0 and I can keep the pitch of the blimp at 0, then I must be hovering. This assumption is incorrect, but it’s also very difficult to take off in a hover based on this sort of flight strategy.
The determining factors for thrust intensity are also currently incorrect. The idea was that the thrust vector for the motors should always be at a neutral (roughly 45 degree) angle, and altitude would be controlled by the tail elevator. Therefore, while the motors would still provide upward thrust, I only adjusted the thrust intensity based on the current forward speed of the aircraft. This created the problem that if the blimp was flying too fast, the motors would shut off, and the blimp would slowly descend in altitude while the tail elevator and rudder couldn’t direct the aircraft as well with the reduction in power.
Finally, in some cases during flight, control of the motors was completely lost if a preceding operation kept the motors powered down. While I’m still speculating somewhat here, my guess is that the PWM update frequency should happen at 50 hertz. Currently, the update frequency is directly correlated to sensor read frequency.
What Went Well
While the test flight to me felt like a failure, it certainly wasn’t because I learned enough to know how to correct the problems. On top of that, it was easy to take for granted some of the now proven assumptions. Primarily, the math used to calculate output to the tail elevator and rudder proved to be spot on. The blimp was able to fly directly to waypoints and cut directly through the target. If the blimps can be considered arrows, then you could consider me to be Robin Hood, but only in the sense that I can shoot arrows really well. Not in the sense that I’d steal money because I don’t want to go to jail. Also I think Robin Hood dies in the end.
But I digress. The point is, pitching and yawing is based on the idea that we have a curent angle (in either the pitch or the yaw plane) and a target angle. From there, we have a target angular velocity. Based on the current angular velocity, we can then output a corresponding intensity percent to the motors. As a result, we can safely compensate for things like wind, and we can counter intuitively pitch or yaw in the opposite direction of the intended target if the aircraft is spinning too fast.
Another point to put on the scoreboard is the effort in putting together all of the details in the user interface proved incredibly fruitful. While some of the view data was based on an idea of putting something cool looking on the display, it ended up helping to diagnose and reveal the problems mentioned above. Had it not been for the user interface, I would only be speculating and postulating what the problems were.
What’s Next
Put simply, I’m going to fix the problems listed above. They’re all fairly straightforward to address except the problem around thrust vector angle and thrust intensity.
After thinking it over, I’d like to rework the flight algorithm differently. Blimps are notoriously a combination of flying a helicopter and flying an airplane (you could think of these RC blimps as a crossover between an MH-53 Pavelow and an F-14 Tomcat, the performance characters are comparable), so it ends up making sense to simply decouple those respective components and let them operate entirely independently. In practice, they’ll be coupled together by happenstance and equilibrate to an ideal flight configuration. The current algorithm for controlling altitude is based purely off of pitching the aircraft up or down through the tail elevator. This is fine; now, the motors should operate independent of the elevator to help manage altitude and forward speed.
The thrust vector will be computed first. I know I can already ascertain a target upward velocity to reach a target altitude, and I can already ascertain a target forward velocity to reach a target forward speed. Throw some arc tangents into the mix, and now I can get the ideal thrust vector to apply forward and upward forces in the correct proportions. Now I can just offset the resultant angle with the current pitch of the aircraft to apply forces in the perfectly correct directions.
Finally, thrust intensity can be computed in a similar way; get the required forces to apply in the forward and upward directions, and the final force applied will be the square root of the sum of squares.
This sort of approach I think will end up creating a very elegant flight pattern. Target speed can also take altitude into account, where target speed will be a function of the current implementation of target speed interpolated against the current altitude relative to the target altitude. In plain English, the blimp’s target forward speed will increase from 0 to max as it ascends, which will end up causing the blimp to rise vertically and then gradually speed up as it reaches a safe altitude.
In normal flight where the blimp is flying straight forward to a target destination, the thrust vector will have reached perfect equilibrium with maximal efficiency to distribute upward and forward forces (in practice this means that the thrust vector will be at or near parallel with the blimp’s air frame).