UAV Systems Engineering · Module 8
M8-T9: Fault detection, isolation and recovery
Recovery is a table, not a hope: for every fault in the FMEA there is a row saying what the vehicle does, and a test that shows it.
Detection and isolation (lesson M3-T16) say what failed. FDIR closes the loop with a policy: the action to take for each fault, in each mode. The most valuable property of such a policy is that you can check it for completeness mechanically.
Learning objectives
After this lesson, you should be able to:
- Describe the FDIR chain and the role of the policy table.
- Choose recovery actions (continue, degrade, hold, return, land, terminate) for typical UAV faults.
- Check a policy for completeness, determinism and a safe default.
- Verify each row by fault injection with a recorded result.
Conceptual explanation
FDIR is the system function that detects faults, isolates them, and recovers: it keeps the vehicle safe when something fails. Recovery is not improvisation; it is a table indexed by fault (and often by current mode) whose entries are actions. Common actions, in increasing severity: continue with a flag; switch to a redundant element; degrade capability (lesson M8-T10); hold position; return along a known path; land here; stop and disarm in an emergency.

Engineering intuition
Three properties make a policy trustworthy. Complete: every fault in the FMEA has a row, and anything unlisted falls to a safe default. Deterministic: one situation, one action, with explicit priority when faults overlap (a link loss during GNSS loss is its own row, not the result of two policies fighting). Monotonic in severity: a more serious fault never results in a milder action than a less serious one in the same context. All three can be checked by a program, which is why the policy is data.
Mathematical formulation
Model faults as a set , modes as , actions as a totally ordered set A (severity 0 … ). The policy is a function : . Properties to check:
Timing matters as much as the action: the time from fault onset to a completed recovery is the sum of the detection delay, the decision latency and the execution time, and must be shorter than the time the hazard takes to develop (for example the time for drift to reach the clearance, lesson M8-T11):
Architecture and data flow
Health records from monitors (lesson M3-T16) → fault set → policy lookup (with the current mode) → recovery command to the navigation manager and mission layer, and, for hard actions (land, disarm), to PX4 through vehicle commands or the configured failsafe parameters (lesson M8-T14). Every decision logs: time, fault set, mode, action, reason. These logs are also the evidence in the verification report.
Real-world UAV applications
- Specifying the failsafe behaviour for Capstone 4.
- Mapping the FMEA to executable software and to PX4 failsafe configuration.
- Writing fault-injection test plans straight from the policy table.
- Reviewing a vehicle’s behaviour for overlapping faults.
Implementation example
This runs as shown: a policy as a table, with a mechanical check of completeness against the FMEA fault list, monotonicity in severity, and the combination rule.
ACTIONS = ["CONTINUE", "SWITCH_SOURCE", "DEGRADE", "HOLD", "RETURN", "LAND", "EMERGENCY_STOP"] # ordered by severity
sev = {a: i for i, a in enumerate(ACTIONS)}
FAULT_SEVERITY = {"imu_secondary_fail": 1, "gnss_loss": 2, "gnss_spoof": 3, "camera_fail": 2, "lidar_fail": 2,
"all_odometry_lost": 5, "link_loss": 4, "imu_all_fail": 6, "companion_crash": 5}
POLICY = { # (fault, mode) -> action ; a fault missing here falls to DEFAULT[mode]
("imu_secondary_fail", "NOMINAL"): "SWITCH_SOURCE", ("imu_secondary_fail", "DEGRADED"): "SWITCH_SOURCE",
("gnss_loss", "NOMINAL"): "DEGRADE", ("gnss_loss", "DEGRADED"): "CONTINUE",
("gnss_spoof", "NOMINAL"): "DEGRADE", ("gnss_spoof", "DEGRADED"): "DEGRADE",
("camera_fail", "NOMINAL"): "SWITCH_SOURCE", ("camera_fail", "DEGRADED"): "HOLD",
("lidar_fail", "NOMINAL"): "SWITCH_SOURCE", ("lidar_fail", "DEGRADED"): "HOLD",
("all_odometry_lost", "NOMINAL"): "HOLD", ("all_odometry_lost", "DEGRADED"): "LAND",
("link_loss", "NOMINAL"): "RETURN", ("link_loss", "DEGRADED"): "LAND",
("companion_crash", "NOMINAL"): "HOLD", ("companion_crash", "DEGRADED"): "LAND",
("imu_all_fail", "NOMINAL"): "EMERGENCY_STOP", ("imu_all_fail", "DEGRADED"): "EMERGENCY_STOP",
}
DEFAULT = {"NOMINAL": "HOLD", "DEGRADED": "LAND"} # safe default for anything not listed
MODES = ["NOMINAL", "DEGRADED"]
def decide(faults, mode):
acts = [POLICY.get((f, mode), DEFAULT[mode]) for f in faults]
return max(acts, key=sev.get) if acts else "CONTINUE" # simultaneous faults: the most severe action wins
# 1) completeness: every FMEA fault has an explicit row in every mode
missing = [(f, m) for f in FAULT_SEVERITY for m in MODES if (f, m) not in POLICY]
assert not missing, missing
# 2) monotonic: a more severe fault is never answered more mildly than a less severe one, in the same mode
for m in MODES:
for f1 in FAULT_SEVERITY:
for f2 in FAULT_SEVERITY:
if FAULT_SEVERITY[f1] >= FAULT_SEVERITY[f2] + 2: # compare faults two severity levels apart
assert sev[POLICY[(f1, m)]] >= sev[POLICY[(f2, m)]], (m, f1, f2)
# 3) overlapping faults resolve to the more severe action, and unknown faults hit the safe default
assert decide(["gnss_loss", "link_loss"], "NOMINAL") == "RETURN"
assert decide(["camera_fail", "lidar_fail"], "DEGRADED") == "HOLD"
assert decide(["something_new"], "DEGRADED") == "LAND" and decide([], "NOMINAL") == "CONTINUE"
print("policy: %d rows, complete, monotonic, with a safe default" % len(POLICY))Common engineering mistakes
Recovery written as scattered if-statements
Unreviewable and untestable; keep the policy as a table.
No safe default
An unlisted fault does nothing; define a conservative default per mode.
Overlapping faults treated independently
Two policies issue contradictory commands; define the combination rule.
A recovery action that depends on the failed element
Returning “home” using the position source that just failed.
Never exercising the rare actions
Land and emergency-stop paths must be run in simulation and ground tests, not discovered in flight.
Failure modes
| Failure mode | How it shows up | Likely cause |
|---|---|---|
| Contradictory commands | Vehicle oscillates between hold and return | Independent handlers for simultaneous faults |
| Recovery too slow | Hazard materialises before the action completes | Timing budget not derived |
| Wrong action for the mode | Return home while GNSS-denied after long drift | Policy not indexed by mode |
| Unhandled fault | Nothing happens | Missing row and no safe default |
Debugging approach
- Run the completeness and monotonicity checks in CI on the policy file.
- Inject each fault in each mode in simulation and compare the commanded action with the table.
- Check that every decision logs fault, mode, action and reason.
- Measure for the slowest rows.
Practical exercises
- Add the fault “barometer fail” with a policy row in both modes and re-run the checks.
- Introduce a non-monotonic row on purpose and see the check fail.
- Add a third mode (DEAD_RECKONING) and fill in its rows.
- Map each action to the PX4 parameter or command that executes it in your version.
Interview questions
Write an answer in your own words, then reveal the model answer. Nothing is sent anywhere.
Question 1
What makes a recovery policy trustworthy?
Model answer
It is complete (every analysed fault has a row and an unlisted fault has a safe default), deterministic with explicit rules for simultaneous faults, monotonic in severity, and every row is tested by fault injection.
Question 2
Why keep the policy as data?
Model answer
It can be reviewed, version-controlled and checked mechanically for completeness and consistency, and test cases can be generated from it.
Question 3
What is the timing requirement on recovery?
Model answer
The sum of detection delay, decision latency and execution time must be shorter than the time the hazard takes to develop, for example the time for drift to reach the clearance.
References and further reading
Primary papers, official documentation and standards. Each claim above that is specific to a tool or version points here.