feat(falcon): v0.19.6 — frame-correctness oracle + SDF/MIXER_X alignment#51
Conversation
The NED↔ENU frame unlock for the full verified cascade. A frame-check
oracle established the true actuator-path convention; the fix was an
SDF rotor index→position+spin alignment to relay-mix-quad's MIXER_X
convention — not a code sign-flip. Roll/pitch torque now produce
correctly-signed body rate; the verified rate-PID damping holds the
body level. Horizontal drift collapsed from −13 m (v0.19.5) to ±0.15 m.
Oracle (--scenario=frame-{roll,pitch,yaw}): commands unit torque on
one axis + hover thrust, reports sensed body-rate sign. AGREE =
+torque→+rate (needed for rate-PID negative feedback to stabilise).
Root cause: the v0.19.0–.5 SDF rotor index→position map was scrambled
vs MIXER_X (0=front-right CW, 1=back-right CCW, 2=back-left CW,
3=front-left CCW) — roll authority was ≈0. The v0.19.4/.5 negate-torque
hack was compensating for the scramble, not a real frame flip.
After aligning the SDF: roll +4.77 AGREE, pitch +4.14 AGREE, yaw
-0.0001 (weak authority, deferred). frame_correct_torque is identity;
unit test frame_correction_is_identity pins it.
Horizontal-stability proof: v0.19.5 drifted to n=−13/e=−7 → crash;
v0.19.6 holds n,e within ±0.15 m (alt-rate) / ±1.4 m (full cascade,
no runaway). The frame fix removed the runaway.
Ships:
- --scenario=frame-{roll,pitch,yaw} oracle.
- SDF rotor index→position+spin aligned to MIXER_X.
- frame_correct_torque adapter (identity) on the rate→mixer path
in both alt-rate and the full hover cascade.
- unit test frame_correction_is_identity.
- bench-evidence/gz-sim/2026-05-28-v0.19.6-frame-correctness.md +
FV-FALCON-SIM-012 + rollout entry.
Verification:
- cargo test --workspace --all-targets → 407 passing.
- cargo test -p falcon-sitl-gz --features gazebo → 9/9.
- frame-check oracle: roll + pitch AGREE under gz.
- rivet validate → PASS.
Honestly NOT claimed: full cascade hovers (doesn't lift yet —
relay-pos hover_thrust tuning for 2 kg body is v0.19.7); yaw
controlled (authority ≈0, separate tuning).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Automated review for PR #51pulseengine/relay: Verdict: 💬 Comment Summary: The code updates the Findings: 0 mechanical (rivet) · 2 from local AI model. Findings (2):
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 |
|
running 13 tests test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 13 tests test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 13 tests test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.09s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 17 tests test result: ok. 17 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s --- scenario: mission --- running 55 tests test result: ok. 55 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 10 tests test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 20 tests test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 9 tests test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 9 tests test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 9 tests test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 9 tests test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.07s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 10 tests test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 10 tests test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 10 tests test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.09s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 17 tests test result: ok. 17 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s --- scenario: attitude --- running 9 tests test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s falcon-sitl-gz: backend=mock scenario=hover duration=5s running 9 tests test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 17 tests test result: ok. 17 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s running 55 tests test result: ok. 55 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 20 tests test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 20 tests test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.22s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 10 tests test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s falcon-hitl-rfspoof: backend=stub duration=5s running 1 test test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 16 filtered out; finished in 0.04s running 10 tests test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 13 tests test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 17 tests test result: ok. 17 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s running 55 tests test result: ok. 55 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 10 tests test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 10 tests test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 10 tests test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 10 tests test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.08s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 13 tests test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 13 tests test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 13 tests test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.51s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 17 tests test result: ok. 17 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s --- scenario: step --- running 16 tests test result: ok. 16 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 16 tests test result: ok. 16 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 16 tests test result: ok. 16 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.68s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 5 tests test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s --- noise=0 (deterministic) --- falcon verification gate (type: sw-verification, filter: (has-tag "falcon"))39 artifact(s) matched: FV-FALCON-POS-001, FV-FALCON-SIM-002, FV-FALCON-NID-002, FV-FALCON-SIM-012, FV-FALCON-SIM-004, FV-FALCON-COV-005, FV-FALCON-EKF-STUB-001, FV-FALCON-ATT-001, FV-FALCON-SIM-006, FV-FALCON-SIM-003, FV-FALCON-FAULT-001, FV-FALCON-MAVLINK-002, FV-FALCON-NID-001, FV-FALCON-ARCH-001, FV-FALCON-HITL-001, FV-FALCON-UAM-001, FV-FALCON-SIM-009, FV-FALCON-PIPELINE-001, FV-FALCON-HITL-002, FV-FALCON-SIM-007, FV-FALCON-MIX-001, FV-FALCON-RATE-001, FV-FALCON-SIM-011, FV-FALCON-ARCH-002, FV-FALCON-GEO-003, FV-FALCON-EKF-001, FV-FALCON-COV-003, FV-FALCON-GEO-002, FV-FALCON-GEO-001, FV-FALCON-SIM-001, FV-FALCON-WORLD-001, FV-FALCON-SIM-008, FV-FALCON-COV-001, FV-FALCON-COV-004, FV-FALCON-MAVLINK-001, FV-FALCON-TQ-001, FV-FALCON-SIM-010, FV-FALCON-SIM-005, FV-FALCON-COV-002[ PASS] ( 7.52s) FV-FALCON-POS-001: cargo test -p relay-pos test result: ok. 17 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 17 tests test result: ok. 17 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 1 test test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 16 filtered out; finished in 0.02s running 9 tests test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s [falcon-hello-demo] building release binary... running 55 tests test result: ok. 55 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 55 tests test result: ok. 55 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s running 55 tests test result: ok. 55 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.34s running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Kani-TQ.md
|
| count | |
|---|---|
| Passed | 32 |
| Failed | -7 |
| Skipped (bench-only — needs hardware / sim) | 7 |
| Skipped (no steps) | 7 |
Failed artifacts
Bench-only artifacts (not run by CI)
FV-FALCON-ARCH-001— spar AADL architectural model — falcon cascade (v0.13)FV-FALCON-ARCH-002— spar codegen --format wit recheck — works at v0.10.0 (v0.15.0)FV-FALCON-GEO-003— Geofence safety path — miri UB/overflow check (v0.12, AI substitute)FV-FALCON-COV-003— witness MC/DC on real Rust source — Geofence subject (v0.14.1)FV-FALCON-SIM-001— PX4-SITL end-to-end loop — recipe + preset + smoke (v0.14.0)FV-FALCON-COV-001— witness MC/DC structural coverage — falcon pipeline wired (v0.13)FV-FALCON-SIM-005— gz-transport NavSat + Home projection — position-dependent loops (v0.18.1)
Source of truth: artifacts/verification/FV-FALCON-*.yaml.
Summary
The NED↔ENU frame unlock for the full verified cascade. A frame-check oracle established the true actuator-path convention; the fix was an SDF rotor index→position+spin alignment to
relay-mix-quad'sMIXER_Xconvention — not a code sign-flip. Horizontal drift collapsed from −13 m (v0.19.5) to ±0.15 m.The oracle (methodology: oracle leads)
--scenario=frame-{roll,pitch,yaw}commands a unit torque on one axis + hover thrust, reports the sensed body-rate sign. AGREE = +torque→+rate (required for the rate-PID negative feedback to be stabilising).Root cause
The v0.19.0–.5 SDF rotor index→position map was scrambled vs the real
MIXER_Xconvention (0=front-right CW, 1=back-right CCW, 2=back-left CW, 3=front-left CCW). The mixer's roll column applied to the wrong physical rotors nearly cancelled → roll authority ≈ 0. The v0.19.4/.5 "negate roll+pitch torque" hack was compensating for the scramble, not a real frame inversion.Fix
Align the SDF rotor index→position+spin to
MIXER_X(gz ENU coords).frame_correct_torqueis now identity; unit testframe_correction_is_identitypins it — if a future SDF/frame change reintroduces a flip, the oracle catches it.Horizontal-stability proof
Verification
cargo test --workspace --all-targets→ 407 passing (+1frame_correction_is_identity).cargo test -p falcon-sitl-gz --features gazebo→ 9/9.rivet validate→ PASS.Honestly NOT claimed
relay-pos's defaulthover_thrust0.5 is insufficient for the 2 kg body; ~0.72 needed). v0.19.7. The frame fix removed the horizontal runaway; lift + sustained hover is next.momentConstant); separate tuning, non-critical for hover.🤖 Generated with Claude Code