Real-Time Linux Quadcopter
July 2022 - September 2022
I had an old quadcopter lying around that was missing a flight controller. I decided to try to build my own out of a pwm controller, a Raspberry Pi and an IMU made up of a gyroscope, accelerometer and a magnetometer. I started this project in order to learn about real-time Linux and become proficient with it. My end goal is to have a simple flying quadcopter whose code runs entirely in the Linux userspace. Currently it uses preempt-rt as the realtime implementation and it runs on a custom version of buildroot.
Linux
For those that are unfamiliar, Linux was first and foremost designed as a desktop operating system kernel, this makes it traditionally poorly suited for real time applications. The goal of the Linux process scheduler (the part of the kernel that decides what processes will be run at any given time) is to make the computer seem responsive to its user(s). This is all fine and dandy for a laptop trying to make sure that its users game of go-fish is not interrupted by a web page running on a web browser, but it poses an issue for a couple pound object precariously hovering a few meters above the ground. It is necessary to ensure that Linux will always reliably update vital hardware system (like motor controllers) based on a steady flow of data from sensors (like gyroscopes) so that a small bug that causes a wireless driver to hang does not make the device rocket off into the stratosphere, starved of sensor information and motor updates. Traditionally, this problem is solved by adding a simple microcontroller co-processor that handles “real time” tasks like managing motors based on sensor inputs and forwards data to the more complex Linux system that might handle user input. Recent advancements in the Linux kernel have brought about changes to the kernel such as the PREEMPT_RT scheduler that make it more suitable for these kinds of tasks.
PREEMPT_RT allows Linux to normally manage processes most of the time using its “Completely Fair Scheduler,” but occasionally interrupts the process, disables most software interrupts and deterministically executes a set of instructions that, for example, polls sensor input from a gyroscope and controls a set of motors that allows a quad-copter to stay in extended level flight. PREEMPT_RT is still in a beta state that is unsuitable for managing human life, like in a car’s airbag system, but it works well enough for most things. In the famed words of our BDFL Linus, “Controlling a laser with Linux is crazy, but everyone in this room is crazy in his own way. So if you want to use Linux to control an industrial welding laser, I have no problem with your using PREEMPT_RT.”
Hardware
In terms of hardware, I am using two I2C devices to manage all of the physical aspects of the device. I am using a MPU-92/65 combination gyroscope, accelerometer and magnetometer that gathers all of the data necessary for keeping the craft in level flight. In addition to this, I installed a PCA9685 PWM controller in order to control the PWM signal going to the motor speed controllers. I put the battery eliminator circuit on one of the ESCs to work powering up the Raspberry Pi Zero and gyroscope. Below is the bill of materials for all of the parts that I used to build the flight controller. Essentially any frame, ESC and motor combo can be used, although some tuning will be necessary to make the flight stable depending on throttle response, weight, motor power, and countless other factors.
Bill of Materials:
- HiLetgo 2pcs PCA9685 16 Channel 12-Bit PWM Servo Motor Driver
- HiLetgo MPU6500 MPU-6500 6-Axis Gyroscope Accelerometer Sensor Module
- Might be a slightly different model, but it should work basically the same
- Raspberry Pi Zero W
- 5x7 cm Perfboard
Of course all of the generic PCB parts came from my favorite manufacturer of dirt cheap bargain bin Chinese electronics HiLetgo! In all seriousness, their products are decent for the price. I don’t have too many issues. Software
This is not a tutorial to teach you how to build one of these, it is more so a log that someone with requisite prior knowledge could use to recreate and understand my work. Consistent with that idea, this is a brief overview of what I did to get this to work. The quad-copter runs a custom distribution of Buildroot based strain of Linux that has the packages, drivers and other code needed to control the aircraft built in to it.
The real time environment is initialized according to the specification outlined on the Linux Foundation’s wiki. The hardware is controlled by the I2C Linux subsystem, the implementation of it’s provided methods are pretty standard in this project. A good resource for learning this if you are doing it for the first time is the Linux Kernel documentation. Data is read and written using the i2c_smbus_read_byte_data and i2c_smbus_write_byte_data commands provided by the smbus.c helper files written by Simon Vogl, Frodo Looijaard and Jean Delvare. These helper methods are available here. Without this low level I2C/SMBus (Very similar protocols, technically some small differences) access is much more difficult.
In order to manage level flight, two common PID loops are used to control the roll and pitch of the craft. They are not complex things, if you have not used one before, I originally learned how to program them from this tutorial/example code by Electronoobs. The PID loop requires a heading of the aircraft, this heading is derived by fusing data from the magnetometer, accelerometer and gyroscope information using the Madgwick inertial measurement unit equations. I will not even pretend to understand what exactly makes this algorithm work, I just implemented it according to online descriptions of the model. I have since forgotten what website I used for this, so you are on your own for this part. There are other sensor fusing algorithms that are used for this purpose, more information about this is on this Wikipedia page.
In order to control the device I have opened a TCP port on it which receives and parses commands that are sent over WiFi to it using a Python script I wrote.It works exactly how you might imagine it does.
Assuming you have a decent grasp of robotics and programming concepts, this should set you on a decent path toward setting up a Linux quad-copter of your own. My code is available on my GitHub. However, there are various shortcomings in my implementation. If you would like to have a high quality and safe device, I would recommend an at least partial rewrite. Shortcomings
My implementation of this was a quick and dirty one I created in a few days simply to experiment with real time code running under Linux, in that respect it was a great success. There are several things I would like to clean up in the future, however.
Missing features:
- The ability to do anything other than hover.
- Sensors for altitude and location control.
- An RF based controller that does not rely on WiFi and a Python script.
In addition to the above missing features, there are some flaws in my design, such as the brush less motors that, being large spinning magnets, tend to interfere with the magnetometer on the aircraft. Similarly, the PWM controller has undefined behavior before it resets, meaning that as Linux boots the 4 powerful brush less motors are all sitting in a state of chaos. Currently, these problems are solved by hoping the impact on heading data isn’t too large and installing the propellers after boot up (which is also dangerous itself) respectively, but they ought to be solved in a much safer and more permanent way in a real solution.
I have not yet deduced a real way to solve the magnetometer issue, but the motor controller issue can be solved by placing a pull down resistor on the PWM lines of the speed controllers and allowing a set of transistors to connect these lines to those of the controller. This way they stay disconnected and in a zero state until the SOC boots and the program is started. It also provides a quick and easy way to disable the motors in cases of emergency.
Final notes
Right now this quad-copter accomplishes the goal I set out for it at the beginning, but I want to make it better. Ideally, I would like to try to make something like the drone that can fly with three rotors after all, it is easy to make an air plane that flies, it is hard to make one that continues to fly with no engines whilst on fire. Fault tolerance has always been very interesting to me, it would be a real accomplishment to get this to work. Aside from that adding the “missing features” outlined above would add to this project.
Thanks for reading!