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

Betaflight RPM Filter: Set It Up and Prove It Works in the Log

The RPM filter is the most precise filter in Betaflight: the flight controller gets every motor's speed and cuts noise exactly at those frequencies. Below is what it needs, how to enable it, what the settings do and how to prove from a real log that it works.

What the RPM filter does, and why it beats a lowpass

A motor and prop shake the frame at the rotation frequency (1×) and its multiples; blade-pass is 2× on a two-blade prop and 3× on a tri-blade. All of them rise with throttle, so a throttle-vs-frequency heatmap shows them as diagonal lines.

Without RPM data, Betaflight has two tools. A lowpass cuts everything above its cutoff but costs a lot of delay and can't push a 200 Hz line down far. The dynamic notch hunts for spectrum peaks, trails throttle changes and has only 3 notches by default, 5 at most. The RPM filter reads every motor's speed from bidirectional DShot on each loop and puts a narrow notch on each harmonic of each motor. That's 4 motors × 3 harmonics = 12 notches that follow RPM with almost no lag.

Raw roll gyro heatmap of a 7-inch quad: diagonal noise bands with each motor's 1×, 2× and 3× lines from eRPM drawn on top; the bands sit on the lines
REKON 7, 6S, Betaflight 4.6.0, 1980 Hz log: raw roll gyro with each motor's median 1× (solid), 2× (dashed) and 3× (dotted) from eRPM per throttle band, colours M1–M4. 84–87% of the raw noise energy above 80 Hz falls within ±10 Hz of these lines, exactly where the RPM filter puts its notches.

It isn't free, though: a notch with quality factor Q at frequency f delays slow quad motion by roughly 1/(2π·Q·f). For a 4 kHz PID loop, 12 notches at Q = 5 with the motors at 200 Hz (1×) add about 1.15 ms. A single PT1 at 150 Hz adds nearly the same (1.06 ms). But that PT1 only knocks a 200 Hz line down by 4–5 dB, while the notches take it down by tens of dB. So for the same delay you remove nearly all motor noise and can run softer lowpasses. The low notches are the expensive ones: at 100 Hz the same 12 cost 2.3 ms, which is what min_hz and the fade range are for.

What you need

ESCBidirectional DShotWhat to do
BLHeli_S (stock)noflash Bluejay (or JESC)
Bluejay, JESCyesnothing, enable it on the FC
BLHeli_32 older than 32.7noupdate to 32.7+
BLHeli_32 32.7+, AM32yesnothing, enable it on the FC

Step-by-step setup

  1. Props off. Back up your config with diff all.
  2. Check the ESC firmware: esc-configurator.com reads BLHeli_S, Bluejay and AM32, BLHeliSuite32 reads BLHeli_32. If it's stock BLHeli_S, flash Bluejay.
  3. Count the magnets in one motor bell.
  4. In the CLI (with your own protocol and pole count):
    set motor_pwm_protocol = DSHOT300
    set dshot_bidir = ON
    set motor_poles = 14
    save
  5. After the reboot, open the Motors tab. Each motor should show its RPM and an error percentage. Spin them up with the slider: RPM should rise smoothly and errors stay near zero. In the CLI, dshot_telemetry_info shows RPM and bad packets per motor. With the RPM filter on, if even one ESC sends no RPM, Betaflight refuses to arm and shows RPMFILTER in the OSD.
  6. Check that the filter itself is on: it is by default (3 harmonics), but borrowed configs sometimes have 0.
  7. On 4.5 and newer, make sure blackbox RPM logging isn't disabled (it's on by default), or the log can't prove anything:
    set blackbox_disable_rpm = OFF
    save

The eRPM log field only exists from 4.5 on (the 4.4 blackbox code lacks it): our 4.4.1 log with bidirectional DShot has no RPM, while every 4.5.x log has eRPM[0]…eRPM[3]. And a 2025.12.5 log had the filter on but RPM logging off, so it can't prove anything.

The settings, with values from real logs

Betaflight 4.3 renamed the parameters and 4.5 added per-harmonic weights; the table shows defaults and values from real logs.

4.3 and newer4.1–4.2DefaultWhat it doesSeen in logs
rpm_filter_harmonicsgyro_rpm_notch_harmonics3how many harmonics to notch; 0 turns the filter off3 everywhere
rpm_filter_qgyro_rpm_notch_q500 (Q = 5)notch width: higher is narrower, with less delay500; 700 (2025.12.5); 850 (VX3.5, 4.5.2)
rpm_filter_min_hzgyro_rpm_notch_min100lowest notch frequency100; 70 (7-inch, 4.6.0)
rpm_filter_fade_range_hz—50band above min_hz where the notch fades out50; 70; 80
rpm_filter_weights (4.5+)—100,100,100notch depth per harmonic, %100,60,100 (4.6.0); 90,50,90 (2025.12.5)
rpm_filter_lpf_hzrpm_notch_lpf150smooths the RPM values before the notches are computed150 everywhere

rpm_filter_q. A narrow notch costs less delay but needs accurate RPM. For a 200 Hz notch, a frequency 10 Hz off-centre is cut by almost 7 dB at Q = 5 but only 3.6 dB at Q = 8.5. In exchange, 12 notches at Q = 8.5 add about 0.7 ms instead of 1.15. The VX3.5 runs 850 on clean telemetry (under 0.02 jumps per second), but the Betaflight wiki advises against going above 750, and raising q on a quad with eRPM errors is a bad idea.

rpm_filter_min_hz and the fade range. A notch never goes below min_hz, and from min_hz + fade down to min_hz its depth ramps to zero. That mostly affects the 1× of slow motors on throttle chops and in propwash, and at very low RPM the 2× too. On the 4.6.0 7-inch (min 70, fade 50) the slowest motor was below 120 Hz, inside the fade zone, for a quarter of the flight (24%) and below 70 Hz for 4.4%. Below 5% throttle its median motor speed was 58 Hz. Five-inch quads spin faster and suit the defaults. Lower min_hz only on big props, and only if the log shows 1× noise at low throttle: low notches are expensive in delay.

rpm_filter_weights (4.5 and newer) sets how deep each harmonic's notch goes: 100 is a full notch, 50 is half. The same 7-inch runs 100,60,100, and the log backs that up: 2× carried only 4–6% of the raw noise above 80 Hz, while 3× carried 68% on roll. Change weights only from your own log: a different blade count moves energy between 2× and 3×.

The 4.3+ defaults, to put everything back:

set rpm_filter_harmonics = 3
set rpm_filter_q = 500
set rpm_filter_min_hz = 100
set rpm_filter_fade_range_hz = 50
set rpm_filter_lpf_hz = 150
set rpm_filter_weights = 100,100,100
save

Skip the rpm_filter_weights line on 4.3–4.4, where it doesn't exist.

What to change once it's running

The dynamic notch: turn it down, not off. The motor lines now belong to the RPM filter; the dynamic notch is left with frame and stack resonances, which stay at one frequency at any throttle, and one or two notches cover them. For 4.2 and newer the Betaflight wiki suggests dyn_notch_count = 1 and dyn_notch_q = 500; nearly all our 4.5.x logs run 1, one runs 2. With the RPM filter on, the config check won't flag it being off, but frame resonance then goes straight into the PIDs.

Dynamic Idle becomes available. It also needs RPM: without bidirectional DShot dyn_idle_min_rpm does nothing, and bbdoctor's config check says so. The value is in hundreds of RPM; bbdoctor suggests 30 for 5", 40–50 for 3" and 20–25 for 7". It keeps the motors spinning on a chop and is one of the first fixes for propwash. On 4.2 it's called idle_min_rpm. It lives in the PID profile, so set it in every profile you fly.

set dyn_notch_count = 1
set dyn_notch_q = 500
set dyn_idle_min_rpm = 30
save

The lowpass filters: no rush. A common tip is to raise the lowpass right away. bbdoctor suggests raising the gyro cutoffs by 15% (then checking motor temperature) only when all of this holds: filtered noise under 1 deg/s, filter delay over 1.5 ms, a log at 500 Hz or more, a quiet raw gyro (under 4 deg/s) and no frame resonance. The 7-inch shows why: 0.66 deg/s after filtering and 2.1 ms of delay looks like headroom. But raw pitch noise is 7.9 deg/s, so the verdict is "there's headroom, but it's too early to relax them": mechanics first. The noise numbers are explained in the filters guide.

Prove it in the log

You need a log with the eRPM field and the unfiltered gyro at 1 kHz or more, ideally 2 kHz (see the blackbox setup guide). Upload it to bbdoctor and check three things.

1. The motor lines are gone after filtering. The raw heatmap shows the diagonals; the filtered one shouldn't. If they survive, the notches are misplaced: check motor_poles and eRPM errors.

Two roll noise heatmaps: on the left the raw gyro with bright diagonal lines, on the right the filtered gyro, almost dark on the same colour scale
Same REKON 7, roll, one colour scale. Left: raw gyro. Right: after the RPM filter, three dynamic notches and a 375 Hz lowpass. Noise above 80 Hz fell from 5.3 to 0.54 deg/s on roll and from 7.9 to 0.66 on pitch: 25.5 dB less on the motor lines, 13–14 dB less off them. The faint patch near 680 Hz at 15–30% throttle is off the tracked harmonics and above dyn_notch_max_hz = 400, so neither filter catches it.

2. The motors agree with each other. bbdoctor compares each motor's RPM with the others at the same command. On this 7-inch the spread is −0.7% to +0.8%. A 6% deviation gets a warning, and a motor 10% or more slower than the rest is flagged as serious. Hunting a motor that drags or shakes: see the vibration guide.

3. eRPM telemetry errors. The RPM filter trusts every packet, so a bad one briefly moves the notch off target. bbdoctor counts, per motor and only while it's spinning (command above 12%):

More than 1 jump per second or more than 1% zeros gives an "eRPM telemetry errors" warning, with advice to check the ESC signal wire joint, interference from the power leads and the ESC firmware. For comparison, four VX3.5 logs show 0 to 0.02 jumps per second.

RPM of four motors from eRPM: M1 and M4 show frequent sharp spikes while M2 and M3 are smooth; zoomed to 100 ms, M4 jumps between 103 and 171 Hz while M2 stays within 121–150 Hz
6S quad, Betaflight 4.5.1, 801 Hz log, flight 10. A: all four motors over 1 s; M1 and M4 are noisy. B: 100 ms, dots are log frames: M2 stays within 121–150 Hz, M4 jumps between 103 and 171 Hz. Median frame-to-frame change: M3 0.6%, M2 3.4%, M1 9.8%, M4 10.3%. Jumps per second: M4 6.1, M1 1.1, M3 0.24, M2 0.

On this quad M4 crosses the threshold in all 11 flights in the file, at 1.6 to 6.1 jumps per second. M1 crosses it in three flights, M3 in two, M2 never (its worst is 0.61). There are no zeros anywhere. Errors that stay on one motor flight after flight aren't random interference; they point at that channel: signal wire joint, routing next to the power leads, the ESC itself. On the longest flight (98 s) bbdoctor warned about two motors: M3 at 1.35 and M4 at 1.61 jumps per second.

RPM in the log also helps after a crash: bbdoctor looks for a desync before impact, a motor commanded at 40% or more spinning below 35% of the others for over 30 ms. If that motor showed eRPM zeros earlier in the flight, it counts as a telemetry fault, not a desync.

Troubleshooting: symptom, cause, fix

SymptomCauseFix
With dshot_bidir on, motors won't spin or show 0 RPMthe ESC can't do bidirectional DShot (stock BLHeli_S, BLHeli_32 older than 32.7)flash Bluejay or update BLHeli_32; AM32 supports it out of the box
Won't arm, OSD shows RPMFILTERRPM filter on, but at least one ESC sends no RPMfind the motor without RPM on the Motors tab or with dshot_telemetry_info, check its ESC and wire
Errors on all motors at oncea common cause on the FC: the DShot output method, remapped motor resourcestry dshot_bitbang = OFF instead of the default AUTO (or back), restore default resources
eRPM errors or jumps on one motorsignal wire joint, routing along the power leads, the ESC channelresolder, reroute, update ESC firmware; if the errors follow the ESC when swapped, replace it
Motors tab RPM looks too high or too lowwrong motor_polescount the magnets and enter that number
Bidirectional DShot is on but the log has no eRPM4.5+: RPM logging is off; 4.1–4.4: no eRPM fieldblackbox_disable_rpm = OFF, or update to 4.5+
Filter on, motors still hotnoise that isn't on the harmonics: frame resonance, bent prop, bearing; or too much D with relaxed D-term lowpassesread the heatmap: a vertical line is a resonance, a strong 1× on one motor is mechanical; then D and its filters
Config check: "Bidirectional DShot is on, but the protocol isn't DShot"dshot_bidir = ON with PWM/OneShot/ProShotswitch the protocol to DShot
Config check: "The RPM filter is off even though RPM data is there"rpm_filter_harmonics = 0set it back to 3

On pole count: a 12-magnet motor configured as 14 reads 6/7 of its real RPM, so at a real 200 Hz the notch sits at 171 Hz. A Q = 5 notch cuts a frequency 29 Hz away by only 1.5 dB, so the filter barely works. At 3× the miss is worse: 514 Hz instead of 600. The config check asks you to recount the poles when motor_kv is 6000 or more and motor_poles = 14. That only fires if you entered your KV: every log of ours that records motor_kv has the 1960 default. bbdoctor also converts eRPM with the header's motor_poles, so wrong poles shift its harmonic lines too.

Common mistakes: the 12 from 12N14P in motor_poles (that's stator teeth), a raised lowpass without a log, the dynamic notch switched off, a higher rpm_filter_q despite eRPM errors. And a desync on a throttle chop is an ESC and motor problem, not the RPM filter.

INAV, ArduPilot and PX4

INAV doesn't use bidirectional DShot here. It takes RPM from ESC telemetry over a separate wire to the RX pin of a free UART, with ESC telemetry enabled on that port in the Ports tab. BLHeli_32 and AM32 ESCs with a telemetry pad provide it; Bluejay doesn't. Poles come from motor_poles. The filter settings are rpm_gyro_harmonics (default 1, max 3), rpm_gyro_min_hz (100; the INAV docs allow 60–70 on a 7") and rpm_gyro_q (500). Turn it on like this:

set rpm_gyro_filter_enabled = ON
save

If the filter is on but no port has ESC telemetry, bbdoctor's config check says the filter isn't working. INAV's blackbox only records a single escRPM value in its slow frames, with no per-motor RPM, so the eRPM checks aren't available for INAV logs.

ArduPilot uses its harmonic notch driven by ESC telemetry. SERVO_BLH_BDMASK is a bitmask of outputs running bidirectional DShot (15 covers the first four), SERVO_BLH_POLES the pole count. INS_HNTCH_MODE = 3 makes the notch follow ESC RPM, and INS_HNTCH_OPTS = 2 gives each motor its own notch. On IOMCU boards (Pixhawk, Cube) bidirectional DShot only works on AUX outputs, so the AUX1–4 mask is 3840, and some boards need a firmware build with -bdshot in its name. Set INS_HNTCH_FREQ below the hover frequency; INS_HNTCH_BW usually starts at half of FREQ, but with per-motor notches the docs suggest a quarter. Our ArduPilot logs with this setup ran MOT_PWM_TYPE 6 (DShot600), INS_HNTCH_FREQ 50, INS_HNTCH_BW 25 and INS_HNTCH_HMNCS 1 (1× only).

MOT_PWM_TYPE = 6
SERVO_BLH_BDMASK = 15
SERVO_BLH_POLES = 14
INS_HNTCH_ENABLE = 1
INS_HNTCH_MODE = 3
INS_HNTCH_OPTS = 2
INS_HNTCH_REF = 1

PX4 uses the dynamic notch IMU_GYRO_DNF_EN: bit 0 follows ESC RPM, bit 1 follows the FFT (which also needs IMU_GYRO_FFT_EN), 3 enables both. IMU_GYRO_DNF_HMC sets the harmonic count. RPM comes from bidirectional DShot, wired DShot telemetry (DSHOT_TEL_CFG) or DroneCAN ESCs. In our 1.16–1.17 logs, DSHOT_BIDIR_EN = 1 enables bidirectional DShot and MOT_POLE_COUNT sets poles; our 1.15 logs don't have DSHOT_BIDIR_EN yet. The current PX4 docs select BDShot as the protocol in the actuator setup and set poles per motor (DSHOT_MOT_POL1…12), so check your version. One 1.16.2 log had IMU_GYRO_DNF_EN = 3 with DSHOT_BIDIR_EN = 0, DSHOT_TEL_CFG = 0 and DroneCAN for sensors only: the RPM part had nothing to follow, and the log has no IMU_GYRO_FFT_* parameters at all.

DSHOT_BIDIR_EN = 1
MOT_POLE_COUNT = 14
IMU_GYRO_DNF_EN = 1
IMU_GYRO_DNF_HMC = 3

More in the ArduPilot log guide and the PX4 ULog guide.

Summary

Enable bidirectional DShot, check the magnet count and Motors-tab errors, and leave the filter at its defaults. Then fly with eRPM logging and upload the log to bbdoctor to see whether the motor lines are gone and eRPM is clean. The config check works without a flight, and the example report shows a finished analysis.

FAQ

Do I need bidirectional DShot if I already have the dynamic notch?

Yes, if your ESCs support it. The dynamic notch finds peaks in the gyro spectrum with some lag and places a few notches for the whole quad (3 by default). The RPM filter knows every motor's speed on every loop and puts a notch on each harmonic of each motor. On the 7-inch log in this guide, 84–87% of the raw gyro noise above 80 Hz sat right on the motor harmonics. Keep the dynamic notch with the RPM filter, but drop it to 1–2 notches to handle frame resonances.

How many harmonics should rpm_filter_harmonics be?

Leave it at 3, the default. Most of the noise sits at 1× and at the blade-pass frequency: 2× on two-blade props, 3× on tri-blades. On the 7-inch in this guide, 3× carried 68% of the raw roll noise. If your log shows one harmonic is empty, on Betaflight 4.5 and newer turn it down with rpm_filter_weights instead of cutting the harmonic count.

Is motor_poles 12 or 14?

It's the number of magnets in the motor bell. In a 12N14P rating it's the second number: 12 stator teeth, 14 magnets. Most 22xx–28xx motors have 14 magnets, while small 08xx–11xx motors often have 12. Get it wrong and Betaflight reads RPM 14–17% off, so every notch lands beside the noise.

DShot300 or DShot600 for bidirectional DShot?

It depends on the PID loop rate. For 4 kHz and 3.2 kHz loops the Betaflight wiki recommends DShot300 as the most reliable choice; DShot600 works too but gains nothing. At 8 kHz you need DShot600, because DShot300 only updates the motors every second loop. Most bidirectional DShot logs in our set at 4 and 3.2 kHz ran DShot300, and the only 8 kHz one ran DShot600.

Updated:

More guides