gwordal

Lesson 5 of 5 · 22 min

Flight controllers and safety

You now know the pieces: mixer, motors, estimator, PID. A flight controller (FC) is the board that runs all of them together, hundreds or thousands of times per second, plus everything around them: radio input, sensors, logging, safety. This lesson covers what is on that board, which firmware to pick, and the habits (arming checks, failsafes, careful testing) that separate a hobby from an accident.

What is on a flight controller

A typical FC has a 32-bit microcontroller (an STM32 F4, F7 or H7 running at 168 to 480 MHz), an IMU, usually a barometer, and ports: several UARTs for the receiver, GPS and telemetry, plus outputs for the four ESCs. The firmware reads the IMU at 1 to 8 kHz, runs the attitude estimator and the PID loops from lessons 3 and 4, mixes the result as in lesson 1, and sends DShot frames to the motors (lesson 2).

Choosing firmware

FeatureBetaflightArduPilotPX4
Focusracing and freestyle quadsmany vehicle types: copters, planes, rovers, boatsmulticopters, VTOL, research
Strengthvery fast loops (8 kHz gyro), crisp feelhuge feature set, missions, many sensorsclean modular code, MAVLink, ROS 2 integration
Navigationonly GPS Rescue returnfull autonomous missionsfull autonomous missions
Ground softwareBetaflight ConfiguratorMission Planner, QGroundControlQGroundControl
LicenceGPLv3GPLv3BSD 3-clause
Typical boardsmall all-in-one F4, F7 or H7Pixhawk classPixhawk class

A rule of thumb: for flying fast by hand, use Betaflight. For a camera platform, mapping or any mission that must fly itself, use ArduPilot or PX4. PX4 and ArduPilot both speak MAVLink, a message protocol your own Python code can use to read telemetry or send commands.

Sensors beyond the IMU

  • Barometer. Air pressure falls with height. The change per metre is rho * g = 1.225 kg/m^3 * 9.81 m/s^2 = 12 Pa/m near sea level. A sensor with 1 Pa resolution therefore sees about 8 cm. But weather moves pressure too: a drop of 100 Pa over a few hours looks like a climb of 100 / 12 = 8 m. So the barometer is good for short-term altitude changes and bad as an absolute reference.
  • GPS. A receiver measures its distance to each satellite from signal travel time. A signal taking 70 ms has travelled 0.070 s * 299 792 km/s = 20 985 km. Three unknowns (x, y, z) would need three satellites, but the receiver clock is cheap and wrong, and a 1 microsecond clock error means 299.8 m of range error. So the clock offset is a fourth unknown, and you need at least four satellites. Consumer GPS gives about 2 to 5 m horizontal accuracy at 5 to 10 Hz, enough to hold position, not to hover through a window.
  • Magnetometer (compass). Gives heading, so yaw stops drifting. It is easily disturbed by the current in the power wires, so mount it away on a mast.
  • Rangefinder and optical flow. A downward lidar or sonar measures height above ground, and a small camera measures sideways drift, which lets the drone hold position indoors without GPS.

The EKF from lesson 3 fuses all of these: it trusts each one by its noise level and rejects a sensor that suddenly disagrees with the rest.

Arming checks

Arming is the explicit step that allows the motors to spin. Before it, the firmware runs pre-arm checks and refuses to arm if something is wrong. Typical checks:

  • the IMU responds and the gyro has been calibrated (the drone was still)
  • the radio link is alive and the throttle stick is at minimum
  • battery voltage is above a safe level
  • GPS has enough satellites and good geometry (low HDOP, below about 2) if a GPS mode is selected
  • compass and barometer data are consistent
  • the flight mode switch is not in an unexpected position

A refusal to arm is not an annoying bug. It is the board telling you the next 30 seconds are likely to go badly.

Failsafes

A failsafe is a pre-decided response to a failure, chosen on the bench, not in the air.

  • Radio loss. No valid packet for a short time (for example 500 ms). With GPS, return to launch (RTL) at a safe altitude. Without GPS, land or cut the motors.
  • Low battery. Land by about 3.5 V per cell. Voltage sags under load by V = I * R: a pack with 20 milliohms at 30 A drops 0.6 V. A reading of 10.2 V at 30 A is really 10.2 + 0.6 = 10.8 V at rest, which is still fine. Good firmware compensates for sag and filters the reading so a single spike does not trigger a landing.
  • GPS or estimator failure. Drop to a mode that does not need GPS (attitude only), or land.
  • Geofence. Fly at most so far away or so high, otherwise return.

The order matters. With a critical battery there is no energy for a long flight home, so landing wins over RTL.

struct Status {
  bool  imuOk;
  float cellVolts;      // average volts per cell
  int   gpsSats;
  float hdop;
  int   throttleUs;     // pilot throttle, 1000 to 2000 microseconds
  bool  rcLinkOk;
};

// Returns nullptr when everything is fine, otherwise the reason for refusing
const char* preArmCheck(const Status &s) {
  if (!s.imuOk)            return "IMU not responding";
  if (!s.rcLinkOk)         return "no radio link";
  if (s.throttleUs > 1050) return "throttle not at minimum";
  if (s.cellVolts < 3.7)   return "battery too low";
  if (s.gpsSats < 8 || s.hdop > 2.0) return "GPS not ready";
  return nullptr;
}

enum Action { NONE, RETURN_HOME, LAND };

Action chooseFailsafe(bool rcLost, bool batteryCritical, bool gpsGood) {
  if (batteryCritical) return LAND;                     // no energy to fly home
  if (rcLost) return gpsGood ? RETURN_HOME : LAND;
  return NONE;
}

void setup() {
  Serial.begin(115200);
  Status s = { true, 3.95, 11, 1.1, 1000, true };
  const char *why = preArmCheck(s);
  Serial.println(why ? why : "ready to arm");
  Serial.println((int)chooseFailsafe(true, false, true));   // 1 = RETURN_HOME
}

void loop() {}

The same logic in Python, with sag compensation added:

def compensated_voltage(measured, current, r_internal=0.020):
    return measured + current * r_internal          # estimate the resting voltage

def pre_arm_check(imu_ok, rc_ok, throttle_us, cell_v, sats, hdop):
    if not imu_ok:            return "IMU not responding"
    if not rc_ok:             return "no radio link"
    if throttle_us > 1050:    return "throttle not at minimum"
    if cell_v < 3.7:          return "battery too low"
    if sats < 8 or hdop > 2:  return "GPS not ready"
    return None

def choose_failsafe(rc_lost, battery_critical, gps_good):
    if battery_critical: return "LAND"
    if rc_lost:          return "RETURN_HOME" if gps_good else "LAND"
    return "NONE"

pack = compensated_voltage(10.2, 30)                # 10.8 V for a 3S pack
print(round(pack, 2), "V, per cell", round(pack / 3, 2))      # 3.6 V per cell
print(pre_arm_check(True, True, 1000, 3.95, 11, 1.1))         # None, so ready
print(choose_failsafe(True, False, True))                     # RETURN_HOME

Testing safely and legally

Software is half of safety. The rest is how you handle the machine.

  • Test in stages. Bench with props off: check motor numbering and spin direction, radio inputs, failsafe triggers. Then a tethered or very low hover. Then short flights, away from people, in open space.
  • Use prop guards for first flights and any indoor testing. They cost a little thrust and flight time, and save your fingers, your furniture and your props.
  • Have a kill switch (disarm switch) mapped and tested before every session. Know where it is without looking.
  • Handle LiPo packs carefully: never fly a puffy, damaged or hot pack, and keep a fireproof bag at hand.
  • Check the rules. Regulations differ by country and change often, so look up your national civil aviation authority and local authorities before flying. In general terms, many places require registration above a weight threshold (commonly around 250 g), keeping the drone within visual line of sight, staying under a maximum altitude (often around 120 m), avoiding airports, crowds and restricted areas, and respecting other people's privacy. Never fly over people, and never fly where you are not sure it is allowed.

Check yourself

You are about to test the flight controller on your desk with the battery connected. What should you do first?

Check yourself

Why does a GPS receiver need signals from at least four satellites, not three?