BBL-Based PID Tuning Protocol

A structured, blackbox-driven PID tuning protocol based on step response analysis. Derived from the methodology implemented in PIDtoolbox (Brian White, MATLAB) and adapted for iterative field use. The protocol requires a .bbl log from a dedicated test flight; do not attempt to tune from a freestyle or racing session log — the input signals are too irregular.


Overview

flowchart TD
    A([Baseline diff all<br/>backup]) --> B[Record step response<br/>test flight]
    B --> C[Export BBL to CSV<br/>Blackbox Explorer]
    C --> D[Run step response<br/>analysis]
    D --> E{Identify dominant<br/>failure mode}
    E -->|Overshoot| F[Reduce P or increase D]
    E -->|Ringing after step| G[Increase D or reduce P]
    E -->|Slow / sluggish| H[Increase P]
    E -->|Long-term drift| I[Increase I]
    E -->|Delayed initial attack| J[Increase FF]
    E -->|Motor noise / heat| K[Tighten filters first]
    F & G & H & I & J --> L[Apply one change<br/>one axis at a time]
    L --> B
    K --> B

Before touching PIDs: verify your filters are correctly configured. High P gains on a noisy, under-filtered quad produce oscillation that looks identical to a P-too-high symptom. Always analyze the motor traces and spectral noise floor before concluding a PID adjustment is needed.


Step 1 — Backup Current Config

1# In Betaflight CLI:
2diff all
3# Save output to a timestamped file before any change
4# e.g.: build-name_2026-07-13_pre-tune.txt

See CLI Backup and Restore for the full workflow.


Step 2 — Record a Proper Test Flight

A step response test flight is not normal flying. The goal is to generate clean, repeated rapid stick inputs on each axis independently.

Maneuver Protocol

AxisInputDescription
RollRapid left–right deflectionsFull deflection in one direction, return to center, opposite full deflection. ~0.5–1s per direction.
PitchRapid forward–back deflectionsSame pattern, in clean level flight.
YawRapid left–right rudderSlower than roll/pitch — yaw is under-actuated on most builds.

Do each axis separately in its own log segment, or at minimum pause between axes. Mixing simultaneous inputs defeats step response analysis.

Flight Conditions

  • Hover height 5–10 m in calm air (wind degrades the analysis)
  • Constant throttle during the inputs — throttle changes shift the hover point and contaminate the data
  • Arms enabled, Air Mode on
  • Blackbox at full rate (denom = 1) or half rate (denom = 2) — see Blackbox Logging

BBL Settings for Tuning Sessions

 1# Enable high-rate logging for tuning:
 2set blackbox_sample_rate = 1/2   # or 1/1 for max resolution
 3set blackbox_device = SPIFLASH   # or SDCARD if available
 4
 5# Fields to ensure are logged:
 6set blackbox_disable_pids = OFF
 7set blackbox_disable_setpoint = OFF
 8set blackbox_disable_gyroraw = OFF
 9set blackbox_disable_motor = OFF
10save

Step 3 — Export from Blackbox Explorer

  1. Open Betaflight Blackbox Explorer, load the .bbl file
  2. Identify the clean step response segments — look for the axis inputs visually
  3. Select the relevant segment (avoid arming transients, landing, or crashes)
  4. Export: File → Export CSV
  5. Note which log number corresponds to which axis / tune iteration

Step 4 — Step Response Analysis

The step response is computed via Wiener deconvolution of the gyro response against the setpoint. The output is a curve showing how the actual rotation rate tracks the commanded rotation rate over time.

flowchart LR
    SET["Setpoint<br/>(RC command)"] -->|Wiener deconvolution| SR[Step Response<br/>curve]
    GYR["Gyro<br/>(actual rate)"] --> SR
    SR --> SHAPE{Shape analysis}
    SHAPE --> OS[Overshoot peak]
    SHAPE --> RT[Rise time]
    SHAPE --> ST[Settling time]
    SHAPE --> SS[Steady-state error]

Target Step Response Shape

{ "type": "line", "data": { "labels": ["0","10","20","30","40","50","60","70","80","90","100","110","120","130","140","150"], "datasets": [ { "label": "Ideal / well tuned", "data": [0,0.35,0.72,0.95,1.05,1.04,1.02,1.01,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00], "borderColor": "rgba(34,197,94,1)", "backgroundColor": "transparent", "borderWidth": 2.5, "tension": 0.3, "pointRadius": 0 }, { "label": "P too high (overshoot + ringing)", "data": [0,0.42,0.90,1.25,1.20,1.10,1.18,1.08,1.13,1.05,1.08,1.02,1.04,1.01,1.02,1.00], "borderColor": "rgba(239,68,68,1)", "backgroundColor": "transparent", "borderWidth": 2, "tension": 0.3, "borderDash": [5,3], "pointRadius": 0 }, { "label": "P too low (slow, undershoots)", "data": [0,0.18,0.40,0.60,0.76,0.86,0.93,0.97,0.99,1.00,1.00,1.00,1.00,1.00,1.00,1.00], "borderColor": "rgba(249,115,22,1)", "backgroundColor": "transparent", "borderWidth": 2, "tension": 0.3, "borderDash": [4,2], "pointRadius": 0 }, { "label": "D too low (overshoot, slow settle)", "data": [0,0.38,0.78,1.10,1.18,1.14,1.08,1.05,1.03,1.02,1.01,1.00,1.00,1.00,1.00,1.00], "borderColor": "rgba(168,85,247,1)", "backgroundColor": "transparent", "borderWidth": 2, "tension": 0.3, "borderDash": [6,2], "pointRadius": 0 }, { "label": "I too low (steady-state error)", "data": [0,0.35,0.72,0.96,1.05,1.03,1.01,1.00,0.97,0.95,0.93,0.91,0.90,0.90,0.90,0.90], "borderColor": "rgba(234,179,8,1)", "backgroundColor": "transparent", "borderWidth": 2, "tension": 0.3, "borderDash": [3,2], "pointRadius": 0 } ] }, "options": { "responsive": true, "interaction": { "mode": "index", "intersect": false }, "plugins": { "title": { "display": true, "text": "Step Response Shapes — Diagnosis Guide (normalized, t in ms)" }, "legend": { "position": "bottom" } }, "scales": { "x": { "title": { "display": true, "text": "Time (ms)" } }, "y": { "min": 0, "max": 1.35, "title": { "display": true, "text": "Normalized rate response" } } } } }

Reading the Step Response

ShapeDiagnosisPrimary fix
Overshoot >10%, oscillates backP too highReduce P by 10–15%
Overshoot present, settles cleanly but slowlyD too lowIncrease D by 10%
Slow rise, no overshoot, sluggishP too lowIncrease P by 10–15%
Rises fast, massive overshoot, sustained ringingP too high AND D too lowReduce P first, then evaluate D
Good shape in analysis but ringing in freestyleNoise / filter issueCheck motor noise spectrum first
Steady-state error: response settles at 0.92 not 1.0I too lowIncrease I by 10–20%
Delayed start of rise (lag before response begins)FF too lowIncrease FF by 10%

Step 5 — Spectral Analysis (Noise Check)

Before any PID adjustment, run spectral analysis on gyro and motor traces. Look for:

  • Prop wash frequency: typically 100–300 Hz depending on frame/prop size. Broad hump in the gyro spectrum.
  • Motor noise: sharp peaks above 300 Hz. RPM filter should place notches here.
  • ESC switching noise: above 1 kHz. Low-pass filter should handle this.
  • Mechanical resonance: sharp narrow peak at a fixed frequency regardless of throttle. Check motor screws, prop balance.
flowchart LR
    SPEC[Spectral plot<br/>of gyro] --> A{Sharp peak<br/>fixed frequency?}
    A -->|Yes| B[Mechanical — tighten screws<br/>balance props<br/>check motor]
    A -->|No| C{Peak tracks<br/>throttle?}
    C -->|Yes| D[Motor noise — verify<br/>RPM filter is active]
    C -->|No — broad hump| E[Prop wash —<br/>D term / filtering issue]

Only proceed to PID adjustment once the noise floor is clean or understood.


Step 6 — Tuning Order

Follow this order. Do not skip to I or FF before P and D are stable.

flowchart TD
    P1[Set P: find the edge of oscillation<br/>then reduce 15–20%] --> D1
    D1[Set D: increase until overshoot<br/>is damped without adding noise] --> I1
    I1[Set I: increase until<br/>steady-state error disappears<br/>watch for low-frequency wobble] --> FF1
    FF1[Set FF: tune for<br/>initial stick response feel<br/>check for FF spikes on fast inputs] --> VERIFY
    VERIFY[Re-fly step response test<br/>Confirm all axes stable<br/>Check motor temps] --> DONE([Commit config<br/>diff all → save])

Axis order: Roll first (most sensitive), then Pitch, then Yaw. Roll and Pitch can often share PID values on symmetrical frames — verify after setting both.


Step 7 — Motor Temperature Check

After any PID change, hover for 2–3 minutes and immediately check motor temperatures:

  • < 50°C — safe; PIDs are reasonable
  • 50–65°C — warm; D or filters may be too aggressive, generating excess motor activity
  • > 65°C — hot; reduce D or re-examine filter cutoffs before flying further

Motors should be checked in the same order (M1–M4) every time. If one motor runs significantly hotter than the others, that arm may have vibration or a soft motor mount — address mechanically before attributing to PIDs.


Reference

Related Snippets