Skip to content

Autotune: applying recommended gains can cause instability/flyaway on arm (D-term filtering reduced from chirp coherence) #5258

Description

@ChrisRosser

Summary

Applying the Autotune tab's recommended gains caused an instant flyaway on arming (5" quad, Betaflight 2026.6.0-rc1). Reviewing recommendGains / computeGainScales in src/js/blackbox/spectral_analysis.js, several issues can combine to produce a destabilising tune. The most serious is that D-term filtering is reduced based on chirp coherence, which does not measure the gyro noise floor.

The analysis quality itself was excellent (mean coherence 97–100%) — ironically, the cleaner the log, the more aggressively the tool strips D-term filtering (see #1 below).

Environment

  • Firmware: Betaflight 2026.6.0-rc1 (F722)
  • Craft: 5"
  • Code reviewed against current master.

Root cause analysis

1. D-term filter cutoff is driven by chirp coherence (primary flyaway trigger)

// findNoiseFloor(): first freq >20 Hz where coherence < 0.5, else…
return frequencies.at(-1);            // fall back to the TOP frequency
...
let filterScale = noiseFloorHz / defaultFilterHz;   // defaultFilterHz = 150
...
filterScale: clamp(filterScale, 0.5, 2),

On a clean log, coherence never drops below 0.5, so findNoiseFloor returns the top of the spectrum (Nyquist, ~1 kHz+). Then filterScale ≈ 1000/150 → clamped to 2, i.e. the tool doubles the D-term filter cutoff and removes ~half the D-term filtering.

This is a category error: chirp coherence measures how well the gyro tracks the injected sweep, not the broadband gyro/motor noise floor that D-term filtering exists to suppress. A craft can have excellent chirp coherence and significant 200–500 Hz noise. Removing D-term filtering is a classic route to D-term-noise → hot motors → oscillation the instant motors spin, with no stick input or movement — consistent with "flyaway on arm".

2. No joint stability check

computeGainScales clamps each scale to ≤2× individually, but nothing checks the combination. For a slightly-soft tune (bandwidth ~30 Hz vs the 45 Hz target) the tool simultaneously raises P (×1.5), FF (×1.5), D (~×1.4) and the D-term filter cutoff (×2). The only post-apply guard is slider_pids_valid / slider_dterm_valid, which validate slider range, not tune stability.

3. Per-axis analysis written to global sliders

recommendGains runs per axis (useAutotune.js computeAxisResult), but the simplified sliders it writes are global. Whichever axis's recommendation is applied is imposed on all three axes, with no reconciliation.

Impact

Applying recommended gains can destabilise the craft immediately on arm — a safety issue. Until hardened, consider gating "Apply" behind an explicit warning + opt-in, before it writes sliders and EEPROM.

Suggested fixes

  • Never reduce D-term filtering from chirp coherence — cap filterScale ≤ 1. Immediate mitigation submitted in the linked PR.
  • Longer term, source any D-term filter change from actual gyro-noise analysis (as the noise-analysis / dynamic-notch tooling does), or leave the D-term filter untouched by autotune.
  • Reconcile per-axis recommendations before writing the global sliders (e.g. apply the most conservative across axes).
  • Add a joint sanity/stability guard, and an explicit user confirmation before apply + EEPROM write.

Related

Part of hardening the new Autotune tab — see #5256 and #5257 (separate parsing/validation fixes).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions