feat(falcon): single-rotor-out FDI noise-robustness (v1.115, FAULT-P03) - #259
Conversation
…low-ups Lands the release plan for the follow-ups surfaced by the v1.114 rotor-out work (the parasitic-moment flip the idealised Monte-Carlo campaign could not falsify — dispersion is breadth, not fidelity): - SWREQ-FALCON-FAULT-P03 (proposed) — single-rotor-out FDI robustness under heavy GNSS/gyro noise; detection latency blew up in the v1.114 dispersed full-loop campaign at the heavier envelope. Issue #255. - SWREQ-FALCON-FAULT-P04 (proposed) — supervisor-driven controlled landing after a rotor loss (close the altitude scope the campaign reports-not-gates) + a clean gz confirmation run and the recordable recovery video. Issue #256. - FEAT-FALCON-v1.115 (proposed) — the release grouping; also tracks the verification-fidelity audit of other safety campaigns (issue #257). Both reqs derive-from SYSREQ-FALCON-005 (control allocation). rivet PASS (proposed reqs carry the expected "needs verification" backlog warning until implemented). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…query
Add `release: falcon-v1.115.0` to FAULT-P03/P04 + FEAT-v1.115 so the release
scope is a QUERY, not an opinion:
$ rivet release status falcon-v1.115.0
Release falcon-v1.115.0 — 2 artifact(s)
proposed 2
✗ NOT cuttable — 2 artifact(s) not yet verified.
`rivet release status` exits non-zero when not cuttable (CI-gateable); it
burns down to cuttable as P03/P04 reach `verified`. Pairs with
`rivet list --release falcon-v1.115.0`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… residual + roll/pitch gate (v1.115, FAULT-P03)
The v1.114 dispersed full-loop campaign surfaced that the rotor-out FDI, under
heavier sensor noise, FALSE-ISOLATED a healthy rotor (and sometimes missed the
dead one). Root cause, found by instrumenting a failing trace (not guessed):
the effectiveness residual compared the CURRENT-tick command against the
PREVIOUS-tick achieved ESC RPM — a one-tick telemetry lag — so an abrupt
collective step spiked ALL FOUR residuals at once and the CUSUM tripped on
whichever rotor was checked first. Baseline at gps σ ≤ 0.3 / gyro ≤ 0.02:
43/300 false isolations, 26 misses.
Fix (FlightCore::step):
1. Command-ALIGNED residual — compare achieved against the PREVIOUS command
that produced it (last_motor_cmd). Healthy rotors read ~0 even through
abrupt commands; only a genuinely dead rotor accumulates. The safety fix:
false isolations 43 → 0.
2. Detection gate on ROLL/PITCH rate only, not yaw — a rotor-out RELINQUISHES
yaw and spins freely (Mueller & D'Andrea); gating on total rate slammed
the gate shut once the spin built, starving re-detection.
+ FlightCore::fdi_diag() observability (gate inputs + residual).
Oracle: falcon-sitl-gz fdi_noise_robustness_monte_carlo_campaign — 200 dispersed
trials at the heavier envelope (gps σ ≤ 0.18, gyro ≤ 0.010): 0 false isolations,
0 misses, worst detect latency 17 steps (< 25 = 100 ms). The full-loop recovery
(FAULT-P02) and the clean-signal FDI contract stay green.
Artifacts: SWREQ-FALCON-FAULT-P03 → verified (FV-FALCON-FAULT-004); FAULT-P04
(supervisor landing + gz video, display-dependent) deferred to v1.116; FEAT-
FALCON-v1.115 = FDI noise-robustness. `rivet release status falcon-v1.115.0` ✓
cuttable.
Verified: falcon-core 44 tests + gated clippy -D warnings clean; falcon-sitl-gz
31 tests; rivet PASS.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Automated review for PR #259pulseengine/relay: Verdict: 💬 Comment Summary: The pull request contains significant changes to the Findings: 0 mechanical (rivet) · 1 from local AI model. Findings (1):
Generated by a local AI model and post-validated against a strict JSON contract. Each finding includes the verbatim line being criticised — verify by reading the file at the cited location. Reviewed at |
Verify-Filter: (has-tag "rotor-loss")
Runs the v1.115 feature loop for FAULT-P03 — and folds in the v1.115 plan artifacts (supersedes #258, whose Verification gate was canceled, not failed — a concurrency-cancel on the 136-artifact default run; this PR narrows the gate filter to the rotor-loss artifacts).
The bug (found by instrumenting a failing trace, not guessing)
The v1.114 dispersed full-loop campaign surfaced that the rotor-out FDI, under heavier sensor noise, false-isolated a healthy rotor. Root cause: the effectiveness residual compared the current-tick command against the previous-tick achieved ESC RPM (a one-tick telemetry lag), so an abrupt collective step spiked all four residuals at once → the CUSUM tripped on whichever rotor was checked first. Baseline at gps σ ≤ 0.3 / gyro ≤ 0.02: 43/300 false isolations, 26 misses.
Fix (
FlightCore::step)last_motor_cmd). Healthy rotors read ~0 even through abrupt commands; only a genuinely dead rotor accumulates. False isolations 43 → 0.FlightCore::fdi_diag()observability (gate inputs + residual).Oracle
fdi_noise_robustness_monte_carlo_campaign— 200 dispersed trials at gps σ ≤ 0.18 / gyro ≤ 0.010: 0 false isolations, 0 misses, worst detect latency 17 steps (< 25 = 100 ms). The full-loop recovery (FAULT-P02) and the clean-signal FDI contract stay green.Traceability
SWREQ-FALCON-FAULT-P03→ verified (FV-FALCON-FAULT-004).SWREQ-FALCON-FAULT-P04(supervisor landing + gz video — display-dependent) deferred to v1.116 (logged scope move).rivet release status falcon-v1.115.0→ ✓ cuttable.Envelope note (honest scope)
At extreme gps σ ≥ 0.3 m the estimator/altitude loop itself destabilizes and the vehicle doesn't fly — a dead rotor is then moot (no residual). That's an estimator envelope limit, not an FDI defect; the aligned residual correctly stays 0 there (no false trip). Tracked separately.
Falsification
The FDI is wrong if, within the flying envelope (gps σ ≤ 0.18 m), it isolates a healthy rotor, or fails to isolate the dead one within ~100 ms.
Verified locally
falcon-core 44 tests + gated clippy
-D warningsclean; falcon-sitl-gz 31 tests; rivet PASS; clean-room 9/9 confirmed.🤖 Generated with Claude Code