Skip to content

Commit 3eede56

Browse files
Merge pull request #11464 from 19379353560/feature/dterm-prediff-lpf
flight/pid: add D-term pre-differentiation LPF (dterm_lpf2_hz)
2 parents abc31a8 + 4387dd7 commit 3eede56

4 files changed

Lines changed: 43 additions & 8 deletions

File tree

docs/Settings.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,16 @@ Sets the DShot beeper tone
925925

926926
---
927927

928+
### dterm_lpf2_hz
929+
930+
Dterm pre-differentiation LPF cutoff (Hz). Filters gyro before differentiation to reduce noise amplification. Higher = less delay, more noise. 0 = disabled. Values around 200-250Hz can add smoothing with small delay.
931+
932+
| Default | Min | Max |
933+
| --- | --- | --- |
934+
| 0 | 0 | 500 |
935+
936+
---
937+
928938
### dterm_lpf_hz
929939

930940
Dterm low pass filter cutoff frequency. Default setting is very conservative and small multirotors should use higher value between 80 and 100Hz. 80 seems like a gold spot for 7-inch builds while 100 should work best with 5-inch machines. If motors are getting too hot, lower the value

src/main/fc/settings.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2034,6 +2034,12 @@ groups:
20342034
default_value: 110
20352035
min: 0
20362036
max: 500
2037+
- name: dterm_lpf2_hz
2038+
description: "Dterm pre-differentiation LPF cutoff (Hz). Filters gyro before differentiation to reduce noise amplification. Higher = less delay, more noise. 0 = disabled. Values around 200-250Hz can add smoothing with small delay."
2039+
default_value: 0
2040+
field: dterm_lpf2_hz
2041+
min: 0
2042+
max: 500
20372043
- name: dterm_lpf_type
20382044
description: "Defines the type of stage 1 D-term LPF filter. Possible values: `PT1`, `BIQUAD`, `PT2`, `PT3`."
20392045
default_value: "PT2"

src/main/flight/pid.c

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ typedef struct {
9797
rateLimitFilter_t axisAccelFilter;
9898
pt1Filter_t ptermLpfState;
9999
filter_t dtermLpfState;
100+
pt1Filter_t dtermLpf2State; // Pre-differentiation LPF (BF-style: filter before diff to reduce noise amplification)
101+
float previousFilteredGyroRate; // Stores filtered gyro for pre-diff architecture
100102

101103
float stickPosition;
102104

@@ -161,6 +163,7 @@ static EXTENDED_FASTRAM float dBoostMaxAtAlleceleration;
161163
static EXTENDED_FASTRAM uint8_t yawLpfHz;
162164
static EXTENDED_FASTRAM float motorItermWindupPoint;
163165
static EXTENDED_FASTRAM float antiWindupScaler;
166+
static EXTENDED_FASTRAM uint16_t dtermLpf2Hz;
164167
#ifdef USE_ANTIGRAVITY
165168
static EXTENDED_FASTRAM float iTermAntigravityGain;
166169
#endif
@@ -264,6 +267,7 @@ PG_RESET_TEMPLATE(pidProfile_t, pidProfile,
264267

265268
.dterm_lpf_type = SETTING_DTERM_LPF_TYPE_DEFAULT,
266269
.dterm_lpf_hz = SETTING_DTERM_LPF_HZ_DEFAULT,
270+
.dterm_lpf2_hz = SETTING_DTERM_LPF2_HZ_DEFAULT,
267271
.yaw_lpf_hz = SETTING_YAW_LPF_HZ_DEFAULT,
268272

269273
.itermWindupPointPercent = SETTING_ITERM_WINDUP_DEFAULT,
@@ -333,6 +337,16 @@ bool pidInitFilters(void)
333337
initFilter(pidProfile()->dterm_lpf_type, &pidState[axis].dtermLpfState, pidProfile()->dterm_lpf_hz, refreshRate);
334338
}
335339

340+
dtermLpf2Hz = pidProfile()->dterm_lpf2_hz;
341+
if (dtermLpf2Hz > 0) {
342+
for (int axis = 0; axis < 3; axis++) {
343+
const float currentGyroRate = pidState[axis].gyroRate;
344+
pt1FilterInit(&pidState[axis].dtermLpf2State, dtermLpf2Hz, US2S(refreshRate));
345+
pt1FilterReset(&pidState[axis].dtermLpf2State, currentGyroRate);
346+
pidState[axis].previousFilteredGyroRate = currentGyroRate;
347+
}
348+
}
349+
336350
for (int i = 0; i < XYZ_AXIS_COUNT; i++) {
337351
pt1FilterInit(&windupLpf[i], pidProfile()->iterm_relax_cutoff, US2S(refreshRate));
338352
}
@@ -775,16 +789,20 @@ static float applyDBoost(pidState_t *pidState, float dT) {
775789
static float FAST_CODE dTermProcess(pidState_t *pidState, float currentRateTarget, float dT, float dT_inv) {
776790
// Calculate new D-term
777791
float newDTerm = 0;
778-
if (pidState->kD == 0) {
779-
// optimisation for when D is zero, often used by YAW axis
780-
newDTerm = 0;
781-
} else {
782-
float delta = pidState->previousRateGyro - pidState->gyroRate;
792+
if (pidState->kD != 0) {
793+
float delta;
794+
if (dtermLpf2Hz > 0) {
795+
// Filter gyro before differentiation so D-term does not amplify high-frequency noise.
796+
const float filteredGyro = pt1FilterApply(&pidState->dtermLpf2State, pidState->gyroRate);
797+
delta = pidState->previousFilteredGyroRate - filteredGyro;
798+
pidState->previousFilteredGyroRate = filteredGyro;
799+
} else {
800+
delta = pidState->previousRateGyro - pidState->gyroRate;
801+
}
783802

784803
delta = dTermLpfFilterApplyFn((filter_t *) &pidState->dtermLpfState, delta);
785804

786-
// Calculate derivative
787-
newDTerm = delta * (pidState->kD * dT_inv) * applyDBoost(pidState, currentRateTarget, dT, dT_inv);
805+
newDTerm = delta * (pidState->kD * dT_inv) * applyDBoost(pidState, currentRateTarget, dT, dT_inv);
788806
}
789807
return(newDTerm);
790808
}
@@ -1510,4 +1528,4 @@ uint16_t getPidSumLimit(const flight_dynamics_index_t axis) {
15101528
} else {
15111529
return 500;
15121530
}
1513-
}
1531+
}

src/main/flight/pid.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ typedef struct pidProfile_s {
103103

104104
uint8_t dterm_lpf_type; // Dterm LPF type: PT1, BIQUAD
105105
uint16_t dterm_lpf_hz;
106+
uint16_t dterm_lpf2_hz; // Dterm second stage LPF (pre-differentiation, like Betaflight)
106107

107108
uint8_t yaw_lpf_hz;
108109

0 commit comments

Comments
 (0)