From fa8437fd015df31b6ba282f9dd6af5deda18a96e Mon Sep 17 00:00:00 2001 From: breadoven <56191411+breadoven@users.noreply.github.com> Date: Sat, 20 Jun 2026 10:25:48 +0100 Subject: [PATCH 1/3] FW throttle rate limiter --- docs/Settings.md | 10 ++++++++++ src/main/fc/fc_core.c | 4 ++-- src/main/fc/settings.yaml | 6 ++++++ src/main/flight/mixer.c | 21 ++++++++++++++++++++- src/main/flight/mixer.h | 2 +- src/main/sensors/battery.c | 1 + src/main/sensors/battery_config_structs.h | 3 ++- 7 files changed, 42 insertions(+), 5 deletions(-) diff --git a/docs/Settings.md b/docs/Settings.md index cbc526adb33..394a5a4fef3 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -1620,6 +1620,16 @@ Reference airspeed. Set this to airspeed at which PIDs were tuned. Usually shoul --- +### fw_throttle_rate_limiter + +Limits throttle output rate of change. Setting defines minimum time in milliseconds for throttle to change by 1000us (min to max throttle range). Set to 100 to disable. Fixed wing only. + +| Default | Min | Max | +| --- | --- | --- | +| 100 | 100 | 10000 | + +--- + ### fw_tpa_time_constant TPA smoothing and delay time constant to reflect non-instant speed/throttle response of the plane. See **PID Attenuation and scaling** Wiki for full details. diff --git a/src/main/fc/fc_core.c b/src/main/fc/fc_core.c index 3223aca497e..d25e04fce3d 100644 --- a/src/main/fc/fc_core.c +++ b/src/main/fc/fc_core.c @@ -925,7 +925,7 @@ void taskMainPidLoop(timeUs_t currentTimeUs) { cycleTime = getTaskDeltaTime(TASK_SELF); - dT = (float)cycleTime * 0.000001f; + dT = US2S(cycleTime); bool fwLaunchIsActive = STATE(AIRPLANE) && isNavLaunchEnabled() && armTime == 0; @@ -992,7 +992,7 @@ void taskMainPidLoop(timeUs_t currentTimeUs) // Calculate stabilisation pidController(dT); - mixTable(); + mixTable(dT); if (isMixerUsingServos()) { servoMixer(dT); diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 15a0c0fff4f..c1e9fd1b73b 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -1177,6 +1177,12 @@ groups: field: nav.fw.launch_idle_throttle min: 1000 max: 2000 + - name: fw_throttle_rate_limiter + description: "Limits throttle output rate of change. Setting defines minimum time in milliseconds for throttle to change by 1000us (min to max throttle range). Set to 100 to disable. Fixed wing only." + default_value: 100 + field: motor.throttle_rate_limiter + min: 100 + max: 10000 - name: limit_cont_current description: "Continous current limit (dA), set to 0 to disable" condition: USE_POWER_LIMITS diff --git a/src/main/flight/mixer.c b/src/main/flight/mixer.c index a80992b772d..9c85dcbc168 100644 --- a/src/main/flight/mixer.c +++ b/src/main/flight/mixer.c @@ -75,6 +75,7 @@ static EXTENDED_FASTRAM int throttleDeadbandHigh = 0; static EXTENDED_FASTRAM int throttleRangeMin = 0; static EXTENDED_FASTRAM int throttleRangeMax = 0; static EXTENDED_FASTRAM int8_t motorYawMultiplier = 1; +static EXTENDED_FASTRAM uint16_t throttleRateLimit = 0; int motorZeroCommand = 0; @@ -235,6 +236,10 @@ void mixerInit(void) } else { motorYawMultiplier = 1; } + + if (currentBatteryProfile->motor.throttle_rate_limiter > 100) { + throttleRateLimit = (PWM_RANGE_MAX - PWM_RANGE_MIN) / MS2S(currentBatteryProfile->motor.throttle_rate_limiter); + } } void mixerResetDisarmedMotors(void) @@ -486,8 +491,10 @@ static int getReversibleMotorsThrottleDeadband(void) return ifMotorstopFeatureEnabled() ? reversibleMotorsConfig()->neutral : directionValue; } -void FAST_CODE mixTable(void) +void FAST_CODE mixTable(float dT) { + static float lastMixerThrottleCommand = 1000.0f; + #ifdef USE_DSHOT if (FLIGHT_MODE(TURTLE_MODE)) { applyTurtleModeToMotors(); @@ -505,6 +512,7 @@ void FAST_CODE mixTable(void) motor[i] = isDisarmed ? motor_disarmed[i] : motorValueWhenStopped; } mixerThrottleCommand = motor[0]; + lastMixerThrottleCommand = mixerThrottleCommand; return; } @@ -607,6 +615,17 @@ void FAST_CODE mixTable(void) throttleMax = throttleRangeMax; throttleRange = throttleMax - throttleMin; + // FW throttle rate limiter + if (STATE(AIRPLANE) && throttleRateLimit) { + const float deltaThrottle = mixerThrottleCommand - lastMixerThrottleCommand; + const float throttleRate = deltaThrottle / dT; + + if (fabsf(throttleRate) > throttleRateLimit) { + lastMixerThrottleCommand = lastMixerThrottleCommand + SIGN(throttleRate) * throttleRateLimit * dT; + mixerThrottleCommand = lastMixerThrottleCommand; + } + } + #define THROTTLE_CLIPPING_FACTOR 0.33f motorMixRange = (float)rpyMixRange / (float)throttleRange; if (motorMixRange > 1.0f) { diff --git a/src/main/flight/mixer.h b/src/main/flight/mixer.h index 6c4370d4176..a18796d7b59 100644 --- a/src/main/flight/mixer.h +++ b/src/main/flight/mixer.h @@ -120,7 +120,7 @@ void writeAllMotors(int16_t mc); void mixerInit(void); void mixerUpdateStateFlags(void); void mixerResetDisarmedMotors(void); -void mixTable(void); +void mixTable(float dT); void writeMotors(void); void processServoAutotrim(const float dT); void processServoAutotrimMode(void); diff --git a/src/main/sensors/battery.c b/src/main/sensors/battery.c index da8a6237bd0..d95aaf479ec 100644 --- a/src/main/sensors/battery.c +++ b/src/main/sensors/battery.c @@ -139,6 +139,7 @@ void pgResetFn_batteryProfiles(batteryProfile_t *instance) #ifdef USE_DSHOT .turtleModePowerFactor = SETTING_TURTLE_MODE_POWER_FACTOR_DEFAULT, #endif + .throttle_rate_limiter = SETTING_FW_THROTTLE_RATE_LIMITER_DEFAULT, // 100 millis }, .failsafe_throttle = SETTING_FAILSAFE_THROTTLE_DEFAULT, // default throttle off. diff --git a/src/main/sensors/battery_config_structs.h b/src/main/sensors/battery_config_structs.h index 8fe49295f8e..cd453a71d28 100644 --- a/src/main/sensors/battery_config_structs.h +++ b/src/main/sensors/battery_config_structs.h @@ -112,9 +112,10 @@ typedef struct batteryProfile_s { #ifdef USE_DSHOT uint8_t turtleModePowerFactor; // Power factor from 0 to 100% of flip over after crash #endif + uint16_t throttle_rate_limiter; // Min time in millis for fixed wing throttle to go from min to max } motor; - uint16_t failsafe_throttle; // Throttle level used for landing - specify value between 1000..2000 (pwm pulse width for slightly below hover). center throttle = 1500. + uint16_t failsafe_throttle; // Throttle level used for landing - slightly below hover for MC, probably motor off for FW. struct { From 401a9c895b33f0d0acba90a5fa8e15ea2a6ff5e9 Mon Sep 17 00:00:00 2001 From: breadoven <56191411+breadoven@users.noreply.github.com> Date: Sun, 21 Jun 2026 09:48:27 +0100 Subject: [PATCH 2/3] change to float + pg bump --- docs/Settings.md | 4 ++-- src/main/fc/settings.yaml | 8 ++++---- src/main/flight/mixer.c | 4 ++-- src/main/sensors/battery.c | 4 ++-- src/main/sensors/battery_config_structs.h | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/Settings.md b/docs/Settings.md index 394a5a4fef3..b22806d6843 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -1622,11 +1622,11 @@ Reference airspeed. Set this to airspeed at which PIDs were tuned. Usually shoul ### fw_throttle_rate_limiter -Limits throttle output rate of change. Setting defines minimum time in milliseconds for throttle to change by 1000us (min to max throttle range). Set to 100 to disable. Fixed wing only. +Limits throttle output rate of change. Setting defines minimum time in milliseconds for throttle to change by 1000us (min to max throttle range). Set to 0 to disable. Fixed wing only. | Default | Min | Max | | --- | --- | --- | -| 100 | 100 | 10000 | +| 0 | 0 | 5000 | --- diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index c1e9fd1b73b..e075ff0dcc2 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -1178,11 +1178,11 @@ groups: min: 1000 max: 2000 - name: fw_throttle_rate_limiter - description: "Limits throttle output rate of change. Setting defines minimum time in milliseconds for throttle to change by 1000us (min to max throttle range). Set to 100 to disable. Fixed wing only." - default_value: 100 + description: "Limits throttle output rate of change. Setting defines minimum time in milliseconds for throttle to change by 1000us (min to max throttle range). Set to 0 to disable. Fixed wing only." + default_value: 0 field: motor.throttle_rate_limiter - min: 100 - max: 10000 + min: 0 + max: 5000 - name: limit_cont_current description: "Continous current limit (dA), set to 0 to disable" condition: USE_POWER_LIMITS diff --git a/src/main/flight/mixer.c b/src/main/flight/mixer.c index 9c85dcbc168..f0a8c4943eb 100644 --- a/src/main/flight/mixer.c +++ b/src/main/flight/mixer.c @@ -75,7 +75,7 @@ static EXTENDED_FASTRAM int throttleDeadbandHigh = 0; static EXTENDED_FASTRAM int throttleRangeMin = 0; static EXTENDED_FASTRAM int throttleRangeMax = 0; static EXTENDED_FASTRAM int8_t motorYawMultiplier = 1; -static EXTENDED_FASTRAM uint16_t throttleRateLimit = 0; +static EXTENDED_FASTRAM float throttleRateLimit = 0.0f; int motorZeroCommand = 0; @@ -237,7 +237,7 @@ void mixerInit(void) motorYawMultiplier = 1; } - if (currentBatteryProfile->motor.throttle_rate_limiter > 100) { + if (currentBatteryProfile->motor.throttle_rate_limiter > 0) { throttleRateLimit = (PWM_RANGE_MAX - PWM_RANGE_MIN) / MS2S(currentBatteryProfile->motor.throttle_rate_limiter); } } diff --git a/src/main/sensors/battery.c b/src/main/sensors/battery.c index d95aaf479ec..f35da2e57f3 100644 --- a/src/main/sensors/battery.c +++ b/src/main/sensors/battery.c @@ -108,7 +108,7 @@ static pt1Filter_t amperageFilterState; batteryState_e batteryState; const batteryProfile_t *currentBatteryProfile; -PG_REGISTER_ARRAY_WITH_RESET_FN(batteryProfile_t, MAX_BATTERY_PROFILE_COUNT, batteryProfiles, PG_BATTERY_PROFILES, 3); +PG_REGISTER_ARRAY_WITH_RESET_FN(batteryProfile_t, MAX_BATTERY_PROFILE_COUNT, batteryProfiles, PG_BATTERY_PROFILES, 4); void pgResetFn_batteryProfiles(batteryProfile_t *instance) { @@ -136,10 +136,10 @@ void pgResetFn_batteryProfiles(batteryProfile_t *instance) .motor = { .throttleIdle = SETTING_THROTTLE_IDLE_DEFAULT, .throttleScale = SETTING_THROTTLE_SCALE_DEFAULT, + .throttle_rate_limiter = SETTING_FW_THROTTLE_RATE_LIMITER_DEFAULT, // 100 millis #ifdef USE_DSHOT .turtleModePowerFactor = SETTING_TURTLE_MODE_POWER_FACTOR_DEFAULT, #endif - .throttle_rate_limiter = SETTING_FW_THROTTLE_RATE_LIMITER_DEFAULT, // 100 millis }, .failsafe_throttle = SETTING_FAILSAFE_THROTTLE_DEFAULT, // default throttle off. diff --git a/src/main/sensors/battery_config_structs.h b/src/main/sensors/battery_config_structs.h index cd453a71d28..b66c2f4329b 100644 --- a/src/main/sensors/battery_config_structs.h +++ b/src/main/sensors/battery_config_structs.h @@ -109,10 +109,10 @@ typedef struct batteryProfile_s { struct { float throttleIdle; // Throttle IDLE value based on min_command, max_throttle, in percent float throttleScale; // Scaling factor for throttle. + uint16_t throttle_rate_limiter; // Min time in millis for fixed wing throttle to go from min to max #ifdef USE_DSHOT uint8_t turtleModePowerFactor; // Power factor from 0 to 100% of flip over after crash #endif - uint16_t throttle_rate_limiter; // Min time in millis for fixed wing throttle to go from min to max } motor; uint16_t failsafe_throttle; // Throttle level used for landing - slightly below hover for MC, probably motor off for FW. From d1118913f4fb74ca557e1af4a9076ab2265977b9 Mon Sep 17 00:00:00 2001 From: breadoven <56191411+breadoven@users.noreply.github.com> Date: Thu, 2 Jul 2026 21:40:22 +0100 Subject: [PATCH 3/3] Change setting limit increase only --- docs/Settings.md | 4 ++-- src/main/fc/settings.yaml | 6 +++--- src/main/flight/mixer.c | 18 +++++++++++++----- src/main/sensors/battery.c | 2 +- src/main/sensors/battery_config_structs.h | 2 +- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/docs/Settings.md b/docs/Settings.md index b22806d6843..98e7d719dda 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -1622,11 +1622,11 @@ Reference airspeed. Set this to airspeed at which PIDs were tuned. Usually shoul ### fw_throttle_rate_limiter -Limits throttle output rate of change. Setting defines minimum time in milliseconds for throttle to change by 1000us (min to max throttle range). Set to 0 to disable. Fixed wing only. +Minimum time allowed for throttle to increase from minimum to maximum throttle (1000 to 2000) in milliseconds. Negative values limit decreasing as well as increasing throttle. Positive values only limit increasing throttle. Set to 0 to disable. Fixed wing only. | Default | Min | Max | | --- | --- | --- | -| 0 | 0 | 5000 | +| 0 | -5000 | 5000 | --- diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index e075ff0dcc2..5df076f4f1c 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -1178,10 +1178,10 @@ groups: min: 1000 max: 2000 - name: fw_throttle_rate_limiter - description: "Limits throttle output rate of change. Setting defines minimum time in milliseconds for throttle to change by 1000us (min to max throttle range). Set to 0 to disable. Fixed wing only." + description: "Minimum time allowed for throttle to increase from minimum to maximum throttle (1000 to 2000) in milliseconds. Negative values limit decreasing as well as increasing throttle. Positive values only limit increasing throttle. Set to 0 to disable. Fixed wing only." default_value: 0 - field: motor.throttle_rate_limiter - min: 0 + field: motor.throttleRateLimiter + min: -5000 max: 5000 - name: limit_cont_current description: "Continous current limit (dA), set to 0 to disable" diff --git a/src/main/flight/mixer.c b/src/main/flight/mixer.c index f0a8c4943eb..c6648202ed3 100644 --- a/src/main/flight/mixer.c +++ b/src/main/flight/mixer.c @@ -237,8 +237,8 @@ void mixerInit(void) motorYawMultiplier = 1; } - if (currentBatteryProfile->motor.throttle_rate_limiter > 0) { - throttleRateLimit = (PWM_RANGE_MAX - PWM_RANGE_MIN) / MS2S(currentBatteryProfile->motor.throttle_rate_limiter); + if (currentBatteryProfile->motor.throttleRateLimiter) { + throttleRateLimit = (PWM_RANGE_MAX - PWM_RANGE_MIN) / MS2S(currentBatteryProfile->motor.throttleRateLimiter); } } @@ -494,7 +494,6 @@ static int getReversibleMotorsThrottleDeadband(void) void FAST_CODE mixTable(float dT) { static float lastMixerThrottleCommand = 1000.0f; - #ifdef USE_DSHOT if (FLIGHT_MODE(TURTLE_MODE)) { applyTurtleModeToMotors(); @@ -619,10 +618,19 @@ void FAST_CODE mixTable(float dT) if (STATE(AIRPLANE) && throttleRateLimit) { const float deltaThrottle = mixerThrottleCommand - lastMixerThrottleCommand; const float throttleRate = deltaThrottle / dT; + bool limitOutput = false; + + if (throttleRateLimit < 0.0f) { + limitOutput = fabsf(throttleRate) > -throttleRateLimit; + } else if (throttleRate > throttleRateLimit) { + limitOutput = true; + } - if (fabsf(throttleRate) > throttleRateLimit) { - lastMixerThrottleCommand = lastMixerThrottleCommand + SIGN(throttleRate) * throttleRateLimit * dT; + if (limitOutput) { + lastMixerThrottleCommand += SIGN(throttleRate) * fabsf(throttleRateLimit) * dT; mixerThrottleCommand = lastMixerThrottleCommand; + } else { + lastMixerThrottleCommand = mixerThrottleCommand; } } diff --git a/src/main/sensors/battery.c b/src/main/sensors/battery.c index f35da2e57f3..8ca7b268afc 100644 --- a/src/main/sensors/battery.c +++ b/src/main/sensors/battery.c @@ -136,7 +136,7 @@ void pgResetFn_batteryProfiles(batteryProfile_t *instance) .motor = { .throttleIdle = SETTING_THROTTLE_IDLE_DEFAULT, .throttleScale = SETTING_THROTTLE_SCALE_DEFAULT, - .throttle_rate_limiter = SETTING_FW_THROTTLE_RATE_LIMITER_DEFAULT, // 100 millis + .throttleRateLimiter = SETTING_FW_THROTTLE_RATE_LIMITER_DEFAULT, // 100 millis #ifdef USE_DSHOT .turtleModePowerFactor = SETTING_TURTLE_MODE_POWER_FACTOR_DEFAULT, #endif diff --git a/src/main/sensors/battery_config_structs.h b/src/main/sensors/battery_config_structs.h index b66c2f4329b..4b634da0a89 100644 --- a/src/main/sensors/battery_config_structs.h +++ b/src/main/sensors/battery_config_structs.h @@ -109,7 +109,7 @@ typedef struct batteryProfile_s { struct { float throttleIdle; // Throttle IDLE value based on min_command, max_throttle, in percent float throttleScale; // Scaling factor for throttle. - uint16_t throttle_rate_limiter; // Min time in millis for fixed wing throttle to go from min to max + int16_t throttleRateLimiter; // Min time in millis for fixed wing throttle to go from min to max #ifdef USE_DSHOT uint8_t turtleModePowerFactor; // Power factor from 0 to 100% of flip over after crash #endif