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.

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
- Betaflight 4.1 or newer. 4.1 brought bidirectional DShot and the RPM filter.
- A DShot protocol (DSHOT150/300/600): over PWM, OneShot, Multishot or ProShot no RPM comes back.
- ESC firmware with bidirectional DShot (table below). Bluejay is flashed from esc-configurator.com.
- The right motor_poles: the magnet count in the bell. Betaflight divides electrical RPM (eRPM) by motor_poles/2 to get mechanical RPM.
| ESC | Bidirectional DShot | What to do |
|---|---|---|
| BLHeli_S (stock) | no | flash Bluejay (or JESC) |
| Bluejay, JESC | yes | nothing, enable it on the FC |
| BLHeli_32 older than 32.7 | no | update to 32.7+ |
| BLHeli_32 32.7+, AM32 | yes | nothing, enable it on the FC |
Step-by-step setup
- Props off. Back up your config with
diff all. - 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.
- Count the magnets in one motor bell.
- In the CLI (with your own protocol and pole count):
set motor_pwm_protocol = DSHOT300 set dshot_bidir = ON set motor_poles = 14 save
- 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_infoshows 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. - Check that the filter itself is on: it is by default (3 harmonics), but borrowed configs sometimes have 0.
- 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 newer | 4.1–4.2 | Default | What it does | Seen in logs |
|---|---|---|---|---|
rpm_filter_harmonics | gyro_rpm_notch_harmonics | 3 | how many harmonics to notch; 0 turns the filter off | 3 everywhere |
rpm_filter_q | gyro_rpm_notch_q | 500 (Q = 5) | notch width: higher is narrower, with less delay | 500; 700 (2025.12.5); 850 (VX3.5, 4.5.2) |
rpm_filter_min_hz | gyro_rpm_notch_min | 100 | lowest notch frequency | 100; 70 (7-inch, 4.6.0) |
rpm_filter_fade_range_hz | — | 50 | band above min_hz where the notch fades out | 50; 70; 80 |
rpm_filter_weights (4.5+) | — | 100,100,100 | notch depth per harmonic, % | 100,60,100 (4.6.0); 90,50,90 (2025.12.5) |
rpm_filter_lpf_hz | rpm_notch_lpf | 150 | smooths the RPM values before the notches are computed | 150 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.

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%):
- jumps: RPM changes by more than 35% from one frame to the next, above 30 Hz. a propped motor can't change speed that fast, so it's a reading error;
- zeros: RPM below 5 Hz on a spinning motor.
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.

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
| Symptom | Cause | Fix |
|---|---|---|
| With dshot_bidir on, motors won't spin or show 0 RPM | the 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 RPMFILTER | RPM filter on, but at least one ESC sends no RPM | find the motor without RPM on the Motors tab or with dshot_telemetry_info, check its ESC and wire |
| Errors on all motors at once | a common cause on the FC: the DShot output method, remapped motor resources | try dshot_bitbang = OFF instead of the default AUTO (or back), restore default resources |
| eRPM errors or jumps on one motor | signal wire joint, routing along the power leads, the ESC channel | resolder, reroute, update ESC firmware; if the errors follow the ESC when swapped, replace it |
| Motors tab RPM looks too high or too low | wrong motor_poles | count the magnets and enter that number |
| Bidirectional DShot is on but the log has no eRPM | 4.5+: RPM logging is off; 4.1–4.4: no eRPM field | blackbox_disable_rpm = OFF, or update to 4.5+ |
| Filter on, motors still hot | noise that isn't on the harmonics: frame resonance, bent prop, bearing; or too much D with relaxed D-term lowpasses | read 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/ProShot | switch the protocol to DShot |
| Config check: "The RPM filter is off even though RPM data is there" | rpm_filter_harmonics = 0 | set 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: