UAV progress had slowed for a while, especially during the 3 weeks I spent in China (but that’s ok because I bought another helicopter while I was there). Since I came back to the US, I’ve been working on this unit, which I’m calling sAAV (semi-Autonomous Aerial Vehicle):
1. I discovered the source of most of the problems with the yellow helicopter: the ultrasonic rangefinder boards I was using function above 4.1V, but not below. Every time I ran some debugging code to indicate the altitude readings, everything looked great. Then I would load code that actually flew, and the battery would droop below 4.1V, and all my altitude readings would be garbage. Conclusion: the ultrasonic boards must be abandoned, and I have to do altitude sending from scratch
2. Following rule #2 of Everett’s rules for tiny autonomous helicopters, I have to add a radio. Might as well make it 2-way communication so I can actually debug things instead of just guessing what’s going on
3. Autonomous vehicles are fun, but I also like remote-controlled ones
So I drew up some boards that include Nordic 2.4GHz radio modules and ultrasonic transducers. I also realized that on my previous board iterations, I had devoted nearly 50% of the board space to an H-bridge for the tail rotor, but it really isn’t important to be able to travel backwards. Certainly not worth the board space (and weight) I was devoting to it. That’s how I came up with this board:
The processor is PIC16F1824 like the last few versions. It includes everything this project needs – SPI for the radio, 4 independent PWMs, EEPROM, ADC, and more than enough code space. The 10-pin header goes to the Nordic nRF24L01 radio module. The only (populated) hardware other than the PIC and decoupling cap are three MOSFETs, one each for CW rotor, CCW rotor, and tail rotor. The stuff on the right were some guesses as to what I might need to operate an ultrasonic transducer. Since I ordered these boards, my testing has shown that ultrasonic distance measurement can’t be done without amplification on the received signal, so I won’t be populating those components.
Because of physical space restrictions, I didn’t end up connecting the radio module with the 10x2 header like this:
Instead I wired it in at 90 degrees so I could put it down inside the frame where the stock board was. I also cut off the trace antenna and added a full-wave wire whip antenna:
My first try at the code to operate the vehicle went like this:
Wait for a packet from the radio
No packet for 4 seconds? Turn off rotors
Load throttle value out to main rotors
Load pitch value onto tail rotor
Retrieve trim value from EEPROM based on current throttle value
Skew power to main rotors one way or the other based on trim value
Skew power to main rotors one way or the other based on yaw value
Adjust stored trim value in EEPROM if the packet contains an adjustment
My big innovation was storing 16 different trim values instead of one. I learned from flying these helicopters in their stock configurations that as soon as you got the trim set right, you’d change the throttle and your trim would be wrong again. To fix that, my code stores an independent trim value for each 1/16th of the throttle range.
The controller is the second version of my tiny transmitter, which was first used with the RC buggy:
The new version of the PCB includes some indicator LEDs, through-holes to mount the battery holder, and gives the microcontroller the ability to switch off power to the high sides of the potentiometers in the joystick. This means it can go to sleep in the off state instead of needing a hardware switch.
Since I want all my RC vehicles to be operated by this one controller, I had the extra challenge of operating a 3-channel helicopter from a single x/y joystick. The throttle and yaw obviously take priority, so those are mapped to stick y and stick x. Pitch isn’t as time-critical, so I have the controller toggle the tail rotor on (25% duty) and off every time the joystick is clicked in. The last command that needs to be sent is trim adjustments. To make trimming a bit more intuitive and to save hardware, I came up with this scheme: while flying the helicopter, I compensate for trim issues by steering right or left with the joystick. Then I press the trim button, and the controller sends a command to the helicopter to adjust trim in the direction that the joystick is currently pushed.
After the first flight, I read out the EEPROM trim values to see how they looked. The trim at all throttle levels was fairly similar, clearly compensating for the added drag of the gyroscopic stabilizer on the top rotor. The first flight also showed me that actively controlling the throttle with this small joystick makes hovering very difficult. The next iteration made joystick movements adjust the throttle level up and down, instead of the throttle level tracking the joystick position. After all these changes, I got the vehicle to the point where I could actually fly it, but yaw trimming just doesn’t work. I can spend a bunch of time getting the yaw trimmed just right, and it will hold a heading for a few seconds, and then just start spinning. Then I finally figured out what the mystery component on the daughter board in the stock electronics is:
MEMS GYROSCOPE! I couldn’t believe a gyro could be so cheap that it’s included in a $20 helicopter, but it is. I figured out the component’s function by putting its output on the scope and moving the board around. It’s clearly a gyro and not an accelerometer because the signal tracks rotational speed through the entirety of a rotation (including when the rotational acceleration is zero), instead of showing the start and end of the rotation as a rotational accelerometer would.
I pulled the gyro off and wired it into my board. I rewrote most of the yaw control code since I’m now taking yaw adjustments from three sources (trim value, controller command, gyroscope) and included adjustable gain coefficients for each term. To my amazement, the first time I flew with the gyro on board, it actually started correcting for yaw drift! But the gain was way too high, leading to oscillations that eventually go unstable. Since the gyro signal’s zero level changes with input (battery) voltage, I maintain a long-term moving average of the signal and then get my current reading my comparing the instantaneous to the long-term.
I then spent way too long tracking down a problem with the radio (turns out you have to read out the RX FIFO buffer even if you don’t want the data, or the radio will start rejecting incoming data and never tell you it has new packets). My debugging work is all done on this proto-board replica of the helicopter electronics:
Working with this setup also showed me the presence of a really dangerous bug: if the radio dies, the MISO line goes high, so all bytes received over SPI are 0xff. This means host software will always think the flag indicating new data is high, and all the new data will 0xff. Because of the way I have my control values encoded (the simplest way), this means that if the radio dies, the helicopter turns all rotors on maximum (and turns right) (and adjusts the trim value left) and never registers that the radio link has died. Yeah, that was one loose wire away from a fourth helicopter flying off into the sunset never to return. Now I recognize the 0xff values and interpret them as a loss of radio link.
Now that I have the gyro working, I need to spend a little time adjusting gains to get flight totally stable. I'll put up a video when it flies better. Only then can I start working on full autonomy. On the side I’ve been working on using ultrasonic transducers to measure distance under my own software control (but yes, it needs active amplification on the receiving end). Won’t it be awesome to just set an altitude with the controller instead of having to constantly adjust throttle to hover!?
Also, this one uses the motor coils to play Ride of the Valkyries when it powers up. As all helicopters should.
Here’s the code that runs the helicopter and controller. It’s mostly unstable from the gyro measurements, but it’s almost there.