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).
Summary
Applying the Autotune tab's recommended gains caused an instant flyaway on arming (5" quad, Betaflight 2026.6.0-rc1). Reviewing
recommendGains/computeGainScalesinsrc/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
master.Root cause analysis
1. D-term filter cutoff is driven by chirp coherence (primary flyaway trigger)
On a clean log, coherence never drops below 0.5, so
findNoiseFloorreturns the top of the spectrum (Nyquist, ~1 kHz+). ThenfilterScale ≈ 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
computeGainScalesclamps 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 isslider_pids_valid/slider_dterm_valid, which validate slider range, not tune stability.3. Per-axis analysis written to global sliders
recommendGainsruns per axis (useAutotune.jscomputeAxisResult), 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
filterScale ≤ 1. Immediate mitigation submitted in the linked PR.Related
Part of hardening the new Autotune tab — see #5256 and #5257 (separate parsing/validation fixes).