Skibidicopter Project Report
Writeup of my group's project for ECE 5 at UCSB, which was to build a quadcopter.
Device Functionality
There are three main components to ensuring the quadcopter can fly properly:
Orientation Estimation
- Estimate angular velocity from the accelerometer as a quaternion.
- Read gyro angular velocity as a quaternion.
- Use the complementary filter to fuse gyro & accel; do not use the accelerometer for azimuth.
Altitude Estimation
- Uses a Kalman Filter for prediction and correction.
- Predict: Apply system dynamics matrix to state and covariance.
- Correction:
- Acceleration: Rotate by inverse estimated orientation, take z component.
- Altitude: Use barometer reading and variance from the datasheet.
Control Algorithm
- Subtract current orientation quaternion from target orientation.
- Convert error quaternion to body-frame deviations.
- Use PID controllers to calculate motor thrust.
Hardware Components
Included in Arduino Kit
- Arduino Uno R3
- MPU6050
- BMP388
- Resistors, capacitors, and wires
Other Components
- Raspberry Pi 3B
- 4x ESCs
- Buck/boost converter
- LM7805 Voltage Regulator
- 3 Cell LiPo Battery
- Flight controller, propellers, quadcopter frame
- Perforated board
Components Needed to Obtain
- Collet-style propeller adaptors, $10 for 4 on Amazon
- Leaded solder, $26 for a fancy roll on Amazon
- External flux, $7 on Amazon
- Electronic stack mount, 3D printed
- Propeller guards, 3D printed
Design Timeline
14 days to showcase
We realised we hadn't worked on the project all quarter, and had two weeks left to finish it.Thursday, 7 days to showcase
Started planning and collecting parts.Friday, 6 days to showcase
Assembled frame, removed old ESCs and residue, reassembled and soldered power.The parts arrived but didn't include the collets needed to mount the propellors on the motor shafts. They also didn't include a battery, which we had forgotten to order beforehand. We ordered a battery and propellor mounts from Amazon, but we had to wait till Monday for them to arrive.
One of the new motors had a different motor constant than the other motors, but we decided to deal with that problem in software.
The frame was missing screws and had mysterious, unlabelled ESCs that we weren’t too sure about. We swapped them with nicer ESCs we found, reassembled the frame with better cable management, and cleaned off adhesive residue and dirt.
Before soldering everything to the power distribution board, we validated that all 4 ESCs worked using a quick Arduino sketch.
We soldered the new ESCs to the frame's pads, but our iron couldn't melt the unleaded solder we had, so we had to use shit-tier leaded solder from an SMD rework kit, which didn't have a flux core. Consequently, the wires barely stuck and had flaky connections.
Saturday, 5 days to showcase
Started writing I2C drivers, Orientation code, and Kalman Filter (code day).We started writing I2C drivers for the sensors in Java but didn’t have our new electronics together yet, so we couldn’t even test our drivers.
We also began writing control code, implementing Quaternion functionality, and setting up gyro rate integration. We also started writing a linear Kalman filter that we could later use to fuse our accelerometer and barometer readings to get altitude.
Sunday, 4 days to showcase
Started electronics and more orientation + Kalman code.We realized we hadn't made a power supply for the Pi, so we designed one. We also realized that the drone's vibrations made the breadboard components loose, so would need to solder everything onto the perfboard.
Luckily, we found a roll of proper leaded solder, so we didn't need to use the SMD rework kit again. We soldered the sensors and power supply components together, using the kit perf board and a board we recovered from a botched Ethernet switch controller we found in the SecLab. The 30-gauge Kynar wire we were using kept breaking and was difficult to strip without cutting because our wire cutters were very blunt.
We wrote a Kalman filter to fuse the orientation from the accelerometer and gyro. We also worked on the orientation code.
Monday, 3 days to showcase
Realized we needed prop inserts and made drone CAD. Some minor code.The Amazon packages arrived, but the prop mounts had the wrong diameter, so we submitted 3D print jobs to the library for inserts that would allow the propellors to screw onto the motors' threads. While designing the prop mounts, we modeled the entire drone in CAD, taking the opportunity to plan the layout of our electronics stack. Placement of the IMU in the center of the frame would make our lives easier later.
We wrote a quick program to test which channel belonged to which flight controller axis.
Tuesday, 2 days to showcase
Finished electronics stack, soldering hell. Mounted electronics stack. Got orientation working with the renderer. Drivers working.The prop inserts worked! We finally assembled our electronics stack and began mounting it onto the frame using a 3D-printed adapter. However, we soon realized that we had no bolts of appropriate diameter and length to mount our 3D-printed adaptor to the frame. Even after scavenging in the IEEE labs and Computer Security Lab, we couldn’t find any bolts that fit. Instead of bolts, we used a combination of wire ties, which fixed the electronics in place vertically, and long, thin bolts taken from an Intel NUC case, which aligned the electronics mount with the frame in the x and y directions.
We accidentally mounted the electronics to the frame 90 degrees from the correct angle, which meant that the bottom of the frame obstructed the downward-facing ultrasonic rangefinder. We didn't want to reassemble the entire thing, so we scrapped the rangefinder from our design.
We discovered that our barometer and IMU drivers, which we previously had declared 'working', were very broken. We spent ages staring at the typo-laden BMP388 datasheet before realizing that the driver code had some missing brackets.
This is partially Java's fault for not supporting unsigned bytes, but if we had to do this in C, half of these operations would probably be UB or inadvertently cause truncations to bizarre, 9-bit Honeywell characters.
The BMP388 has two modes: 'normal' mode, where it periodically takes measurements and stores them in a FIFO, and 'forced' mode, where it takes measurements when they're explicitly requested over I2C. Normal mode was really broken, probably due to a weird race condition somewhere, but we eventually got tired of debugging it and decided to use forced mode instead.
Wednesday, 1 day to showcase
Motor testing with Raspi. Failed motor characterization. Tried to figure out the error quaternion -> PID conversion. Stayed up in the SecLab.We worked all night to debug our orientation software. We sent the drone's orientation to a computer via a UDP socket over Ethernet (with an IP assigned using a DHCP running on the Pi). We used JavaFX to visualize the drone's orientation.
Using a complementary filter to combine gyro rates with accelerometer data proved very difficult, especially since none of us were comfortable with quaternions. Orientation from the accelerometer is ambiguous since absolute azimuth cannot be determined without a magnetometer, which we didn't have. So, we had to extract global yaw from the gyro rates and add it back into the accelerometer roll/pitch.
Thursday, Day of showcase
Demonstrated “balance” in 1 axisWe finally hooked up the propellers to the drone and started working on code that would get the drone to fly now that all the helper classes were written and the sensor input was successfully converted into data useful to the flying algorithm. We applied the PID controller to send mapped throttle commands to the ESCs to balance on one axis. We started testing the code but ran into issues preventing all four ESCs from working correctly.
With hours left before the showcase, we called it quits and put together a demo of balancing on one axis using only two motors. At the showcase, we presented our real-time orientation renderer. The drone was too big to fly indoors anyway, but our classmates enjoyed seeing the live orientation demo.
Friday, Day after showcase
Solved hardware PWM, ESC stability. Program stop state. Learnt more about orientation.One of our most mysterious unsolved bugs was the motors randomly briefly switching on when the program was running – even if the PWM duty cycle was 0. We eventually figured out that GC pauses during the on-period of our bit-bashed PWM signals pushed the pulse width past the threshold the ESCs considered to be 'on', making them briefly accelerate the motors. We fixed this by switching to ZGC, a fully concurrent GC built into HotSpot.
We also enabled the Raspberry Pi's two hardware PWM peripherals, so only 2 of the motors had to be bit-banged.
Our understanding of quaternions and their relationship with Euler angles and rotation matrices finally started to become coherent.