Skip to content

Scale the gyro calibration movement threshold with sensitivity - #11905

Open
Raffi1202 wants to merge 2 commits into
iNavFlight:maintenance-10.xfrom
Raffi1202:fix/gyro-calibration-scaling
Open

Raffi1202 wants to merge 2 commits into
iNavFlight:maintenance-10.xfrom
Raffi1202:fix/gyro-calibration-scaling

Conversation

@Raffi1202

@Raffi1202 Raffi1202 commented Sep 10, 2026

Copy link
Copy Markdown

Problem

@and-sh reported in #10650 that the gyro zero calibration never completes when the gyro is configured for a higher sensitivity than the default. He hit it on a custom board with an ICM45686 while testing different full-scale ranges, and pointed out that the standard deviation is computed from raw readings while the threshold does not depend on the gyro scale. Fixes #10650.

Cause

src/main/sensors/sensors.h:46 defines CALIBRATING_GYRO_MORON_THRESHOLD 32 in raw counts, and src/main/sensors/gyro.c:380 passes it unchanged to zeroCalibrationStartV(). performGyroCalibration() (gyro.c:403-405) feeds gyroADCRaw[] into the calibrator, and src/main/common/calibration.c:159 compares the raw-count standard deviation against that fixed number, restarting the window whenever it is exceeded. A gyro with more LSB per dps shows a larger raw deviation for the same physical stillness, so every window fails.

Change

sensors.h replaces the constant with CALIBRATING_GYRO_MORON_THRESHOLD_DPS (32.0f / 16.4f), i.e. 1.95 dps, which is what 32 LSB meant at the default 16.4 LSB/dps. gyroStartCalibration() in gyro.c divides that by gyroDev[0].scale and passes the result as the threshold. Drivers with scale = 1.0f / 16.4f keep exactly 32 LSB; LSM6DXX Gen-V (scale = 0.070f) moves to 27.9 LSB and the fake gyro (0.0625f) to 31.2 LSB.

Test

Not run on hardware or SITL. Cause verified by reading sensors.h:46, gyro.c:380-405 and calibration.c:154-171 on maintenance-10.x. Not compiled: no fork CI run exists for fd6d88e, and the upstream "Build firmware" run (https://github.com/iNavFlight/inav/actions/runs/34512924844) is waiting for approval with no jobs. sensors/gyro.c is compiled by flight_imu_unittest (src/test/unit/CMakeLists.txt:20-23); no test references the old macro. Compiled for all targets and the four SITL builds on the fork, green: https://github.com/Raffi1202/inav/actions/runs/34770676315

Flash / RAM

Builds clean on all targets. No size comparison yet: the fork build has no baseline for this branch, and the upstream size report runs once CI is released for this PR.

Docs

No documentation change needed: no setting or user-visible procedure changes; docs/Buzzer.md:15 describes the calibration restart on movement and stays correct.

Interaction with #11933

#11933 (dual-gyro blackbox logging, targeting master) adds a second
zeroCalibrationStartV() call in the same function, gyroStartCalibration(), and it passes
CALIBRATING_GYRO_MORON_THRESHOLD - the constant this PR removes. Whichever of the two lands
second will either fail to build or leave the secondary gyro on the unscaled threshold, which is
the bug this PR fixes. Raised by @MrScothh on #11933; noted here so whoever merges first sees it.

The clean end state is one expression per sensor over its own gyroDev[n].scale, since a dual-IMU
board may pair two unrelated parts and the primary's scale is the wrong reference for the
secondary.

The movement threshold of the gyro zero calibration was a fixed number of
raw sensor counts. A gyro configured for a higher sensitivity reports more
counts for the same physical rotation, so its standard deviation exceeded
the threshold while the board was perfectly still and the calibration
restarted forever.

Express the threshold as a rotation rate and convert it into sensor counts
using the scale reported by the driver. At the default 16.4 LSB/dps the
effective threshold stays at 32 counts.

Fixes iNavFlight#10650
@Raffi1202
Raffi1202 marked this pull request as ready for review September 11, 2026 15:55
@qodo-code-review

Copy link
Copy Markdown
Contributor

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Scale gyro calibration threshold by sensor sensitivity

🐞 Bug fix 🕐 Less than 10 minutes

Grey Divider

AI Description

• Expresses gyro movement tolerance as a physical rotation rate.
• Converts tolerance to raw counts using detected gyro sensitivity.
• Preserves the legacy 32-count threshold at the default scale.
Diagram

graph TD
    A["DPS threshold"] --> C["Gyro calibration"] --> D["Zero calibrator"]
    B["Driver scale"] --> C
Loading
High-Level Assessment

The current approach is appropriately localized: scaling the threshold once preserves raw calibration samples and raw zero-offset semantics while avoiding per-sample conversion. Converting all calibration samples to physical units would require converting the resulting offsets back to raw counts and would introduce unnecessary complexity.

Files changed (2) +9 / -2

Bug fix (2) +9 / -2
gyro.cConvert calibration tolerance using detected gyro sensitivity +5/-1

Convert calibration tolerance using detected gyro sensitivity

• Calculates the raw-count movement threshold from the physical DPS tolerance and the detected gyro's scale before starting zero calibration. This prevents sensitive gyros from repeatedly restarting calibration while preserving default behavior.

src/main/sensors/gyro.c

sensors.hDefine gyro calibration tolerance in physical units +4/-1

Define gyro calibration tolerance in physical units

• Replaces the fixed 32-count threshold with a DPS-based constant equivalent to 32 counts at the default 16.4 LSB/dps sensitivity.

src/main/sensors/sensors.h

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can switch off images and animations for a plain-text comment

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@sensei-hacker

Copy link
Copy Markdown
Member

Note: #11932 fixes gyroUpdateAndCalibrate() writing to gyroCalibration[0] directly instead of the passed-in calibration state. Different bug, same function — worth checking both apply cleanly together.

@Raffi1202

Copy link
Copy Markdown
Author

@sensei-hacker Looked at #11932 - they do not touch, in either sense.

Different functions, ~50 lines apart in sensors/gyro.c:

No shared hunk, so they apply cleanly in either order.

No shared state either. gyroCalibration[] is sized MAX_GYRO_COUNT, but every access in the file uses index 0: the start at :380, the completion check at :395, and the only call into gyroUpdateAndCalibrate() at :581, which passes &gyroCalibration[0]. So #11932 is a correctness fix that changes no behaviour today - it makes the function honour its parameter for a future second gyro. This PR only changes the movement threshold handed in at start time. One sets the threshold, the other fixes who owns the state; neither reads what the other writes.

Worth merging #11932 regardless - passing a parameter and then ignoring it is the kind of thing that bites exactly once, later.

Still unbuilt here: the firmware CI has not been released for this PR, so the threshold change has not been compiled upstream. The arithmetic is 32 / 16.4 dps against gyroDev[0].scale, which reproduces the legacy 32 LSB exactly at the default sensitivity.

MrScothh added a commit to MrScothh/inav that referenced this pull request Sep 13, 2026
The movement threshold is a number of raw counts, and raw counts mean different
rotation rates on different parts - a dual-IMU board is free to pair two unrelated
sensors. Passing the constant unchanged asked the secondary for the same number
rather than the same physical stillness, so a more sensitive secondary could fail a
calibration the primary passes.

Converting through both scales asks for the same stillness. Where the two sensors
are the same part the scales cancel and the value is exactly the old constant, so
nothing changes on the boards this has been tested on.

This is the same class of bug as iNavFlight#11905, which fixes it for the primary by moving
the constant into dps. That change and this one both edit gyroStartCalibration();
once it lands, both call sites become the same expression over each sensor's own
scale.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Asks each gyro for its own scale by index instead of hardcoding the
primary. A dual-IMU board may pair two unrelated parts, so the primary's
sensitivity is the wrong reference for a secondary sensor. Requested on
 iNavFlight#11933, which adds a second calibration call site in this function.
@sensei-hacker

Copy link
Copy Markdown
Member

I have approved the CI here, which may make testing on hardware slightly easier.

@github-actions

Copy link
Copy Markdown

RAM / Flash usage vs. base commit 76ee415 — commit 668db00

Target Flash Δ RAM Δ
MATEKF405 +168 B (+0.02%) ±0 B (±0.00%)
MATEKF722 +104 B (+0.02%) ±0 B (±0.00%)
MATEKF765 +120 B (+0.02%) ±0 B (±0.00%)
MATEKH743 +104 B (+0.01%) ±0 B (±0.00%)

See RAM/flash optimization guide for techniques to reduce usage.

@github-actions

Copy link
Copy Markdown

Test firmware build ready — commit 668db00

Download firmware for PR #11905

249 targets built. Find your board's .hex file by name on that page (e.g. MATEKF405SE.hex). Files are individually downloadable — no GitHub login required.

Development build for testing only. Use Full Chip Erase when flashing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants