Opt-in mag-offset outage pin: hold heading through a GNSS gap with a trusted mag - #64
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Collaborator
Author
Update: grew from the pin into the magnetometer-tuning toolchainSince the original description, this branch became the full offline loop for Added:
Adversarial Opus review run on the branch: EKF core correct, unset-pin Tests: native 177, sim 206, both pico + pico_rawlog firmware build. Note: the pin stays default-OFF. The intended path is characterize the mag |
This was referenced Jul 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
An audit of the no-GPS heading coast (drift when GNSS lock is lost) plus one
opt-in hardening lever, validated in the sim. Requested with ocean-imu as a
reference and the option to swap the filter; see the verdict below on why no
swap. Nothing changes by default: the new knob is unset out of the box, so
shipped behaviour is identical (host 67, sim 190, pico build all green).
Audit findings
The filter already fails safe. The rudder only steers on heading while
heading_valid(sigma <= 5 deg,rudder_task.cpp). In sim a GNSS outagepushes heading sigma past 5 deg within ~16 s, mag on or off, so the rudder
stops steering on fused heading ~16 s into any outage. The large errors that
build over a long outage happen entirely after heading is already flagged
invalid; max error while still valid is < 1 deg. There is no "steering on a
silently-wrong heading" hazard in sim.
As tuned, the mag does not extend usable coast.
t_validis ~16 s for everycase (clean/moderate/indoor, mag-on/off). The loose
q_offset(1.0/step =100 deg^2/s) that correctly absorbs mag wander also floats the offset so fast
that even a perfect mag cannot pin heading during an outage. The mag's benefit
(re-bounding error to ~60 deg vs ~180 at 20 min) lands only after heading is
long since invalid.
So the real lever is usable coast time, and it is parameterized exactly by
the unknown: how good the boat's mag is.
ocean-imu verdict (no swap)
No filter to swap in. It is Eigen, 18-21 state, wave-aware Kalman targeting
ESP32-S3; its roll/pitch/heave work is redundant with the MTi-3's onboard marine
AHRS (we consume its fused quaternion), its QMEKF was already declined, and an
18-21 state Eigen filter at 100 Hz is not viable on the RP2040 (M0+, no FPU,
already stack-tight). Its heading-without-GPS still rests on a magnetometer, same
as our
mag_offsetpath, so no architecture beats the physics: no GPS and notrusted mag means heading drifts with gyro bias. The one transferable idea,
regime-aware measurement trust, is applied below.
The change: opt-in outage offset-pin
New optional
mti_yaw.q_offset_outage_deg2. Once GNSS has been absent pastMAG_OUTAGE_GRACE(3 s), the mag-offset random walk switches to this value.Pinning it small freezes the offset at its last GNSS-anchored value, so the mag
observes heading directly and holds it. Unset (default) keeps
q_offset_deg2and today's fail-safe behaviour.
Before vs after, static heading, 0.2 deg/s turn-on Z bias, 300 s outage:
Clean mag: heading held < 1 deg for the whole 5 min outage and stays valid, vs
losing heading at 16 s. Dirty mag: the pin makes the filter confidently wrong
(valid_err ~10-13 deg), which is why it is opt-in and must be gated on a
characterized-clean mag.
docs/tuning.mdexplains the tradeoff and how tocharacterize the mag.
Tests
test_fusion(+2): pinned offset holds heading and confidence through aclean-mag outage while the loose default decays past the steer-on bound; the
grace delays the switch.
test_outage_hold.py(new): same, end to end through the bound C++ filter.Recommendation
Before enabling the pin, characterize the boat's magnetometer: log MTi mag-yaw
vs GNSS heading over a straight run and measure the residual after the offset
converges. A steady residual is a clean mag (enable the pin); a wandering one is
not (leave it off, fail safe). Worth also capturing a real long-outage bench run
to check the sim's ~16 s fail-safe against hardware.
Notes
The 3-axis bias, gates, sigma caps, and q_offset were left as settled decisions;
this adds an orthogonal, default-off knob rather than re-tuning them.