This page details the high-level design behind the autopilot code.
Highest level:
The autopilot will consist of 3 threads that each manage a state machine and many other threads that manage sensor data acquisition. The 3 state machines are the attitude manager; responsible for putting and keeping the aircraft in some desired attitude, the path manager; responsible for instructing the attitude manager to achieve some attitude in order to navigate the aircraft to some particular location, and the telemetry manager, responsible for all communications with the ground station. At the same time, there will be a thread for each sensor, ensuring data is collected at well timed intervals.
Path Manager
- The sensors relevant to the path manager are the GPS, altitude sensors, and anything responsible for communicating to/from the ground.
- As with the lower level, this architecture is also interrupt based.
- Not sure how we should manage loop period. Should this be run more or less often then the lower level loop ? And how to keep loop periods constant when we have 2 separate loops running.
- Gps stuff comes around fairly slowly and can be fairly inaccurate. As mentioned in Asana, seems like a good idea to fuse measurements made by the lower level with it to figure out what is going on. I don’t think this was implemented in PicPilot.
- The PicPilot does a ton of really cool waypoint tracking stuff. I think most of it is fine to port over.
- A couple things not included in these documents: Battery level checkers, led managers... Not sure where these things should go.
- Autonomous takeoff/landing is still a whiles away, but not sure how it would fit into such an architecture. Probably want another state machine that takes control from this one when we deal with take offs/landings.
- Not sure how often data needs to be sent to/received from the ground. We should make sure that uncertainty is accounted for when figuring out how often to run this loop. Surely FreeRtos gives us methods for doing things like this.
- Actually, I’m having second thoughts about having ground communications be part of this module. What does path managing have to do with ground communication anyway ? Maybe a third module should exist, feeding this one with relevant ground data and figuring out what to give the ground.
Attitude/airspeed manager
- The sensors relevant to the attitude manager are the IMU and the airspeed sensor.
- This architecture is purely interrupt based and we need spend no time polling.
- The “trigger new measurements” state exists to begin the I2C/uart/spi/whatever exchanges with the appropriate sensors. These exchanges should all be interrupt based so we are free to process data in the mean time (this is different then PicPilot, where IMU data is polled for).
- Some sensors are faster than others. The “trigger new measurements” state is simplified. Ideally, we have various tasks assigned to FreeRtos that all fire at set periods to trigger the given sensor’s measurements.
- The control period should be the slower of: the fastest sensor’s refresh rate or the time it takes to perform all the computations ( not quite sure how to keep things at a regular period, considering there is a higher level state machine also running??? Freertos surely has ways for this to work).
- Any time we don’t have a new measurement (if we even run that fast), maybe we should extrapolate what’s going on based on previous measurements.
- Looks like most of the data processing algorithms used by the PicPilot can be ported. Pid gains have even already been tuned for Spike it seems.
- Stall protection / verifying whether the higher level is asking for garbage belongs in this module.
- The whole “gather new instructions” and “Communicate results” thing might not need to actually be operations done. We may want to have key things shared between higher and lower levels. This was done across multiple chips via DMA on the PicPilot. Things would be much simpler here since we’ve only got one big autopilot chip.
- Autonomous take offs/landings should be opaque to this module. If a higher level module tells it what speed and attitude the plane needs to be at, this module just obliges, having no idea whether we’re at 10 000 feet or about to land/on the ground.
Sensor interfaces
As an example, here is the GPSinterface. Interfaces to all sensors should look very similar.
Code Block | ||
---|---|---|
| ||
class Gps
{
public:
/**
* Initialises internal parameters.
* Should be called exactly once before anything is attempted to be done with the module.
*/
virtual void Init(void) = 0;
/**
* Begins the process of collecting the sensor's data.
* This is a non blocking function that returns right away.
*/
virtual void BeginMeasuring(void) = 0;
/**
* Gets the information about the aircraft's position (See GpsData_t struct).
* This is a non blocking function that returns right away, either with new data,
* or with old data (in case of old data, the dataIsNew flag of the result struct will be cleared).
* @param[out] Data pointer to the result struct.
* @return AAQUAD_SUCCEEDED, AAQUAD_BUSY or AAQUAD_FAILED
*/
virtual void GetResult(GpsData_t *Data) = 0;
}; |
The design is such that a "GPS" thread would periodically call BeginMeasuring, which would begin the process of collecting the data from the sensor. That data should be available by the time the appropriate state machine thread gets around to calling GetResult to collect it.
The Data struct that gets populated by the GetResult function will contain, along with the fields that store the sensor specific data, an "isDataNew" field that indicates whether the sensor data has indeed been refreshed since the last time the caller called GetResult, and a "sensorStatus" field that indicates any sensor specific failures.
None of the methods in the interface should be blocking. This means all communications with the sensor will be internally interrupt (and possibly DMA) based. As a result, we can guarantee the processor spends it's time doing meaningful work rather than polling sensors.
The interface is made up entirely of pure virtual methods. This allows many implementations of the module (for many different parts). This way, we would be able to easily select which part is available to us at the time of flight without having to modify any of he calling code.
Note: We might find it useful to add a 4th function to the interface: “Sensor_Calibrate” which might either load calibration values we stored earlier or execute a calibration or something.
RTOS
The main autopilot systems are built on FreeRTOS, an open-source Real-Time Operating System. FreeRTOS gives us the ability to run multiple concurrent tasks on a single embedded system, each of which has a priority and can "block" for a period of time while they wait for data. At any given time, the highest priority task that is not blocking will be running.
A few notes about developing for real-time systems:
- Treat timeouts as a design error. Most blocking OS functions on the autopilot allow (or require) specifying a timeout for the blocking operation.
Autopilot Drivers
Required:
- GPS
- XBee
- Altimeter
- IMU
- Interchip
- EEPROM
Optional:
- Airspeed
- Battery
- Ultrasonic
Safety Drivers
Required:
- PWM
- PPM
- Interchip
Autopilot Logic
Required:
- Task management
- PID
- Waypoint management
- Navigation
- Telemetry
- Autonomous level
- EEPROM storage
- Startup codes
Optional:
- Sensor fusion
- Landing/take off
- Multi-vehicle
Safety Logic
Required:
- Anonymous level
- Safety switch
- PPM disconnect
- Startup codes
- SPI heartbeat
Overview
From the highest level, one can think of the entirety of the ZeroPilot software engine as being a black box which takes in instructions from a ground station (about the flight plan, amongst other data) and outputs a series of actuator commands in order to fly the aircraft. ZeroPilot runs across 2 separate microcontrollers that communicate with each other over SPI. The first chip, “Autopilot”, is responsible for all aspects of autonomous flight except attitude management and actuator control. The second chip, “Safety”, is responsible for attitude management (generating PWM values for the actuators) and sending instructions to the actuators. Safety is also used for reading in a redundant telemetry link which may be used in the event of catastrophic failure.
As a whole, ZeroPilot is designed to take care of everything from takeoff to level flight to landing and apart from collecting instructions from the ground station about where we want to go, it does all this without the need for any human intervention.
Software design
...
Safety
The safety firmware is made to contain the bare minimum required to perform manual flight. As seen in the flowchart, the safety component of ZeroPilot consists of a few blocks. These are responsible for collecting data from Autopilot as well as from the secondary telemetry link. Depending on whether an emergency has occurred or not, the Decision Module will either send the Autopilot or telemetry information into the Attitude Manager. The Attitude Manager then converts the inputted data into PWM values that are sent to the actuators. The functionality of each Safety module is detailed in a child page of this one.
Autopilot
Firmware that runs on the AutoPilot chip does so atop FreeRTOS.
The entirety of the autopilot consists of two managers (telemetry and path), a sensor fusion engine, a series of peripheral drivers, and an inter-chip driver. All these components are run at strict rates. The managers each live inside their own threads, as do the sensor fusion module and inter-chip. The drivers are grouped together based on how often they refresh. For instance, all sensor drivers that refresh at 200Hz live inside the same thread. Most drivers, however, will be used across 2 threads, one for beginning a transaction, and one for using the data (this is detailed in the “Sensors” child page).
All threads, with no exceptions apart from a nop “idle task”, are run with the same priority. This design ensures 2 very important things. First, it ensures that every thread is allowed to complete a complete cycle without the OS ever interrupting it half way. That implies that modules do not ever need to worry about concurrent data access, save from any hardware interrupts. Second, it ensures that each thread is guaranteed to execute exactly once within its specified interval. That implies that thread starvation is never an issue, makes the use of FIFOs completely unnecessary, and allows synchronous modules, such as PID and SensorFusion to be time independent.
The functionality of each AutoPilot module is detailed in this page’s child pages, but from a high level overview, the best way to describe AutoPilot’s operation is with an analogy. Imagine a Warship’s control room. You may find a captain, a navigator, and a radio operator. The radio operator is responsible for relaying all data between the ship and a control station on land somewhere. That includes anything from sending updates of the ship’s position to receiving any instructions that would update the ship’s navigation plan. Note that the radio operator never acts on any of this data. His sole responsibility is to relay it. On ZeroPilot, Telemetry Manager is our radio operator, exchanging data with a groundstation. Next, the navigator is in charge of taking instructions as to where the ship needs to go and figuring out what changes to the ship’s heading, speed, etc need to be made to get there. On ZeroPilot, Path manager is our navigator. Lastly, the captain is in charge of collecting the required changes in heading, speed, etc and actuating his ship to achieve those things. On ZeroPilot, attitude manager is our captain. This is a good place to note that a captain intimately knows his ship. If he is to move to another kind of ship, it will take him some time to adjust to it, while navigators and radio operators are generic; the kind of ship they are on makes no difference to their work. The same is true with ZeroPilot; controlling a different aircraft requires no modification of the telemetry or Path managers, but will require tuning of the Attitude manager. Lastly, both the captain and the navigator rely on various instruments to be able to do their job properly. Instruments like compasses, speed indicators, motor strain gauges etc. On ZeroPilot, all “instruments” are contained inside Sensor fusion. Sensor fusion processes all raw sensor data and is public to all modules, so anyone can ask it for any data at any time.
Each of these components along with their sub components has an associated architecture page, listed as children of this page. Here is the hierarchy.
Child pages (Children Display) | ||||
---|---|---|---|---|
|