bbdoctor
Drone diagnostics from logs: Betaflight, INAV, ArduPilot, PX4
RUENESPTUA

INAV Blackbox Analysis: Tuning PIDs and Filters From Your Log

People run INAV for navigation: position hold, return to home, missions. All of it rests on the inner rate loop. If the gyro is noisy and the PIDs are off, the quad holds altitude worse, sways in POSHOLD and cooks its motors. Below: how to record a log worth analysing, which INAV settings do what, and what nine real INAV 6–8 logs showed in bbdoctor.

Logging setup: rate, raw gyro, motors

INAV sets the logging rate as a fraction of the loop rate: 1,000,000 / looptime × blackbox_rate_num / blackbox_rate_denom. With the standard looptime = 500 (2 kHz), 1/2 gives 1 kHz and 1/32 roughly 62 Hz. Two INAV 7.1 quads in our collection logged at 60 and 58 frames per second.

blackbox GYRO_RAW
blackbox MOTORS
set blackbox_rate_num = 1
set blackbox_rate_denom = 2
save

With a small flash chip, keep 500 Hz and fly short 30–60 s test flights instead. To pin down a resonance, record one short 1/1 log (2 kHz). General advice: the blackbox setup guide.

INAV 8 and newer log activeFlightModeFlags, the modes that were actually active, so ACRO is easy to tell from self-levelling and navigation modes. INAV 7 and older only log flightModeFlags, the mode switches that were on, and a switched-on navigation mode may never engage (no GPS fix, for example).

What to fly for the log

PIDs can only be judged from ACRO. In ANGLE, HORIZON and navigation modes the stick commands an angle; an angle loop sits between stick and rate loop, so the response says little about the PIDs. bbdoctor leaves those stretches out of the PID verdict. On the second flight of the H743 quad below (73 s, 78% ANGLE/HORIZON) the report covers noise, filters and load balance, but says nothing about overshoot: there is nothing to judge.

  1. 6–8 sharp rolls and flips, snapping the stick back to centre and holding it there for half a second. With fewer than six stops per axis bounce-back can't be checked; the good H743 log had one per axis.
  2. 5–8 clean throttle chops: climb at over 35% throttle, snap to zero, sticks centred for a second. A chop that runs into a flip is useless for propwash.
  3. A few punch-outs from hover to full throttle, plus some steady flying at different throttle levels for the noise map.

A large craft needs no flips: short, sharp tilts at a safe height will do. Leave navigation off.

INAV settings you tune from the log

SettingDefaultWhat it does, when to touch it
mc_p_roll, mc_i_roll, mc_d_roll (pitch the same)40 / 30 / 23Main PIDs. Overshoot from the P/D loop: less P or more D. I fights constant forces: an off-centre CG, wind.
mc_p_yaw, mc_i_yaw, mc_d_yaw85 / 45 / 0Yaw D is zero, so the log has no axisD[2] field.
mc_cd_roll, mc_cd_pitch, mc_cd_yaw60Control Derivative, INAV's feedforward, driven by stick speed. Logged as axisF.
gyro_main_lpf_hz60 HzMain gyro lowpass (gyro_lpf_hz in the header). Lower is quieter but slower.
dterm_lpf_hz, dterm_lpf_type110 Hz, PT2D-term lowpass. Hot motors with noise coming through D: lower by about 15%.
dynamic_gyro_notch_enabled, dynamic_gyro_notch_min_hz, dynamic_gyro_notch_qON, 50 Hz, 120Dynamic notches (Matrix Filter). The lower limit must sit below the resonance, or the notch can't reach it.
rpm_gyro_filter_enabled, rpm_gyro_min_hzOFF, 100 HzFed by ESC telemetry; enable only if it (usually a separate wire) delivers sensible RPM.
setpoint_kalman_enabled, setpoint_kalman_qON, 100Kalman filter on the gyro. Higher q filters less, with less delay.
d_boost_min, d_boost_max0.5 / 1.25D is scaled down (×0.5) on fast stick moves and up (×1.25) on sharp external disturbances.
tpa_rate, tpa_breakpoint, tpa_on_yaw0 / 1500 / OFFAbove the breakpoint TPA scales down P, D and CD (FF); 0 = off. Overshoot or shakes only at high throttle: turn it on.
mc_iterm_relax, mc_iterm_relax_cutoffRP / 15Stops I winding up on fast stick moves. Bounce-back: cutoff 15 → 10; on yaw, mc_iterm_relax = RPY.
antigravity_gain, antigravity_accelerator1 / 1Anti-gravity, where 1 means off. Nose dips on sharp throttle: set 2.
airmode_typeSTICK_CENTERSTICK_CENTER stops I growing at zero throttle with centred sticks. The INAV docs recommend THROTTLE_THRESHOLD for props up to 7".
throttle_idle8 (%)Motor idle. Propwash with a motor sitting at idle: +2.
ez_enabledOFFEzTune: when ON, PIDs, filters and rates come from the ez_filter_hz, ez_response, ez_damping and other sliders, and manual edits get overwritten.

Defaults are from INAV's Settings.md; confirm yours with get. Many never reach the log header: it has the PID strings, gyro_lpf_hz, dterm_lpf_hz, dynamicGyroNotchMinHz, TPA and rpm_gyro_*, but no d-boost, anti-gravity, I-term relax, airmode_type or any EzTune setting, and bbdoctor reconstructs throttle_idle from minthrottle. That's why the report says "check with get" for those. Save your current values first:

get mc_
get dterm_lpf
get dynamic_gyro_notch
get antigravity
get mc_iterm_relax
get throttle_idle
get airmode_type

For the whole configuration, paste your diff all into the config check.

Real INAV logs and what they showed

INAV 7.1 at 60 Hz: no vibration, no PIDs

A Matek F722SE on 6S (flights of 213 and 171 seconds) and a SpeedyBee F7 V3 (26 seconds), both with blackbox_rate_denom = 32 on a 2 kHz loop: 60 and 58 frames per second. The F722SE wasn't logging GYRO_RAW either, and every flight was entirely ANGLE/HORIZON. Only 0–30 Hz is visible, so motor noise, resonances and filters are out of view, and the PIDs can't be judged twice over: wrong rate, wrong mode.

Still usable: the load split between motors (on the F722SE the right motors work 17% harder: the CG sits to the right, so check the battery and anything heavy off-centre), battery voltage under load, failsafe events and the crash timeline. These are slow processes; 60 Hz is enough.

Pitch gyro spectrum: top, an INAV 8 log at 995 Hz with a 117 Hz peak in the raw gyro; bottom, an INAV 7.1 log at 60 Hz whose spectrum ends at 30 Hz
A: INAV 8 at 995 Hz, raw (grey) and filtered (blue) pitch gyro: a 117 Hz peak, plus a hump near 147 Hz that the filters almost remove. B: INAV 7.1 at 60 Hz, same scale: the spectrum stops at 30 Hz; the hatched part never reached the log.

INAV 8, Matek H743, 6S: a 117 Hz resonance and overshoot that isn't FF

A good log: 995 Hz, GYRO_RAW, 55 seconds of ACRO, with gains well above stock: roll P 80, I 22, D 32, FF 100; pitch 88 / 24 / 35 / 110.

The report's logic: pitch P down 10%. Roll P stays, because the axis already responds with 31 ms of delay; roll D goes up 10% (32 → 35) only once the motor noise is gone, or the motors run hotter.

set dterm_lpf_hz = 157
set mc_p_pitch = 79
save

Check EzTune first, though. The filters in this log match the EzTune formula for ez_filter_hz = 190 exactly: gyro_lpf_hz 190, dterm_lpf_hz 5 Hz lower, the notch floor at two thirds (126) and notch Q 250. The pitch gains are 1.1 times roll, which is ez_axis_ratio 110. If get ez_enabled says ON, EzTune will overwrite the commands above. Either set ez_enabled = OFF (current PIDs and filters stay) and then apply them, or use the slider: ez_filter_hz 190 → 160 gives dterm_lpf_hz 155, but also drops the gyro lowpass to 160 Hz (more delay) and the notch floor to 106 Hz.

escRPM in this log is alive and follows the throttle, yet rpm_gyro_filter_enabled = 0. The RPM filter would remove some motor noise, but not the 117 Hz resonance, which doesn't move with motor speed.

Pitch gyro noise map against throttle: left, the raw gyro with a bright vertical band at 117 Hz; right, after filtering, where the band is weaker and noise above 200 Hz is gone
INAV 8.0, Matek H743, 995 Hz: pitch noise against throttle. Left, raw gyro: a 117 Hz band at every throttle level, 12 dB above the background. Right, filtered: noise above 200 Hz is gone, the 117 Hz band is about 17 dB weaker but still there.

INAV 8, BetaFPV F411, 1S whoop: D twelve times stronger than P

Ten seconds at 973 Hz with GYRO_RAW. Roll PID: P 17, I 86, D 50, FF 60; pitch 40 / 90 / 35 / 60. Roll D is three times P, while stock INAV has D 23 against P 40. In the log the roll D-term is 12 times stronger than P and carries almost all the motor noise: 5.5% on the motors (M4, critical), 22.1 deg/s of raw roll noise, 4.8 deg/s left after filtering. It looks like a typo or swapped values, and that's where the report starts:

set mc_d_roll = 35
save

Three more findings. The RPM filter is on (rpm_gyro_filter_enabled = 1), yet escRPM holds the same meaningless number all flight; so the filter apparently ran without RPM data. The M1+M4 diagonal works harder all the time (46%): the mixer is constantly correcting yaw, so look for a tilted motor or mismatched props. Propwash was 9.4 deg/s, but from only three clean chops (a confident verdict needs five), and in two of them the weakest motor sat at idle for 150 ms or more of the half-second window after the chop. That's not a lack of D; the flight controller has nothing left to brake with. If the next flight confirms it: throttle_idle 8 → 10 and airmode_type = THROTTLE_THRESHOLD. More in the propwash guide.

INAV 6.x: the filtered gyro dropped to zero while the raw gyro lived on

Two quads, both crashed. INAV 6.1.1 on an AocodaRC H7 Dual (3S): the filtered gyro reads a flat zero for 0.9 s as a failsafe fires, although RC packets keep arriving. INAV 6.0.0 on an iFlight Blitz F7 (6S): zero for 1.5 s, failsafe 1.4 s before a 9 g impact, throttle falling from 32% to 4%, and again RC packets still arriving. Both times the raw gyro kept seeing rotation: the sensor was fine, the zero came from the firmware.

bbdoctor calls it a freeze when all three gyro axes stay exactly constant for 0.3 s or more while airborne. Without GYRO_RAW you can't tell a firmware glitch from a sensor failure. What to do: update the firmware, check whether failsafe sits on a switch on your radio, and review failsafe_procedure. If it happens without a failsafe too, check the soldering and the FC's 3.3 V supply.

Tuning order for INAV

  1. Logging. 500 Hz or more, GYRO_RAW, MOTORS. Decide on EzTune: work its sliders, or set ez_enabled = OFF and tune by hand.
  2. Mechanics. Raw noise above 4 deg/s on roll or pitch, peaks that climb with throttle (props, motors), a peak fixed in frequency (frame): props, screws and a soft-mounted stack come first. Filters don't fix mechanics. More in the noise and filter guide.
  3. Filters. Motor noise of 2.5% or more and warm motors: dterm_lpf_hz −15%. Noise still getting through: gyro_main_lpf_hz −15%. A resonance below dynamic_gyro_notch_min_hz: lower the limit once a 2 kHz log confirms the frequency. Working ESC telemetry: enable the RPM filter. Little noise and cool motors: relax the filters slightly for less delay.
  4. P and D. On overshoot, check FF's share first. If it's small, the overshoot is P/D: lower P, and with ζ below 0.6 and cool motors add D in 10% steps.
  5. I, relax, anti-gravity, TPA. Bounce-back: mc_iterm_relax_cutoff 15 → 10, then I −10%. Nose dips on sharp throttle: antigravity_gain = 2. Overshoot or shakes only at high throttle: tpa_rate 0 → 20, or +15 if TPA is already on (in INAV it scales P, D and CD).
  6. Propwash. A motor sitting at idle during chops: throttle_idle +2 and airmode_type = THROTTLE_THRESHOLD, not more D. Airmode itself must be on: feature AIRMODE or the AIR MODE switch.
  7. FF and stick smoothing last, once the loop stops overshooting.

One change per flight; feel the motors after each one. Reading the step response is covered in PID tuning from blackbox.

When to change nothing. A log below 500 Hz: leave the filters alone. A flight in ANGLE or a navigation mode: leave the PIDs alone. Fewer than five clean chops: don't treat propwash; fewer than six sharp stops per axis: leave relax and I alone. A noticeable share of lost frames: fly again. With raw noise under 4 deg/s, motor noise under 2.5% and damping of 0.6–0.9, leave the tune alone: more filtering adds delay, more D heats the motors.

How INAV logs differ from Betaflight

Navigation only after the inner loop is clean

INAV's position and altitude controllers output a target angle and throttle, which the angle and rate loops you just tuned have to deliver. If roll overshoots by 30% and rings at 7 Hz, POSHOLD will sway, and tweaking nav_mc_pos_xy_p or nav_mc_vel_xy_p fixes the wrong loop. The order:

  1. A clean inner loop, judged from an ACRO log.
  2. Hover throttle: find the throttle (the rcCommand[3] field) at which the quad hovers without climbing or sinking and set nav_mc_hover_thr to it (same 1000–2000 units); the altitude controller starts from it.
  3. Then the altitude gains (nav_mc_pos_z_p, nav_mc_vel_z_*) and position gains (nav_mc_pos_xy_p, nav_mc_vel_xy_*), one per flight.

bbdoctor analyses the inner loop, not the navigation controllers.

Check it automatically

bbdoctor analyses INAV blackbox logs (.txt and .bbl) online for free: everything measured in the cases above, with commands in INAV CLI syntax scaled from your current values. Since the thresholds were tuned on Betaflight FPV quads, the report warns that for large drones they are approximate. See a sample report.

FAQ

How do I log the raw gyro in INAV?

In the CLI type blackbox GYRO_RAW, then save. Typing blackbox on its own lists the fields being logged; MOTORS must be on too. The rate is set by blackbox_rate_num and blackbox_rate_denom: vibration and filter work needs 500 Hz or more, which with looptime 500 means 1/2 (about 1 kHz). In practice 1/4 lands just under 500 Hz, and the vibration checks stay off.

Does INAV have autotune for multirotors?

INAV's built-in AUTOTUNE mode is for fixed-wing aircraft. Multirotors are tuned by hand from the log, or the simple way with EzTune (ez_enabled), which derives PIDs and filters from a handful of sliders. With EzTune on, manual PID edits get overwritten.

What is mc_cd_roll in INAV?

Control Derivative, INAV's equivalent of Betaflight feedforward. It pushes the craft into a move based on how fast the stick moves, ahead of P. The default is 60 on every axis. In the log header it is the fourth number of rollPID; in the log data it is the axisF field.

Why is my INAV log recorded at 60 Hz?

The logging rate is the loop rate times blackbox_rate_num / blackbox_rate_denom. With looptime 500 (2 kHz) and a 1/32 ratio you get about 62 Hz, so the log only shows 0 to 30 Hz. Set blackbox_rate_num = 1 and blackbox_rate_denom = 2 for 1 kHz.

Updated:

More guides