Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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 (which has yet to be designed), 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

Image Removed

The duty of this module is to determine where the aircraft needs to go and how to get it there. Using the GPS, the altimeter, and information from the ground, this module decides how the aircraft should be oriented and at what speed it needs to be at to get where it needs to go. Those instructions are communicated to the attitude manager.

This state machine will be implemented in a thread of its own.

For the most part, the way-point management algorithm should be fine to port from PicPilot. The same should be true with the PID algorithm (used to determine ideal attitude/airspeed to ask the lower level to achieve).

The reason we ask the attitude manager for data is that GPS is actually fairly inaccurate (only accurate within a few meters, at best). So we can combine what we know about how the aircraft is oriented and how fast it's going with the GPS measurements to get more accurate results.

The above diagram shows the state flow of the Path manager. The following diagram shows which modules get which data from which other modules. (Note that no 2 sub modules directly talk to each other. Rather it's the state machine that does the work of communicating the data from one to the other).

Image Removed

Some notes:

  • Autonomous takeoffs/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 information coming from the computer vision will be used. Does it belong in this state machine ? Probably will need another state machine that takes over control from this one once we're close enough that we can start finding targets via computer vision.

Attitude/airspeed manager

Image Removed

The duty of this module is to constantly accept instructions from the path manager module (it's told what attitude and airspeed are required) and use information it gets from the appropriate sensors to get the aircraft to arrive at the desired attitude and airspeed as fast as possible.

This state machine is meant to be implemented inside a thread of its own.

The sensors relevant to the attitude manager are the IMU and the airspeed sensor.

The Sensor Fusion, the PID and the output mixing algorithms can, for the most part, be ported from PicPilot.

Data is also transferred up to the higher level Path manager, because it can make use of the IMU data in it's own Sensor fusion algorithms.

Some notes:

  • Any time we don’t have a new sensor measurement (if we even run that fast), maybe we should extrapolate what’s going on based on previous measurements.
  • Stall protection / verifying whether the higher level is asking for garbage belongs in this module.
  • 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 GPS interface. Interfaces to all sensors should look very similar.

Code Block
languagecpp
typedef struct
{
    long double latitude;  // 8 Bytes
    long double longitude; // 8 Bytes
    float utcTime;     // 4 Bytes. Time in seconds since 00:00 (midnight)
    float groundSpeed; // in m/s
    int altitude; // in m
    int16_t heading; // in degrees. Should be between 0-360 at all times, but using integer just in case
    uint8_t numSatellites;    // 1 Byte

    uint8_t sensorStatus; // 0 = no fix, 1 = gps fix, 2 = differential gps fix (DGPS) (other codes are possible)
    bool dataIsNew; // true if data has been refreshed since the previous time GetResult was called, false otherwise.

} GpsData_t;

class Gps
{
	public:

		/**
		* Initialises internal parameters.
		* Does nothing. derived classes take care of whatever they need themselves.
		*/
		Gps(){};

		/**
		* 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.
		*/
        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)
alltrue
depth100