Hackflight logo by MC Greenleaf
Hackflight is simple, platform-independent, header-only C++ firmware for multirotor flight controllers and simulators. It is geared toward people like me who want to tinker with flight-control firmware, and use it to teach students about ideas like inertial measurement and PID tuning. If you are in the 99% percent of users who just want to get your vehicle flying without getting into firmware hacking, I recommend Cleanflight (great for getting started when you're on a budget) or the Ardupilot system (for sophisticated mission planning with waypoint navigation and the like). In addition to big user communities and loads of great features, these platforms have safety mechanisms that Hackflight lacks, which will help avoid injury to you and damage to your vehicle.
Hackflight is current working on the following platforms:
-
Ladybug brushed flight controller from Tlera Corp.
-
SuperFly Hackable ESP8266 Flight Controller from Pesky Products
-
Certain STM32F-based flight controllers
-
HackflightSim flight simulator based on UnrealEngine4
By supporting floating-point operations, these platforms allow us to write simpler code based on standard units:
- Distances in meters
- Time in seconds
- Quaternions in the interval [-1,+1]
- Euler angles in radians
- Accelerometer values in Gs
- Barometric pressure in Pascals
- Stick demands in the interval [-1,+1]
- Motor demands in [0,1]
Thanks to some help from Sytelus, the core Hackflight firmware adheres to standard practices for C++, notably, short, simple methods and minimal use of compiler macros like #ifdef that can make it difficult to follow what the code is doing.
Because a multirotor build typically involves choosing a flight-control board, radio receiver, model (airframe), and PID control settings, Hackflight provides a separate C++ class to support each of these components:
- The Board class specifies a set of four abstract (pure virtual) methods that you must implement for a particular flight controller or simulator: getting the current quaternion from the IMU; getting gyrometer rates from the IMU; sending commands to the motors; and getting the current time.
- The Receiver class performs basic functions associated with R/C control (tracking stick positions, checking switches) and specifies a set of abstract methods that you implement for a particular receiver (reading channels values).
- The Mixer class is an abstract class that can be subclassed for various motor configurations (QuadX, Hexacopter, Tricopter, etc.). The QuadX subclass is already implemented.
- The PID_Controller class provides a constructor where you specify the PID values appropriate for your model (see PID Controllers discussion below).
Because it is useful to get some visual feedback on things like vehicle orientation and RC receiver channel values, we also provide a very simple “Ground Control Station” (GCS) program. that allows you to connect to the board and see what's going on. Windows users can run this program directly: just download this zipfile, unzip the file, open the folder, and double-click on hackflight.exe. Others can run the hackflight.py Python script in the extras/gcs/python folder. To run the Python script you'll need to install MSPPG, a parser generator for the Multiwii Serial Protocol (MSP) messages used by the firmware. Follow the directions in that folder to install MSPPG for Python.
To support working with new new sensors and PID control algorithms, the Hackflight C++ class provides two methods: addSensor and addPidController. For an example of how to use these methods, take a look at this sketch, which uses the VL53L1X long-range proximity sensor to provide altitude hold.
To get started with Hackflight, take a look at the build wiki. To understand the principles behind the software, contniue reading.
Design Principles
There are two basic data types in Hackflight: state and demands. For anyone who's studied Kalman filtering, the state will be familiar: it is the set of values that define the state of the vehicle at a given time (altitude, orientation, angular velocity, ...), which gets modified by a set of sensors (gyrometer, accelerometer, barometer, rangefinder, ...). Once the state has been determined, it is used by a set of PID controllers to modify the demands (throttle, roll, pitch, yaw) received by the R/C receiver or other control device. Then the demands are then sent to the mixer, which determines the values to be sent to each motor:
Sensors
As discussed above, Hackflight requires a bare minimum of two sensor readings: quaternion and gyrometer. Technically, the quaternion is more properly part of the vehicle state, but because of the availability of “hardware quaternion” data from modern sensors like the EM7180 SENtral Sensor Fusion Solution, we find it convenient to treat the quaternion as a sensor reading. For inertial measurement units (IMUs) like the MPU9250 that do not deliver a hardware quaternion, Hackflight provides a QuaternionFilter class that can be used to compute the quaternion on your microcontroller.
To provide access to other popular surface-mount sensors that you may wish to read, Hackflight also has classes to support accelerometers, magnetometers, and barometers. Together with the quaternion and gyrometer, these are all sub-classes of the SurfaceMountSensor class, which is in turn a sub-class of the Sensor class. Each surface-mount sensor accesses the appropriate virtual method of the Board class (getQuaternion(), getGyrometer(), ...). The Sensor class is an abstract (virtual) class (a.k.a. interface) specifying two methods that any sensor must implement: (1) reporting whether the sensor is ready to deliver new data; (2) modifying the vehicle state. By requiring each sensor to report its readiness, we can avoid the need to write a separate timing loop for each sensor in the main loop code.
If you're mathematically-minded, you can think of a sensor as a function from states to states: Sensor: State → State
To implement additional sensors, you can directly sub-class the Sensor class, as we've done with the Rangefinder class that we use to support the VL53L1 time-of-flight rangefinder in an example sketch. Once you've implemented the sub-class(es) for a new sensor, you can call Hackflight::addSensor() to ensure that the sensor code will be called by the checkOptionalSensors method.
PID Controllers
Like sensors, PID controllers in Hackflight are subclasses of an abstract PID Controller class, whose modifyDemands() method takes the current state and demands, and modifies the demands based on the state. (This class also provides an optional shouldFlashLed() method, to help you see when the PID controller is active.) The Hackflight class init method requires you to provide a Rate PID controller; however (especially for beginners), it is advisable to add a Level PID controller as well, as shown in this example. For an introduction to Rate (a.k.a. Acro) and Level modes, read this blog post.
As with sensors, you can sub-class the PID_Controller class and call Hackflight::addPidController() to ensure that your PID controller is called in the Hackflight::runPidControllers() method. The addPidController() method requires you to to specify the auxiliary-switch state in which the specified PID controller will be active. For example, you can specify that a Level PID controller will be active in switch state 1 (or higher) and an AltitudeHold PID controller will be active in switch state 2, as we've done in the previously-cited example sketch.
If you're mathematically-minded, you can think of a PID Controller as a function from a (State, Demands) pair to Demands:
PID Controller: State × Demands → Demands
Control Flow Diagrams
Courtesy of Juan Gallostra
Rate PID
Level PID
Altitude-hold PID
This PID controller was adapted from the algorithm used in the iNAV flight-control system.