Sitelet https://github.com/betaflight/betaflight/pull/15366
Skip to content

Position hold small improvements - #15366

Merged
haslinghuis merged 6 commits into
betaflight:masterfrom
ctzsnooze:position--hold-small-improvements
Jul 1, 2026
Merged

haslinghuis merged 6 commits into
betaflight:masterfrom
ctzsnooze:position--hold-small-improvements

Conversation

@ctzsnooze

@ctzsnooze ctzsnooze commented Jun 27, 2026 •

Copy link
Copy Markdown
Member

This is a tidying up PR to follow #15361.

The main change is to block stick input from taking control during a Nav mode. Stick inputs in Nav Mode will be ignored. The Pilot must exit Nav mode to regain control. Previously, any time a pilot moved the sticks, the stick inuts would control the craft, while the Nav PIDs, iterms etc.remained active in the background.

Other minor functional changes:

  • While a Mag is active, it is more strongly favoured over GPS, the pilot must fly fast and straight ahead for a long time for the GPS course over ground to offset the IMU heading towards that course over ground. This minimises the risk that lateral drifts while flying slowly forward could contaminate the IMU heading assessment from an otherwise good compass.
  • Sanity check now ensures the pilot will immediately regain stick control. The quad should flatten out in angle mode, the OSD indication of poshold failure should be displayed. A value of 50 is written into the status debug line to identify a sanity check failure
  • Sanity check distance is set to 20m, increasing above 20mfor incoming velocities over 10 m/s.
  • Sanity check unit test updated to suit.
    Refactoring:
    -use vectors and vector maths, not float arrays.
  • refactor the for loop for clarity
  • get the gyro_filter_debug_channel on init time
  • other minor changes

Position hold now works really well. Stops like a rock in just 1.2s from 8m/s existing velocity, using 45 deg pitch.Super clean and smooth motor requests. Amazing.

Todo:

  • test nav modes to check accuracy of velocity control, etc
  • confirm heading is OK and general behavior with optical flow sensors

Summary by CodeRabbit

Summary of Changes

  • Bug Fixes

    • Improved position-hold flyaway detection by lowering the XY safety threshold (2000 vs 3000), making triggers more responsive.
    • Refined position-hold fail handling so the controller cleanly exits the hold and transitions to a safer mode.
    • Updated GPS heading correction so a healthy magnetometer reduces GPS influence.
    • Improved control-mode behavior so navigation remains authoritative when navigation is active.
  • Tests

    • Updated unit tests to reflect the new flyaway threshold and revised sticks-active/settling expectations.

@github-actions

Copy link
Copy Markdown

Do you want to test this code? You can flash it directly from the Betaflight App:

  • Simply put #15366 (this pull request number) in the Select commit field in the Firmware Flasher tab (you need to Enable expert mode, Show release candidates and Development).

WARNING: It may be unstable. Use only for testing!

@coderabbitai

coderabbitai Bot commented Jun 27, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 05912237-0927-4e23-bdeb-ea1350539ee1

📥 Commits

Reviewing files that changed from the base of the PR and between da4b3d4 and d8c6bc9.

📒 Files selected for processing (1)
  • src/main/flight/autopilot_multirotor.c
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/flight/autopilot_multirotor.c

Walkthrough

Position-hold XY state now uses vector-backed storage and revised sanity-distance handling in positionControl(). The control loop caches the gyro debug axis for telemetry. imuCalcGroundspeedGain() now reduces GPS heading correction when a healthy magnetometer is present.

Changes

Multirotor position-hold and heading gain updates

Layer / File(s) Summary
State layout and debug-axis init
src/main/flight/autopilot_multirotor.c
SANITY_CHECK_DISTANCE is reduced, the XY position-hold state switches to vector2_t, and autopilotInit() caches debugAxis from the gyro debug-axis setting.
Position-control flow and telemetry
src/main/flight/autopilot_multirotor.c, src/test/unit/poshold_unittest.cc
updatePositionHoldTarget(), calculateSanityCheckDistance(), resetPositionControl(), and positionControl() use vector-backed XY state, vector distance checks, cached debug-axis selection, and the nav/sticks control policy update; the flyaway and stick-response tests now match the revised control outputs.
Groundspeed gain magnetometer suppression
src/main/flight/imu.c
imuCalcGroundspeedGain() adds MagSuppression to the GPS heading-correction gain, and imuCalculateEstimatedAttitude() adds a clarifying comment in the GPS COG path.

Sequence Diagram(s)

sequenceDiagram
  participant positionControl
  participant position estimator
  participant nav subsystem

  positionControl->>position estimator: read current XY position
  position estimator-->>positionControl: XY position
  positionControl->>nav subsystem: use target velocity when nav active
  nav subsystem-->>positionControl: target velocity
  positionControl->>positionControl: compute sanity distance and PID terms
  positionControl-->>positionControl: set sticksActive and zero roll/pitch on failure
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • betaflight/betaflight#15361: Also changes resetPositionControl(), positionControl(), and imuCalcGroundspeedGain() in the same navigation and heading-control paths.
  • betaflight/betaflight#15346: Also updates XY position-hold axis handling and stop-distance logic in autopilot_multirotor.c.
  • betaflight/betaflight#15150: Also touches the position-hold target capture and sanity-check distance logic in positionControl().

Suggested labels

RN: MINOR FEATURE, RN: REFACTORING

Suggested reviewers

  • SteveCEvans
  • haslinghuis
  • blckmn
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is related to the change set, but "small improvements" is too vague to convey the main update clearly. Use a more specific title such as "Block stick input during Nav mode and improve position-hold sanity checks".
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly summarizes the main behavior changes, refactoring, and test updates, with only minor gaps in implementation detail.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ctzsnooze
ctzsnooze requested a review from blckmn June 27, 2026 05:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/main/flight/imu.c (1)

459-465: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the healthy-mag suppression path in the IMU unit test.

This branch now changes groundspeedGain when sensors(SENSOR_MAG) && compassIsHealthy() is true, but the current src/test/unit/flight_imu_unittest.cc stub only enables SENSOR_ACC while still returning true for compassIsHealthy(). That leaves MagSuppression at 1.0f in tests, so the new 0.1x path can regress unnoticed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/flight/imu.c` around lines 459 - 465, The healthy-magnetometer
suppression branch in the IMU logic is not covered by the unit test setup, so
the new 0.1f path can regress unnoticed. Update the flight IMU test in
src/test/unit/flight_imu_unittest.cc so it enables SENSOR_MAG in the sensor stub
while compassIsHealthy() still returns true, and verify the behavior exercised
through the IMU update logic that uses MagSuppression/groundspeedGain. Keep the
existing ACC stub, but add a test case that explicitly hits the
sensors(SENSOR_MAG) && compassIsHealthy() path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/flight/autopilot_multirotor.c`:
- Around line 452-456: The autopilot angle reset in autopilotMultirotor should
not depend on navActive, since manual stick input must always zero the outputs.
Update the condition around the autopilotAngle[AI_ROLL] and
autopilotAngle[AI_PITCH] assignments so any ap.sticksActive state forces both
angles to 0.0f before the rotation branch runs, keeping the fix localized in the
autopilotMultirotor control flow.
- Around line 318-320: The position-hold startup in resetPositionControl is
enabling held mode before the hold target is initialized, which leaves
positionControl and updatePositionHoldTarget working with stale or zero
targetPosition/posHoldStartPosition data. Initialize the hold target first by
calling the position-hold setup logic (the same initialization used by
initPositionHold and positionControl) before setting isPositionHeld true, so the
first held-mode cycle uses the current XY position instead of skipping target
initialization.

---

Nitpick comments:
In `@src/main/flight/imu.c`:
- Around line 459-465: The healthy-magnetometer suppression branch in the IMU
logic is not covered by the unit test setup, so the new 0.1f path can regress
unnoticed. Update the flight IMU test in src/test/unit/flight_imu_unittest.cc so
it enables SENSOR_MAG in the sensor stub while compassIsHealthy() still returns
true, and verify the behavior exercised through the IMU update logic that uses
MagSuppression/groundspeedGain. Keep the existing ACC stub, but add a test case
that explicitly hits the sensors(SENSOR_MAG) && compassIsHealthy() path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a7bc4c3d-4477-4626-a4d2-07f323284d3c

📥 Commits

Reviewing files that changed from the base of the PR and between e20d2cc and 81cf448.

📒 Files selected for processing (2)
  • src/main/flight/autopilot_multirotor.c
  • src/main/flight/imu.c

Comment thread src/main/flight/autopilot_multirotor.c Outdated
Comment thread src/main/flight/autopilot_multirotor.c Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/test/unit/poshold_unittest.cc (1)

193-196: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Re-add assertions for the failure side effects.

positionControl() now returns false only after it also hands stick control back, levels roll/pitch, and writes the failure debug/status 50 in src/main/flight/autopilot_multirotor.c:324-386. With only EXPECT_FALSE, regressions in those outputs would still pass even though they are part of this PR’s stated behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/unit/poshold_unittest.cc` around lines 193 - 196, The poshold unit
test only checks the boolean result from positionControl(), so regressions in
the failure side effects can slip through. Update the test around
positionControl() to also assert the expected failure behavior: stick control is
handed back, roll/pitch are leveled, and the failure debug/status value 50 is
written when the sanity check distance is exceeded.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/test/unit/poshold_unittest.cc`:
- Around line 193-196: The poshold unit test only checks the boolean result from
positionControl(), so regressions in the failure side effects can slip through.
Update the test around positionControl() to also assert the expected failure
behavior: stick control is handed back, roll/pitch are leveled, and the failure
debug/status value 50 is written when the sanity check distance is exceeded.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2000e552-7a41-4e8e-87f4-8d17c2af580c

📥 Commits

Reviewing files that changed from the base of the PR and between 81cf448 and 73bc92b.

📒 Files selected for processing (1)
  • src/test/unit/poshold_unittest.cc

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/test/unit/poshold_unittest.cc (1)

193-196: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep assertions for the new flyaway fail-safe side effects.

This change now appears to only check the false return, but the new contract also levels roll/pitch, re-enables manual sticks, and writes debug status 50 on sanity failure (src/main/flight/autopilot_multirotor.c, Lines 361-383). Please keep explicit assertions for those side effects here so this fail-safe path cannot regress silently.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/unit/poshold_unittest.cc` around lines 193 - 196, The
positionControl() sanity-failure test currently only asserts the false return,
but it should also verify the flyaway fail-safe side effects introduced in
autopilot_multirotor behavior. Update the poshold_unittest case that manipulates
testEstimate.position.x and calls positionControl() to explicitly assert
roll/pitch are leveled, manual sticks are re-enabled, and the debug status is
set to 50 when the sanity check fails, using the existing positionControl() test
fixture and related state accessors.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/test/unit/poshold_unittest.cc`:
- Around line 193-196: The positionControl() sanity-failure test currently only
asserts the false return, but it should also verify the flyaway fail-safe side
effects introduced in autopilot_multirotor behavior. Update the poshold_unittest
case that manipulates testEstimate.position.x and calls positionControl() to
explicitly assert roll/pitch are leveled, manual sticks are re-enabled, and the
debug status is set to 50 when the sanity check fails, using the existing
positionControl() test fixture and related state accessors.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1b7948be-f2cb-407f-a7cb-458f404288ee

📥 Commits

Reviewing files that changed from the base of the PR and between fdae4de and da4b3d4.

📒 Files selected for processing (2)
  • src/main/flight/autopilot_multirotor.c
  • src/test/unit/poshold_unittest.cc
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/flight/autopilot_multirotor.c

@haslinghuis
haslinghuis merged commit 4a9cc32 into betaflight:master Jul 1, 2026
53 checks passed
@github-project-automation github-project-automation Bot moved this from Bugfix to Done in 2026.6.0 Jul 1, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Jul 2, 2026
gwlim pushed a commit to gwlim/betaflight that referenced this pull request Jul 25, 2026
* Don't adapt a mag heading to GPS COG unless there is very strong forward flight

* Refactoring, improve sanity check distances and behaviour

* fix unit test for sanity check to test to expect return false

* lock sticks out of nav modes, minor refactoring

* change unit tests

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

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants