At the time I wrote last, I had great intentions
of following up with some details on my research. At that point, I thought I was within a week
or two of having my robot airborne. Now,
I don't know about you, but I sometimes feel that despite 36+ years of experience
on this planet, my mind has a fairly fragile grasp of the relationship between
the amount of work I have to do and the time it will take. I console myself with the idea that I can't
predict the vagaries of circumstance.
For example, on renovation: how could I know that the wallpaper would be
glued directly to the drywall without even any primer (who in their right mind
would do that)?; on car repair: how could I know that the starter constitutes
an effective motor mount and that, on removing it, the motor would drop (ever
so slightly) and thereby make installation of the new one virtually impossible
(without a jack)?; on building my ramshackle boardwalk: how could I know that I
could only carry one 12-foot long, 100-pound log at a time? (okay, that last one I did kind of know, but
I thought I'd be able to walk faster...) (note: all of those are from the past;
no renos, no car repairs, and no boardwalk construction for me here in
Kiwi-land) So, yeah, things generally
conspire against me (says I) and I have my own internal selfish motto: Nothing
is ever easy. And the implied
perspective there is that "Nothing is ever as easy as it should
be". And here, once again, my
faith is revealed for the weak thing that it is. The truth is that things are generally a lot
easier, finer, better, and more wonderful than they could be. What they should be is up to my Maker
and not up to me and for that, I would do well to be grateful (alas, easily
said, seldom done).
Weeks and weeks have gone by and, I'm very pleased
to report, my bot can now fly! Despite
the fact that quadrotor flight is pretty commonplace these days, it's
surprisingly complex and tricky to manage it from only parts and first
principles. When I arrived at the
University in May, I was presented with an untested circuit board and a bare
airframe. First step was to try out the
board and get the microprocessor working with all of its peripherals. I've done some coding in a computer language
called 'C' in the past, but this was my first foray into development for a
small embedded processor. And it's been
a long time since I thought that 16MHz was a respectable processor speed or
8-bit a respectable architecture.
Regardless, it was lots of fun to be employing my skills in a whole new
area and I spent the first month or so writing code that would enable the
microprocessor to talk with its GPS (used for outdoor positioning), its sonar
rangefinder (used for low-altitude height control), its Inertial Measurement
Unit (used for orientation and acceleration measurements; the key component for
stable flight), its current & voltage sensors (used to determine battery
and motor state), and its wireless RF communication module (used for
bot-to-base communication). I found a
couple of small issues with that control board and the University technicians
undertook to make a new one for me.
While I waited for that, I turned my attention to
the airframe. it consists of a
plastic/nylon platform in the center with 4 carbon-fiber rods radiating out
from it. At the end of each rod is a
platic/nylon motor mount that holds a small electric motor. (The airframe was designed by an AUT
mechanical engineering student and the plastic/nylon pieces were made in-house
on a 3D printer - very cool.) Each motor
requires an Electronic Speed Controller that basically allows low-current
digital (microprocessor) control of an electric motor that requires high
current (lots of power) and is inherently somewhat analog. So, I wired those up and got them all
attached to the airframe. By then, my
microprocessor board was back and the next step was to wire all of the bits
together. That was all done by sometime
early-mid July.
With a fully assembled Quadrotor robot (long
since dubbed QBot1 (QBot0 and QBot2 are in the hands of the undergrads)), it
was time to start writing motor control code to see what it could do. With not uncharacteristic hubris, my first
attempts were largely intuitive and I somewhat ignored the careful equations my
thesis advisor had provided me with. I
quickly determined that a quadrotor aircraft is a highly unstable thing. We have a testing stand that the robot sits
atop and that prevents it from flying into a wall or ceiling while still
allowing free rotation around it's roll (left/right tilt), pitch (nose
up/down), and yaw (compass heading) axes.
On that stand, the whole airframe would gyrate like a thing
possessed. Many days and much analysis
later, I was able to get it somewhat stable so long as I held it level when
powering up. To call it
"stable" was a bit of a stretch, though; my kids exhibit about the
same amount of stability when it comes to their emotions. A content and happy child one moment is only
a slight nudge (for example, the word "no") away from hysterics the
next. Just so with temperamental QBot1. I tried and I tweaked, I blamed
center-of-gravity imbalance and testing-stand interference, but in the end, it
was mostly me (as it almost always is).
I had the wrong sign (+ verus -) on one term of
my control loop and I believe that was the majority of my problem. In the effort to track that down, though, I
rewrote everything and applied the principals and equations originally given me
by my advisor (Dr. Loulin Huang who holds two Master's degrees and a PhD in
mechanical engineering and is one of the most natural and enthusiastic applied
mathematicians I've ever seen). Doing so
gave me much better grounding in the principles governing our system and forced
me to clean up my code. With that, I was
able to get QBot1 rock solid on his tethered stand (sans battery; which is
significant) and I was certain that free flight was only a week or two
away. That was when I wrote my last
post.
The next step was to sort out the dynamics for
free flight at the high thrust rates needed for takeoff and hover. For that, I had devised a bearing clamp
system that would limit my bot to rotation around a single axis (so that I could test pitch control only, or roll control only). Early in my tuning and re-testing, though, it
became apparent that my motor mounts wouldn't be up to the task. At the higher (load bearing) RPMs, they would
vibrate immensely and it was clear that they wouldn't hold up when
airborne. The young man who had designed
them quickly developed new ones but we had to wait a couple of weeks for the 3D
printer to have an open slot. In the
meantime, I started adding features to my code to improve communication
robustness; I had noticed that my wireless base-to-bot link would occassionally
drop information and that could obviously be disastrous during flight. So, I added checksums to the communicated
data (a checksum is a very simple data integrity method that adds together the
inherent numerical value of all parts of a computerized message; only if the
sum matches between sender and receiver is the data accepted). But then things really started to get weird.
With the checksums in place and a fresh overhaul
of my code structure, I suddenly started getting random reboots, crazy branch
paths, skipped instructions and the like.
By changing a few debug messages things could work better, or worse, and
the connection between cause (a new change) and its effect entirely broke
down. My first suspicion was that I was
the victim of a somewhat common computer issue known as stack overflow. That occurs in situations where memory regions
are too close to one another and/or a program has too many nested subroutine
calls (where one calls another that calls another...) (quick description: each
time a program calls a subroutine, it sort of sets aside what it is doing in a
place called “the stack” and when it finishes up, it picks up where it left off
by grabbing that work back off the stack).
Many security issues through the years have come through exploitation of
stack overflow vulnerabilities. In my
case, I was certain that my problem was elsewhere; a modern programmer
generally relies on his compiler to manage the stack and I was certain that
mine would be doing the right thing (I'm using a targeted version of a compiler
called gcc - it's known to be very good).
Since I had lots of available memory (I was only using 10% of the
total), there's no way the compiler would place my stack anywhere near my
program instructions. So, the problem
was presumed to be with my code and I figured that I had stuffed up my memory
references somewhere along the way. I
searched and searched and couldn't find anything. I wrote and rewrote my program, I went down
one rabbit trail after another and I was starting to wonder what more I could
do.
In the end, by painstakingly watching the
processor execute one instruction at a time; I directly observed the source of
my trouble. Guess what it was. Stack overflow! Not possible, says inner-me! I have too much memory for that to be the
problem! Ah yes, says the wiser me that
only speaks in hindsight… but did you check the processor specification? Well, no… but the company who made the chip
and wrote the specification also provides the compile environment. Why would they lie? Because they made a small mistake and they
calculate memory usage not based on what you really have but what you could
have. So, that was it. What was reported to me at 10% was actually
100% and I was out of luck. Others have
reported the issue to the manufacturer and they have fixed it for themselves,
but have yet to roll it out to customers.
C’est la vie, non?
Anyhow, with the communication path up and
working, I was back to the races. I
spent some time tweaking my motor control parameters and then spent a bunch of
time writing a base station program for windows. Included with that were the routines to allow
remote control of the bot via my playstation sixaxis controller. After much testing, I put the bot back on its
stand and ran it at liftoff thrust; it went crazy! Bleh!
Can we please get a competent engineer in Aisle 1? …
Amazingly, though, I changed 2 control parameters and Blam-o (technical
engineer jargon for: Eureka!)! Rock solid on the stand; with the battery! I could hardly stop grinning.
The next step was to take it off the stand and
attempt the first flight of Qbot1. I
only managed a few inches and things looked very, very good. Nice, stable holding of position parallel to
the ground. It would drift a fair bit
because I have no position controls in place yet but I headed home for a happy
weekend. The next Monday, I tethered it
with string to prevent it from drifting out of its cage (or into the walls) and
was able to fly it several feet in the air.
It’s pretty cool. Many people
came by to see it work and I had fun showing it off.
But now it’s in pieces. I have accomplished a lot and the undergrad
working on Qbot2 had a controller but no airframe. So, I donated mine. Hopefully in the next few weeks, it will be
flying again. Meanwhile, I will be
building a new airframe and need to spend some time understanding the advanced
theoretical formulas that we will be applying next. Without the grounding in Kinematics and
Dynamics that mechanical engineering students get, I have a lot of foundational
concepts to work through. The fun doesn’t
stop…



Dan....that is super cool! Makes a great racket too!
ReplyDeleteHey Dan, this was fascinating to read! It sounds like great fun working on this. I've always been interested in physics, math, flight, and programming, and this combines all 4! How is it doing debugging on an embedded process? Sounds like hunting down bugs (especially that stack overflow) would be a real challenge. I can't wait to see the next steps, which I assume would be trying to control some of the direction in flight? Wish I could work on it with you!
ReplyDeleteHi Jason, debugging is actually pretty good; it can be done in-system via a JTAG interface. The micro we're using is made by Atmel and the latest version of their development environment is based on Windows Visual Studio. So, it's actually pretty slick; well integrated and with great features like breakpoints, instantaneous register values, and disassembly. The problem I had is that they don't have source code for built-in functions (like printf), so you're forced to debug from assembly. That wouldn't be so bad, except for the fact that, without source code, stepping through assembly resets the assembler view and you have to keep telling it to take you back to the program counter (after each instruction). So, it's a bit tedious.
ReplyDeleteYes, next steps are: 1) Assemble new airframe, 2) Height control (via sonar range finder), 3) Position control. I'll keep you posted. (I wish you could help me, too! That would be a ton of fun!)