Proposal: Early abort compass calibration when magnetic field is extreme

Hi all,

I’d like to propose a small improvement to the compass calibrator that would
save users a lot of frustration in a specific but common failure scenario.

The problem

I was calibrating the compass on my ZOHD Drift (ArduPlane 4.6.3, Matek
F405-WMN, external QMC5883L). I rotated the aircraft for over 3 minutes in
Mission Planner — the progress bar kept advancing slowly but calibration never
completed. I tried multiple times.

It turned out the foam hatch lid had small neodymium magnets embedded in it
(factory hatch closure magnets). These produced a field of ~3800 mGauss at the
compass location. The valid range for Earth’s field is roughly 185–875 mGauss,
so the reading was 4× too high.

Once I removed the magnets, the field dropped to ~425 mGauss and calibration
succeeded on the first attempt.

Why it took so long to diagnose

The calibrator gave zero feedback during the 3+ minutes of spinning. The
progress bar moved normally. There was no message saying “hey, the field is way
too high, something is wrong.” The failure only came at the very end, with no
hint about magnets.

The pre-arm check does catch this ("Check mag field: 3800, max 875, min 185"), but that only runs at arming — long after you’ve already spent minutes
trying to calibrate.

What happens in the code

Looking at CompassCalibrator.cpp:

  1. pull_sample() accepts all samples regardless of magnitude
  2. Samples are collected until the buffer is full (~300 samples)
  3. Sphere fit runs (STEP_ONE), then more samples, then ellipsoid fit (STEP_TWO)
  4. Only at the very end does fit_acceptable() check:
    _params.radius > FIELD_RADIUS_MIN && _params.radius < FIELD_RADIUS_MAX
    (where MIN=150, MAX=950)
  5. With ~3800 mGauss, the fitted radius is ~3800, which fails — but only after
    the entire pipeline has run

There is no early sanity check on the incoming field magnitude.

Proposed fix

Add a field-magnitude check in pull_sample() after 10 samples have been
collected. If the average magnitude is wildly out of range, abort immediately
with a clear message:

// In pull_sample(), after _samples_collected is incremented:
if (_samples_collected == 10) {
    float total_magnitude = 0;
    for (uint16_t i = 0; i < 10; i++) {
        total_magnitude += _sample_buffer[i].get().length();
    }
    float avg_magnitude = total_magnitude / 10;
    if (avg_magnitude > FIELD_RADIUS_MAX * 2) {
        GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL,
            "Mag(%u) field too high: %.0f mGauss (max %d). Check for magnets near compass",
            _compass_idx, avg_magnitude, FIELD_RADIUS_MAX);
        set_status(Status::BAD_RADIUS);
        return;
    }
    if (avg_magnitude < FIELD_RADIUS_MIN / 2) {
        GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL,
            "Mag(%u) field too low: %.0f mGauss (min %d). Check compass connection",
            _compass_idx, avg_magnitude, FIELD_RADIUS_MIN);
        set_status(Status::BAD_RADIUS);
        return;
    }
}

Thresholds:

  • Too high: >1900 mGauss (2× FIELD_RADIUS_MAX). Earth’s field peaks at ~650
    at the poles. 1900 is clearly not Earth’s field.
  • Too low: <75 mGauss (½ FIELD_RADIUS_MIN). Suggests sensor fault or
    disconnected compass.

Why 10 samples: Available within the first 1–2 seconds. Enough to average
out noise. Tiny fraction of the ~300 total needed.

Impact

  • User gets actionable feedback within ~1 second instead of 3+ minutes
  • Message is delivered via STATUSTEXT so all GCS apps display it automatically
    (no Mission Planner / QGC changes needed)
  • BAD_RADIUS status already exists and is handled by all GCS
  • No behavior change for normal calibrations (threshold is extremely generous)
  • No new parameters

User experience comparison

Before: Start cal → spin 3 min → fail → no idea why → repeat → give up

After: Start cal → 1 sec → “Mag(0) field too high: 3800 mGauss (max 950).
Check for magnets near compass” → remove magnets → recalibrate → success

This isn’t a new problem

The lack of early feedback during compass calibration has been a pain point for
years. A few related reports:

  • Onboard Mag calibration doesn’t complete #4734 (43 comments, 2016–2019)
    One user reported getting blisters from rotating a hex for hours. The QGC lead
    dev commented: “People are bailing out of ArduPilot because of it.” Another
    contributor asked the question this proposal answers: “Is there a way to know
    if there is EM noise making the calibration from happening? If so, that should
    be shown to the user somehow.”

  • Error: Mag(0) bad radius xxx expected 222 #14393 (2020)
    Multiple users hitting the “bad radius” error after calibration runs to
    completion — same end symptom, different root cause (low field in Brazil vs
    high field from magnets), but in both cases the calibrator runs the full
    pipeline before telling you something was wrong from the start.

  • Onboard Compascal stuck if in certain configuration #9316 (2018)
    Calibration gets stuck with no progress and no feedback. Different root cause
    (compass ordering), but same user frustration pattern — zero indication of
    what’s wrong.

  • Compass Interference Causing EKF Crash on copter (Feb 2023, Discourse)
    Recurring compass interference causing EKF crashes and flyaways — the kind of
    scenario that starts with a calibration that “succeeded” in a bad environment.

The common thread: when the magnetic environment is fundamentally wrong,
the calibrator gives no early warning. It either runs to completion and fails
cryptically, or sits there collecting useless samples forever.

Summary

I think this is a fairly small, safe change that could save users a lot of
confusion. Magnets in foam hatches/canopies are surprisingly common in RC
aircraft — it’s not an exotic failure mode.

Happy to submit a PR if the approach seems reasonable. Would appreciate any
feedback on the thresholds or implementation location.

Thanks,
Christian

2 Likes

Rather than copying and pasting this rather long, likely LLM generated diatribe, perhaps you could test such a fix/feature and propose a pull request?

Thanks for the feedback! You’re right, a PR is the way to go. I’ll implement and test it, and if the results look good I’ll open one against master.

1 Like

PR submitted: AP_Compass: early abort calibration on extreme magnetic field by christianpetri · Pull Request #32757 · ArduPilot/ardupilot · GitHub

Implemented and tested on MatekF405-TE with external QMC5883L:

- With neodymium magnet near compass (~4800 mGauss): calibration aborts in ~1 sec

- Without magnet (~425 mGauss): calibration completes normally

Checks average field magnitude after 10 samples. If wildly out of range, aborts with BAD_RADIUS and a GCS message. No new parameters, no change for normal calibrations.

2 Likes