From d691333e82ff74358450dc333e3cb577bf8e2461 Mon Sep 17 00:00:00 2001 From: shota Date: Fri, 26 Sep 2025 14:04:45 +0900 Subject: [PATCH 01/11] Add airspeed TPA support --- docs/Settings.md | 10 +++++ src/main/fc/controlrate_profile.c | 3 +- .../fc/controlrate_profile_config_struct.h | 1 + src/main/fc/settings.yaml | 5 +++ src/main/flight/pid.c | 37 ++++++++++--------- src/main/sensors/pitotmeter.c | 9 +++++ src/main/sensors/pitotmeter.h | 1 + 7 files changed, 48 insertions(+), 18 deletions(-) diff --git a/docs/Settings.md b/docs/Settings.md index 256dd05a005..afab765a87d 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -302,6 +302,16 @@ ADC channel to use for analog pitot tube (airspeed) sensor. If board doesn't hav --- +### airspeed_tpa + +Use airspeed instead of throttle position for TPA if airspeed is available. use throttle as {tpa_breakpoint + (airspeed - fw_reference_airspeed)/fw_reference_airspeed * (tpa_breakpoint - ThrottleIdleValue(default:1150))} for TPA calculation + +| Default | Min | Max | +| --- | --- | --- | +| OFF | OFF | ON | + +--- + ### align_board_pitch Arbitrary board rotation in deci-degrees (0.1 degree), to allow mounting it sideways / upside down / rotated etc diff --git a/src/main/fc/controlrate_profile.c b/src/main/fc/controlrate_profile.c index c8b144e136c..42fd3129ce1 100644 --- a/src/main/fc/controlrate_profile.c +++ b/src/main/fc/controlrate_profile.c @@ -46,7 +46,8 @@ void pgResetFn_controlRateProfiles(controlRateConfig_t *instance) .dynPID = SETTING_TPA_RATE_DEFAULT, .dynPID_on_YAW = SETTING_TPA_ON_YAW_DEFAULT, .pa_breakpoint = SETTING_TPA_BREAKPOINT_DEFAULT, - .fixedWingTauMs = SETTING_FW_TPA_TIME_CONSTANT_DEFAULT + .fixedWingTauMs = SETTING_FW_TPA_TIME_CONSTANT_DEFAULT, + .airspeed_tpa = SETTING_AIRSPEED_TPA_DEFAULT, }, .stabilized = { diff --git a/src/main/fc/controlrate_profile_config_struct.h b/src/main/fc/controlrate_profile_config_struct.h index e4038537603..acd4e40926f 100644 --- a/src/main/fc/controlrate_profile_config_struct.h +++ b/src/main/fc/controlrate_profile_config_struct.h @@ -32,6 +32,7 @@ typedef struct controlRateConfig_s { bool dynPID_on_YAW; uint16_t pa_breakpoint; // Breakpoint where TPA is activated uint16_t fixedWingTauMs; // Time constant of airplane TPA PT1-filter + bool airspeed_tpa; // Use airspeed instead of throttle position for TPA calculation } throttle; struct { diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 5fe29261630..a5593efaf03 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -1343,6 +1343,11 @@ groups: field: throttle.dynPID min: 0 max: 100 + - name: airspeed_tpa + description: "Use airspeed instead of throttle position for TPA if airspeed is available. use throttle as {tpa_breakpoint + (airspeed - fw_reference_airspeed)/fw_reference_airspeed * (tpa_breakpoint - ThrottleIdleValue(default:1150))} for TPA calculation" + type: bool + field: throttle.airspeed_tpa + default_value: OFF - name: tpa_breakpoint description: "See tpa_rate." default_value: 1500 diff --git a/src/main/flight/pid.c b/src/main/flight/pid.c index ae8eb57fb30..f957073ccbd 100644 --- a/src/main/flight/pid.c +++ b/src/main/flight/pid.c @@ -469,15 +469,15 @@ static float calculateFixedWingTPAFactor(uint16_t throttle) return tpaFactor; } -static float calculateMultirotorTPAFactor(void) +static float calculateMultirotorTPAFactor(uint16_t throttle) { float tpaFactor; // TPA should be updated only when TPA is actually set - if (currentControlRateProfile->throttle.dynPID == 0 || rcCommand[THROTTLE] < currentControlRateProfile->throttle.pa_breakpoint) { + if (currentControlRateProfile->throttle.dynPID == 0 || throttle < currentControlRateProfile->throttle.pa_breakpoint) { tpaFactor = 1.0f; - } else if (rcCommand[THROTTLE] < getMaxThrottle()) { - tpaFactor = (100 - (uint16_t)currentControlRateProfile->throttle.dynPID * (rcCommand[THROTTLE] - currentControlRateProfile->throttle.pa_breakpoint) / (float)(getMaxThrottle() - currentControlRateProfile->throttle.pa_breakpoint)) / 100.0f; + } else if (throttle < getMaxThrottle()) { + tpaFactor = (100 - (uint16_t)currentControlRateProfile->throttle.dynPID * (throttle - currentControlRateProfile->throttle.pa_breakpoint) / (float)(getMaxThrottle() - currentControlRateProfile->throttle.pa_breakpoint)) / 100.0f; } else { tpaFactor = (100 - currentControlRateProfile->throttle.dynPID) / 100.0f; } @@ -493,20 +493,24 @@ void schedulePidGainsUpdate(void) void updatePIDCoefficients(void) { STATIC_FASTRAM uint16_t prevThrottle = 0; + STATIC_FASTRAM uint16_t tpaThrottle = 0; - // Check if throttle changed. Different logic for fixed wing vs multirotor - if (usedPidControllerType == PID_TYPE_PIFF && (currentControlRateProfile->throttle.fixedWingTauMs > 0)) { - uint16_t filteredThrottle = pt1FilterApply(&fixedWingTpaFilter, rcCommand[THROTTLE]); - if (filteredThrottle != prevThrottle) { - prevThrottle = filteredThrottle; - pidGainsUpdateRequired = true; - } + if (usedPidControllerType == PID_TYPE_PIFF && pitotValidForAirspeed() && currentControlRateProfile->throttle.airspeed_tpa) { + // Use airspeed instead of throttle for TPA calculation + const float airspeed = getAirspeedEstimate(); // in cm/s + const float referenceAirspeed = pidProfile()->fixedWingReferenceAirspeed; // in cm/s + tpaThrottle = currentControlRateProfile->throttle.pa_breakpoint + (uint16_t)((airspeed - referenceAirspeed) / referenceAirspeed * (currentControlRateProfile->throttle.pa_breakpoint - getThrottleIdleValue())); + //upper and lower limits will be applied in calculateFixedWingTPAFactor() + } + else if (usedPidControllerType == PID_TYPE_PIFF && (currentControlRateProfile->throttle.fixedWingTauMs > 0)) { + tpaThrottle = pt1FilterApply(&fixedWingTpaFilter, rcCommand[THROTTLE]); } else { - if (rcCommand[THROTTLE] != prevThrottle) { - prevThrottle = rcCommand[THROTTLE]; - pidGainsUpdateRequired = true; - } + tpaThrottle = rcCommand[THROTTLE]; + } + if (tpaThrottle != prevThrottle) { + prevThrottle = tpaThrottle; + pidGainsUpdateRequired = true; } #ifdef USE_ANTIGRAVITY @@ -528,8 +532,7 @@ void updatePIDCoefficients(void) return; } - const float tpaFactor = usedPidControllerType == PID_TYPE_PIFF ? calculateFixedWingTPAFactor(prevThrottle) : calculateMultirotorTPAFactor(); - + const float tpaFactor = usedPidControllerType == PID_TYPE_PIFF ? calculateFixedWingTPAFactor(prevThrottle) : calculateMultirotorTPAFactor(prevThrottle); // PID coefficients can be update only with THROTTLE and TPA or inflight PID adjustments //TODO: Next step would be to update those only at THROTTLE or inflight adjustments change for (int axis = 0; axis < 3; axis++) { diff --git a/src/main/sensors/pitotmeter.c b/src/main/sensors/pitotmeter.c index baf7bed0a61..cb5b9f76f9a 100755 --- a/src/main/sensors/pitotmeter.c +++ b/src/main/sensors/pitotmeter.c @@ -305,4 +305,13 @@ bool pitotIsHealthy(void) return (millis() - pitot.lastSeenHealthyMs) < PITOT_HARDWARE_TIMEOUT_MS; } +bool pitotValidForAirspeed(void) +{ + bool ret = false; + ret = pitotIsHealthy() && pitotIsCalibrationComplete(); + if (detectedSensors[SENSOR_INDEX_PITOT] == PITOT_VIRTUAL) { + ret = ret && STATE(GPS_FIX); + } + return ret; +} #endif /* PITOT */ diff --git a/src/main/sensors/pitotmeter.h b/src/main/sensors/pitotmeter.h index aed924f8e4c..68f5a9c1a02 100755 --- a/src/main/sensors/pitotmeter.h +++ b/src/main/sensors/pitotmeter.h @@ -69,5 +69,6 @@ void pitotStartCalibration(void); void pitotUpdate(void); float getAirspeedEstimate(void); bool pitotIsHealthy(void); +bool pitotValidForAirspeed(void); #endif From 6bc321b773c41ee928f62d5994b8e2e1345f4e20 Mon Sep 17 00:00:00 2001 From: shota Date: Sat, 27 Sep 2025 19:10:17 +0900 Subject: [PATCH 02/11] minor fix --- src/main/flight/pid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/flight/pid.c b/src/main/flight/pid.c index f957073ccbd..a85d9bf6bab 100644 --- a/src/main/flight/pid.c +++ b/src/main/flight/pid.c @@ -500,7 +500,7 @@ void updatePIDCoefficients(void) const float airspeed = getAirspeedEstimate(); // in cm/s const float referenceAirspeed = pidProfile()->fixedWingReferenceAirspeed; // in cm/s tpaThrottle = currentControlRateProfile->throttle.pa_breakpoint + (uint16_t)((airspeed - referenceAirspeed) / referenceAirspeed * (currentControlRateProfile->throttle.pa_breakpoint - getThrottleIdleValue())); - //upper and lower limits will be applied in calculateFixedWingTPAFactor() + tpaThrottle = constrain(tpaThrottle, getThrottleIdleValue(), tpaThrottle); } else if (usedPidControllerType == PID_TYPE_PIFF && (currentControlRateProfile->throttle.fixedWingTauMs > 0)) { tpaThrottle = pt1FilterApply(&fixedWingTpaFilter, rcCommand[THROTTLE]); From c1e211205d107293bbad9632af4721f46d918880 Mon Sep 17 00:00:00 2001 From: shota Date: Thu, 6 Nov 2025 10:44:51 +0900 Subject: [PATCH 03/11] airspeed tpa --- docs/Settings.md | 6 +- src/main/fc/controlrate_profile.c | 2 +- .../fc/controlrate_profile_config_struct.h | 2 +- src/main/fc/settings.yaml | 12 ++-- src/main/flight/pid.c | 60 ++++++++++++------- 5 files changed, 51 insertions(+), 31 deletions(-) diff --git a/docs/Settings.md b/docs/Settings.md index afab765a87d..aea055fb937 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -302,13 +302,13 @@ ADC channel to use for analog pitot tube (airspeed) sensor. If board doesn't hav --- -### airspeed_tpa +### airspeed_tpa_pow -Use airspeed instead of throttle position for TPA if airspeed is available. use throttle as {tpa_breakpoint + (airspeed - fw_reference_airspeed)/fw_reference_airspeed * (tpa_breakpoint - ThrottleIdleValue(default:1150))} for TPA calculation +Use airspeed instead of throttle position for TPA if airspeed is available on fixedwing. pid_multiplier(tpa_factor) = (referenceAirspeed/airspeed)**(airspeed_tpa_pow/100). Set to 0 will disable this feature and use throttle based tpa; | Default | Min | Max | | --- | --- | --- | -| OFF | OFF | ON | +| 120 | 0 | 200 | --- diff --git a/src/main/fc/controlrate_profile.c b/src/main/fc/controlrate_profile.c index 42fd3129ce1..1c2d632d9ce 100644 --- a/src/main/fc/controlrate_profile.c +++ b/src/main/fc/controlrate_profile.c @@ -47,7 +47,7 @@ void pgResetFn_controlRateProfiles(controlRateConfig_t *instance) .dynPID_on_YAW = SETTING_TPA_ON_YAW_DEFAULT, .pa_breakpoint = SETTING_TPA_BREAKPOINT_DEFAULT, .fixedWingTauMs = SETTING_FW_TPA_TIME_CONSTANT_DEFAULT, - .airspeed_tpa = SETTING_AIRSPEED_TPA_DEFAULT, + .airspeed_tpa_pow = SETTING_AIRSPEED_TPA_POW_DEFAULT, }, .stabilized = { diff --git a/src/main/fc/controlrate_profile_config_struct.h b/src/main/fc/controlrate_profile_config_struct.h index acd4e40926f..8c4e57b7f1f 100644 --- a/src/main/fc/controlrate_profile_config_struct.h +++ b/src/main/fc/controlrate_profile_config_struct.h @@ -32,7 +32,7 @@ typedef struct controlRateConfig_s { bool dynPID_on_YAW; uint16_t pa_breakpoint; // Breakpoint where TPA is activated uint16_t fixedWingTauMs; // Time constant of airplane TPA PT1-filter - bool airspeed_tpa; // Use airspeed instead of throttle position for TPA calculation + uint16_t airspeed_tpa_pow; // Use airspeed instead of throttle position for TPA calculation,0 to disable } throttle; struct { diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index a5593efaf03..1b14b82d580 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -1337,17 +1337,19 @@ groups: field: throttle.rcExpo8 min: 0 max: 100 + - name: airspeed_tpa_pow + description: "Use airspeed instead of throttle position for TPA if airspeed is available on fixedwing. pid_multiplier(tpa_factor) = (referenceAirspeed/airspeed)**(airspeed_tpa_pow/100). Set to 0 will disable this feature and use throttle based tpa;" + type: uint16_t + field: throttle.airspeed_tpa_pow + default_value: 120 + min: 0 + max: 200 - name: tpa_rate description: "Throttle PID attenuation reduces influence of PDFF on ROLL and PITCH of multi-rotor, PIDFF on ROLL,PITCH,YAW OF fixed_wing as throttle increases. For every 1% throttle after the TPA breakpoint, P is reduced by the TPA rate." default_value: 0 field: throttle.dynPID min: 0 max: 100 - - name: airspeed_tpa - description: "Use airspeed instead of throttle position for TPA if airspeed is available. use throttle as {tpa_breakpoint + (airspeed - fw_reference_airspeed)/fw_reference_airspeed * (tpa_breakpoint - ThrottleIdleValue(default:1150))} for TPA calculation" - type: bool - field: throttle.airspeed_tpa - default_value: OFF - name: tpa_breakpoint description: "See tpa_rate." default_value: 1500 diff --git a/src/main/flight/pid.c b/src/main/flight/pid.c index a85d9bf6bab..9b85d413e69 100644 --- a/src/main/flight/pid.c +++ b/src/main/flight/pid.c @@ -441,6 +441,14 @@ float pidRcCommandToRate(int16_t stick, uint8_t rate) return scaleRangef((float) stick, -500.0f, 500.0f, -maxRateDPS, maxRateDPS); } +static float calculateFixedWingAirspeedTPAFactor(void){ + const float airspeed = getAirspeedEstimate(); // in cm/s + const float referenceAirspeed = pidProfile()->fixedWingReferenceAirspeed; // in cm/s + float tpaFactor= powf(referenceAirspeed/(airspeed+0.01f), currentControlRateProfile->throttle.airspeed_tpa_pow/100.0f); + tpaFactor= constrainf(tpaFactor, 0.3f, 2.0f); + return tpaFactor; +} + static float calculateFixedWingTPAFactor(uint16_t throttle) { float tpaFactor; @@ -485,6 +493,19 @@ static float calculateMultirotorTPAFactor(uint16_t throttle) return tpaFactor; } +static float calculateTPAThtrottle(void) +{ + uint16_t tpaThrottle = 0; + + if (usedPidControllerType == PID_TYPE_PIFF && (currentControlRateProfile->throttle.fixedWingTauMs > 0)) { //fixed wing TPA with filtering + tpaThrottle = pt1FilterApply(&fixedWingTpaFilter, rcCommand[THROTTLE]); + } + else { + tpaThrottle = rcCommand[THROTTLE]; //multirotor TPA without filtering + } + return tpaThrottle; +} + void schedulePidGainsUpdate(void) { pidGainsUpdateRequired = true; @@ -492,26 +513,7 @@ void schedulePidGainsUpdate(void) void updatePIDCoefficients(void) { - STATIC_FASTRAM uint16_t prevThrottle = 0; - STATIC_FASTRAM uint16_t tpaThrottle = 0; - - if (usedPidControllerType == PID_TYPE_PIFF && pitotValidForAirspeed() && currentControlRateProfile->throttle.airspeed_tpa) { - // Use airspeed instead of throttle for TPA calculation - const float airspeed = getAirspeedEstimate(); // in cm/s - const float referenceAirspeed = pidProfile()->fixedWingReferenceAirspeed; // in cm/s - tpaThrottle = currentControlRateProfile->throttle.pa_breakpoint + (uint16_t)((airspeed - referenceAirspeed) / referenceAirspeed * (currentControlRateProfile->throttle.pa_breakpoint - getThrottleIdleValue())); - tpaThrottle = constrain(tpaThrottle, getThrottleIdleValue(), tpaThrottle); - } - else if (usedPidControllerType == PID_TYPE_PIFF && (currentControlRateProfile->throttle.fixedWingTauMs > 0)) { - tpaThrottle = pt1FilterApply(&fixedWingTpaFilter, rcCommand[THROTTLE]); - } - else { - tpaThrottle = rcCommand[THROTTLE]; - } - if (tpaThrottle != prevThrottle) { - prevThrottle = tpaThrottle; - pidGainsUpdateRequired = true; - } + STATIC_FASTRAM float tpaFactorprev=1.0f; #ifdef USE_ANTIGRAVITY if (usedPidControllerType == PID_TYPE_PID) { @@ -526,13 +528,29 @@ void updatePIDCoefficients(void) for (int axis = 0; axis < 3; axis++) { pidState[axis].stickPosition = constrain(rxGetChannelValue(axis) - PWM_RANGE_MIDDLE, -500, 500) / 500.0f; } + + float tpaFactor=1.0f; + if(usedPidControllerType == PID_TYPE_PIFF){ // Fixed wing TPA calculation + if(currentControlRateProfile->throttle.airspeed_tpa_pow>0){ + tpaFactor = calculateFixedWingAirspeedTPAFactor(); + }else{ + tpaFactor = calculateFixedWingTPAFactor(calculateTPAThtrottle()); + } + } else { + tpaFactor = calculateMultirotorTPAFactor(calculateTPAThtrottle()); + } + if (tpaFactor != tpaFactorprev) { + pidGainsUpdateRequired = true; + } + tpaFactorprev = tpaFactor; + // If nothing changed - don't waste time recalculating coefficients if (!pidGainsUpdateRequired) { return; } - const float tpaFactor = usedPidControllerType == PID_TYPE_PIFF ? calculateFixedWingTPAFactor(prevThrottle) : calculateMultirotorTPAFactor(prevThrottle); + // PID coefficients can be update only with THROTTLE and TPA or inflight PID adjustments //TODO: Next step would be to update those only at THROTTLE or inflight adjustments change for (int axis = 0; axis < 3; axis++) { From 4c44d5f01227a810e8a9873b9024d06210915c2d Mon Sep 17 00:00:00 2001 From: shota Date: Thu, 6 Nov 2025 12:02:22 +0900 Subject: [PATCH 04/11] Initialize pidGainsUpdateRequired to true for proper PID gain updates --- src/main/flight/pid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/flight/pid.c b/src/main/flight/pid.c index 9b85d413e69..a35b5add72a 100644 --- a/src/main/flight/pid.c +++ b/src/main/flight/pid.c @@ -126,7 +126,7 @@ static EXTENDED_FASTRAM pt1Filter_t headingHoldRateFilter; static EXTENDED_FASTRAM pt1Filter_t fixedWingTpaFilter; // Thrust PID Attenuation factor. 0.0f means fully attenuated, 1.0f no attenuation is applied -STATIC_FASTRAM bool pidGainsUpdateRequired; +STATIC_FASTRAM bool pidGainsUpdateRequired= true; FASTRAM int16_t axisPID[FLIGHT_DYNAMICS_INDEX_COUNT]; #ifdef USE_BLACKBOX @@ -513,7 +513,7 @@ void schedulePidGainsUpdate(void) void updatePIDCoefficients(void) { - STATIC_FASTRAM float tpaFactorprev=1.0f; + STATIC_FASTRAM float tpaFactorprev=-1.0f; #ifdef USE_ANTIGRAVITY if (usedPidControllerType == PID_TYPE_PID) { From 960336143959958364cbf44586d0f17f5509a1cd Mon Sep 17 00:00:00 2001 From: shota Date: Thu, 6 Nov 2025 12:13:59 +0900 Subject: [PATCH 05/11] raise pitot_lpf_milli_hz --- docs/Settings.md | 2 +- src/main/fc/settings.yaml | 2 +- src/main/flight/pid.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Settings.md b/docs/Settings.md index aea055fb937..193cc38485a 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -5578,7 +5578,7 @@ Pitot tube lowpass filter cutoff frequency. Lower cutoff frequencies result in s | Default | Min | Max | | --- | --- | --- | -| 350 | 0 | 10000 | +| 1000 | 0 | 10000 | --- diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 1b14b82d580..d5a80e745d3 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -656,7 +656,7 @@ groups: description: "Pitot tube lowpass filter cutoff frequency. Lower cutoff frequencies result in smoother response at expense of command control delay" min: 0 max: 10000 - default_value: 350 + default_value: 1000 - name: pitot_scale description: "Pitot tube scale factor" min: 0 diff --git a/src/main/flight/pid.c b/src/main/flight/pid.c index a35b5add72a..14a78ea7842 100644 --- a/src/main/flight/pid.c +++ b/src/main/flight/pid.c @@ -531,7 +531,7 @@ void updatePIDCoefficients(void) float tpaFactor=1.0f; if(usedPidControllerType == PID_TYPE_PIFF){ // Fixed wing TPA calculation - if(currentControlRateProfile->throttle.airspeed_tpa_pow>0){ + if(currentControlRateProfile->throttle.airspeed_tpa_pow>0 && pitotValidForAirspeed()){ tpaFactor = calculateFixedWingAirspeedTPAFactor(); }else{ tpaFactor = calculateFixedWingTPAFactor(calculateTPAThtrottle()); From 00c9db0f46d4c0bc28a750007d9a2b060e0bcf02 Mon Sep 17 00:00:00 2001 From: shota hayashi Date: Mon, 17 Nov 2025 02:15:19 +0900 Subject: [PATCH 06/11] enhance tpa with pitch angle aware --- docs/Settings.md | 22 +++++++++---------- src/main/fc/controlrate_profile.c | 2 +- .../fc/controlrate_profile_config_struct.h | 2 +- src/main/fc/settings.yaml | 8 +++---- src/main/flight/pid.c | 11 +++++++--- 5 files changed, 25 insertions(+), 20 deletions(-) diff --git a/docs/Settings.md b/docs/Settings.md index 193cc38485a..7cc99c94e7a 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -302,16 +302,6 @@ ADC channel to use for analog pitot tube (airspeed) sensor. If board doesn't hav --- -### airspeed_tpa_pow - -Use airspeed instead of throttle position for TPA if airspeed is available on fixedwing. pid_multiplier(tpa_factor) = (referenceAirspeed/airspeed)**(airspeed_tpa_pow/100). Set to 0 will disable this feature and use throttle based tpa; - -| Default | Min | Max | -| --- | --- | --- | -| 120 | 0 | 200 | - ---- - ### align_board_pitch Arbitrary board rotation in deci-degrees (0.1 degree), to allow mounting it sideways / upside down / rotated etc @@ -432,6 +422,16 @@ Max Antigravity gain. `1` means Antigravity is disabled, `2` means Iterm is allo --- +### apa_pow + +Use airspeed instead of throttle position for PID attenuation if airspeed is available on fixedwing. pid_multiplier = (referenceAirspeed/airspeed)**(apa_pow/100). Set to 0 will disable this feature and use throttle based PID attenuation; + +| Default | Min | Max | +| --- | --- | --- | +| 120 | 0 | 200 | + +--- + ### applied_defaults Internal (configurator) hint. Should not be changed manually @@ -6394,7 +6394,7 @@ Throttle PID attenuation also reduces influence on YAW for multi-rotor, Should b ### tpa_rate -Throttle PID attenuation reduces influence of PDFF on ROLL and PITCH of multi-rotor, PIDFF on ROLL,PITCH,YAW OF fixed_wing as throttle increases. For every 1% throttle after the TPA breakpoint, P is reduced by the TPA rate. +Throttle based PID attenuation(TPA) reduces influence of PDFF on ROLL and PITCH of multi-rotor, PIDFF on ROLL,PITCH,YAW OF fixed_wing as throttle increases. For every 1% throttle after the TPA breakpoint, P is reduced by the TPA rate. | Default | Min | Max | | --- | --- | --- | diff --git a/src/main/fc/controlrate_profile.c b/src/main/fc/controlrate_profile.c index 1c2d632d9ce..a7794e47eed 100644 --- a/src/main/fc/controlrate_profile.c +++ b/src/main/fc/controlrate_profile.c @@ -47,7 +47,7 @@ void pgResetFn_controlRateProfiles(controlRateConfig_t *instance) .dynPID_on_YAW = SETTING_TPA_ON_YAW_DEFAULT, .pa_breakpoint = SETTING_TPA_BREAKPOINT_DEFAULT, .fixedWingTauMs = SETTING_FW_TPA_TIME_CONSTANT_DEFAULT, - .airspeed_tpa_pow = SETTING_AIRSPEED_TPA_POW_DEFAULT, + .apa_pow = SETTING_APA_POW_DEFAULT, }, .stabilized = { diff --git a/src/main/fc/controlrate_profile_config_struct.h b/src/main/fc/controlrate_profile_config_struct.h index 8c4e57b7f1f..2ac4aa421ee 100644 --- a/src/main/fc/controlrate_profile_config_struct.h +++ b/src/main/fc/controlrate_profile_config_struct.h @@ -32,7 +32,7 @@ typedef struct controlRateConfig_s { bool dynPID_on_YAW; uint16_t pa_breakpoint; // Breakpoint where TPA is activated uint16_t fixedWingTauMs; // Time constant of airplane TPA PT1-filter - uint16_t airspeed_tpa_pow; // Use airspeed instead of throttle position for TPA calculation,0 to disable + uint16_t apa_pow; // Use airspeed instead of throttle position for TPA calculation,0 to disable } throttle; struct { diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index d5a80e745d3..7ff9c3b7a6e 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -1337,15 +1337,15 @@ groups: field: throttle.rcExpo8 min: 0 max: 100 - - name: airspeed_tpa_pow - description: "Use airspeed instead of throttle position for TPA if airspeed is available on fixedwing. pid_multiplier(tpa_factor) = (referenceAirspeed/airspeed)**(airspeed_tpa_pow/100). Set to 0 will disable this feature and use throttle based tpa;" + - name: apa_pow + description: "Use airspeed instead of throttle position for PID attenuation if airspeed is available on fixedwing. pid_multiplier = (referenceAirspeed/airspeed)**(apa_pow/100). Set to 0 will disable this feature and use throttle based PID attenuation;" type: uint16_t - field: throttle.airspeed_tpa_pow + field: throttle.apa_pow default_value: 120 min: 0 max: 200 - name: tpa_rate - description: "Throttle PID attenuation reduces influence of PDFF on ROLL and PITCH of multi-rotor, PIDFF on ROLL,PITCH,YAW OF fixed_wing as throttle increases. For every 1% throttle after the TPA breakpoint, P is reduced by the TPA rate." + description: "Throttle based PID attenuation(TPA) reduces influence of PDFF on ROLL and PITCH of multi-rotor, PIDFF on ROLL,PITCH,YAW OF fixed_wing as throttle increases. For every 1% throttle after the TPA breakpoint, P is reduced by the TPA rate." default_value: 0 field: throttle.dynPID min: 0 diff --git a/src/main/flight/pid.c b/src/main/flight/pid.c index 14a78ea7842..061646840b4 100644 --- a/src/main/flight/pid.c +++ b/src/main/flight/pid.c @@ -444,7 +444,7 @@ float pidRcCommandToRate(int16_t stick, uint8_t rate) static float calculateFixedWingAirspeedTPAFactor(void){ const float airspeed = getAirspeedEstimate(); // in cm/s const float referenceAirspeed = pidProfile()->fixedWingReferenceAirspeed; // in cm/s - float tpaFactor= powf(referenceAirspeed/(airspeed+0.01f), currentControlRateProfile->throttle.airspeed_tpa_pow/100.0f); + float tpaFactor= powf(referenceAirspeed/(airspeed+0.01f), currentControlRateProfile->throttle.apa_pow/100.0f); tpaFactor= constrainf(tpaFactor, 0.3f, 2.0f); return tpaFactor; } @@ -496,9 +496,14 @@ static float calculateMultirotorTPAFactor(uint16_t throttle) static float calculateTPAThtrottle(void) { uint16_t tpaThrottle = 0; + static const fpVector3_t vDown = { .v = { 0.0f, 0.0f, 1.0f } }; if (usedPidControllerType == PID_TYPE_PIFF && (currentControlRateProfile->throttle.fixedWingTauMs > 0)) { //fixed wing TPA with filtering - tpaThrottle = pt1FilterApply(&fixedWingTpaFilter, rcCommand[THROTTLE]); + fpVector3_t vForward = { .v = { HeadVecEFFiltered.x, -HeadVecEFFiltered.y, -HeadVecEFFiltered.z } }; + float groundCos = vectorDotProduct(&vForward, &vDown); + int16_t throttleAdjustment = currentBatteryProfile->nav.fw.pitch_to_throttle * groundCos * 90.0f; // 90 degrees is to scale from cos to throttle adjustment + uint16_t throttleAdjusted = rcCommand[THROTTLE] + constrain(throttleAdjustment, -1000, 1000); + tpaThrottle = pt1FilterApply(&fixedWingTpaFilter, constrain(throttleAdjusted, 1000, 2000)); } else { tpaThrottle = rcCommand[THROTTLE]; //multirotor TPA without filtering @@ -531,7 +536,7 @@ void updatePIDCoefficients(void) float tpaFactor=1.0f; if(usedPidControllerType == PID_TYPE_PIFF){ // Fixed wing TPA calculation - if(currentControlRateProfile->throttle.airspeed_tpa_pow>0 && pitotValidForAirspeed()){ + if(currentControlRateProfile->throttle.apa_pow>0 && pitotValidForAirspeed()){ tpaFactor = calculateFixedWingAirspeedTPAFactor(); }else{ tpaFactor = calculateFixedWingTPAFactor(calculateTPAThtrottle()); From 89850d7fc31fa8c3e4c00648350a5d75891fe9a4 Mon Sep 17 00:00:00 2001 From: shota hayashi Date: Thu, 11 Dec 2025 01:03:03 +0900 Subject: [PATCH 07/11] update on tpa --- src/main/fc/controlrate_profile.c | 1 + src/main/fc/controlrate_profile_config_struct.h | 1 + src/main/fc/settings.yaml | 10 ++++++++-- src/main/flight/pid.c | 10 +++++----- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main/fc/controlrate_profile.c b/src/main/fc/controlrate_profile.c index a7794e47eed..d004908b72b 100644 --- a/src/main/fc/controlrate_profile.c +++ b/src/main/fc/controlrate_profile.c @@ -48,6 +48,7 @@ void pgResetFn_controlRateProfiles(controlRateConfig_t *instance) .pa_breakpoint = SETTING_TPA_BREAKPOINT_DEFAULT, .fixedWingTauMs = SETTING_FW_TPA_TIME_CONSTANT_DEFAULT, .apa_pow = SETTING_APA_POW_DEFAULT, + .tpa_pitch_compensation = SETTING_TPA_PITCH_COMPENSATION_DEFAULT }, .stabilized = { diff --git a/src/main/fc/controlrate_profile_config_struct.h b/src/main/fc/controlrate_profile_config_struct.h index 2ac4aa421ee..7f46fb57a83 100644 --- a/src/main/fc/controlrate_profile_config_struct.h +++ b/src/main/fc/controlrate_profile_config_struct.h @@ -33,6 +33,7 @@ typedef struct controlRateConfig_s { uint16_t pa_breakpoint; // Breakpoint where TPA is activated uint16_t fixedWingTauMs; // Time constant of airplane TPA PT1-filter uint16_t apa_pow; // Use airspeed instead of throttle position for TPA calculation,0 to disable + uint8_t tpa_pitch_compensation; // Pitch angle based throttle compensation for fixed wing } throttle; struct { diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 5e513b328f3..19dbb0ce5f6 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -1387,17 +1387,23 @@ groups: min: 0 max: 200 - name: tpa_rate - description: "Throttle based PID attenuation(TPA) reduces influence of PDFF on ROLL and PITCH of multi-rotor, PIDFF on ROLL,PITCH,YAW OF fixed_wing as throttle increases. For every 1% throttle after the TPA breakpoint, P is reduced by the TPA rate." + description: "Throttle based PID attenuation(TPA) reduces influence of PDFF on ROLL and PITCH of multi-rotor, PIDFF on ROLL,PITCH,YAW OF fixed_wing as throttle increases. On multirotor, For every 1% throttle after the TPA breakpoint, P is reduced by the TPA rate. for fixedwing modifies PIDFF. See **PID Attenuation and scaling** Wiki for full details." default_value: 0 field: throttle.dynPID min: 0 - max: 100 + max: 200 - name: tpa_breakpoint description: "See tpa_rate." default_value: 1500 field: throttle.pa_breakpoint min: PWM_RANGE_MIN max: PWM_RANGE_MAX + - name: tpa_pitch_compensation + description: "Pitch angle based throttle compensation for fixed wing. Positive values will increase throttle when pitching up, and decrease throttle when pitching down." + default_value: 8 + field: throttle.tpa_pitch_compensation + min: 0 + max: 20 - name: tpa_on_yaw description: "Throttle PID attenuation also reduces influence on YAW for multi-rotor, Should be set to ON for tilting rotors." type: bool diff --git a/src/main/flight/pid.c b/src/main/flight/pid.c index a5e34c6f795..b529b30b511 100644 --- a/src/main/flight/pid.c +++ b/src/main/flight/pid.c @@ -460,9 +460,6 @@ static float calculateFixedWingTPAFactor(uint16_t throttle) if (throttle > getThrottleIdleValue()) { // Calculate TPA according to throttle tpaFactor = 0.5f + ((float)(currentControlRateProfile->throttle.pa_breakpoint - getThrottleIdleValue()) / (throttle - getThrottleIdleValue()) / 2.0f); - - // Limit to [0.5; 2] range - tpaFactor = constrainf(tpaFactor, 0.5f, 2.0f); } else { tpaFactor = 2.0f; @@ -470,6 +467,8 @@ static float calculateFixedWingTPAFactor(uint16_t throttle) // Attenuate TPA curve according to configured amount tpaFactor = 1.0f + (tpaFactor - 1.0f) * (currentControlRateProfile->throttle.dynPID / 100.0f); + // Limit to [0.5; 2] range + tpaFactor = constrainf(tpaFactor, 0.3f, 2.0f); } else { tpaFactor = 1.0f; @@ -488,7 +487,7 @@ static float calculateMultirotorTPAFactor(uint16_t throttle) } else if (throttle < getMaxThrottle()) { tpaFactor = (100 - (uint16_t)currentControlRateProfile->throttle.dynPID * (throttle - currentControlRateProfile->throttle.pa_breakpoint) / (float)(getMaxThrottle() - currentControlRateProfile->throttle.pa_breakpoint)) / 100.0f; } else { - tpaFactor = (100 - currentControlRateProfile->throttle.dynPID) / 100.0f; + tpaFactor = (100 - constrain(currentControlRateProfile->throttle.dynPID, 0, 100)) / 100.0f; } return tpaFactor; @@ -502,7 +501,8 @@ static float calculateTPAThtrottle(void) if (usedPidControllerType == PID_TYPE_PIFF && (currentControlRateProfile->throttle.fixedWingTauMs > 0)) { //fixed wing TPA with filtering fpVector3_t vForward = { .v = { HeadVecEFFiltered.x, -HeadVecEFFiltered.y, -HeadVecEFFiltered.z } }; float groundCos = vectorDotProduct(&vForward, &vDown); - int16_t throttleAdjustment = currentBatteryProfile->nav.fw.pitch_to_throttle * groundCos * 90.0f; // 90 degrees is to scale from cos to throttle adjustment + int16_t throttleAdjustment = currentControlRateProfile->throttle.tpa_pitch_compensation * groundCos * 90.0f / (PI/2); //when 1deg pitch up, increase throttle by pitch(deg)_to_throttle. cos(89 deg)*90/(pi/2)=0.99995,cos(80 deg)*90/(pi/2)=9.9493, + throttleAdjustment= throttleAdjustment<0? throttleAdjustment/2:throttleAdjustment; //reduce throttle compensation when pitch down uint16_t throttleAdjusted = rcCommand[THROTTLE] + constrain(throttleAdjustment, -1000, 1000); tpaThrottle = pt1FilterApply(&fixedWingTpaFilter, constrain(throttleAdjusted, 1000, 2000)); } From 4690775c8489723d1567128f562195fd015c30f8 Mon Sep 17 00:00:00 2001 From: shota hayashi Date: Thu, 11 Dec 2025 01:06:18 +0900 Subject: [PATCH 08/11] update docs --- docs/Settings.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/Settings.md b/docs/Settings.md index 02cae38e59b..daf6990dacb 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -6462,13 +6462,23 @@ Throttle PID attenuation also reduces influence on YAW for multi-rotor, Should b --- +### tpa_pitch_compensation + +Pitch angle based throttle compensation for fixed wing. Positive values will increase throttle when pitching up, and decrease throttle when pitching down. + +| Default | Min | Max | +| --- | --- | --- | +| 8 | 0 | 20 | + +--- + ### tpa_rate -Throttle based PID attenuation(TPA) reduces influence of PDFF on ROLL and PITCH of multi-rotor, PIDFF on ROLL,PITCH,YAW OF fixed_wing as throttle increases. For every 1% throttle after the TPA breakpoint, P is reduced by the TPA rate. +Throttle based PID attenuation(TPA) reduces influence of PDFF on ROLL and PITCH of multi-rotor, PIDFF on ROLL,PITCH,YAW OF fixed_wing as throttle increases. On multirotor, For every 1% throttle after the TPA breakpoint, P is reduced by the TPA rate. for fixedwing modifies PIDFF. See **PID Attenuation and scaling** Wiki for full details. | Default | Min | Max | | --- | --- | --- | -| 0 | 0 | 100 | +| 0 | 0 | 200 | --- From 31a5cbea8d74fd5ca8652e04b66a0fe83c9daab3 Mon Sep 17 00:00:00 2001 From: shota hayashi Date: Thu, 11 Dec 2025 01:10:23 +0900 Subject: [PATCH 09/11] minor fix on comments --- src/main/flight/pid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/flight/pid.c b/src/main/flight/pid.c index b529b30b511..62e3d7f0dd8 100644 --- a/src/main/flight/pid.c +++ b/src/main/flight/pid.c @@ -502,7 +502,7 @@ static float calculateTPAThtrottle(void) fpVector3_t vForward = { .v = { HeadVecEFFiltered.x, -HeadVecEFFiltered.y, -HeadVecEFFiltered.z } }; float groundCos = vectorDotProduct(&vForward, &vDown); int16_t throttleAdjustment = currentControlRateProfile->throttle.tpa_pitch_compensation * groundCos * 90.0f / (PI/2); //when 1deg pitch up, increase throttle by pitch(deg)_to_throttle. cos(89 deg)*90/(pi/2)=0.99995,cos(80 deg)*90/(pi/2)=9.9493, - throttleAdjustment= throttleAdjustment<0? throttleAdjustment/2:throttleAdjustment; //reduce throttle compensation when pitch down + throttleAdjustment= throttleAdjustment<0? throttleAdjustment/2:throttleAdjustment; //reduce throttle compensation when pitch up(when throttleAdjustment is negative which means trying to reduce "throttle") uint16_t throttleAdjusted = rcCommand[THROTTLE] + constrain(throttleAdjustment, -1000, 1000); tpaThrottle = pt1FilterApply(&fixedWingTpaFilter, constrain(throttleAdjusted, 1000, 2000)); } From 253cd41085fc18f9b495d0a861d1c3c4f4cb24ed Mon Sep 17 00:00:00 2001 From: shota hayashi Date: Thu, 11 Dec 2025 01:30:56 +0900 Subject: [PATCH 10/11] minor fix --- src/main/flight/pid.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/flight/pid.c b/src/main/flight/pid.c index 82257897c01..b67ec110519 100644 --- a/src/main/flight/pid.c +++ b/src/main/flight/pid.c @@ -501,8 +501,7 @@ static float calculateTPAThtrottle(void) if (usedPidControllerType == PID_TYPE_PIFF && (currentControlProfile->throttle.fixedWingTauMs > 0)) { //fixed wing TPA with filtering fpVector3_t vForward = { .v = { HeadVecEFFiltered.x, -HeadVecEFFiltered.y, -HeadVecEFFiltered.z } }; float groundCos = vectorDotProduct(&vForward, &vDown); - int16_t throttleAdjustment = currentControlProfile->throttle.tpa_pitch_compensation * groundCos * 90.0f / (PI/2); //when 1deg pitch up, increase throttle by pitch(deg)_to_throttle. cos(89 deg)*90/(pi/2)=0.99995,cos(80 deg)*90/(pi/2)=9.9493, - throttleAdjustment= throttleAdjustment<0? throttleAdjustment/2:throttleAdjustment; //reduce throttle compensation when pitch up(when throttleAdjustment is negative which means trying to reduce "throttle") + int16_t throttleAdjustment = currentControlProfile->throttle.tpa_pitch_compensation * groundCos * 90.0f / 1.57079632679; //when 1deg pitch up, increase throttle by pitch(deg)_to_throttle. cos(89 deg)*90/(pi/2)=0.99995,cos(80 deg)*90/(pi/2)=9.9493, uint16_t throttleAdjusted = rcCommand[THROTTLE] + constrain(throttleAdjustment, -1000, 1000); tpaThrottle = pt1FilterApply(&fixedWingTpaFilter, constrain(throttleAdjusted, 1000, 2000)); } From 4d3ee07ac0418cd1922c89f96ecc728777bc1e3a Mon Sep 17 00:00:00 2001 From: shota hayashi Date: Thu, 11 Dec 2025 01:56:32 +0900 Subject: [PATCH 11/11] fix: correct floating-point division in throttle adjustment calculation --- src/main/flight/pid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/flight/pid.c b/src/main/flight/pid.c index b67ec110519..7a112ff8f4f 100644 --- a/src/main/flight/pid.c +++ b/src/main/flight/pid.c @@ -501,7 +501,7 @@ static float calculateTPAThtrottle(void) if (usedPidControllerType == PID_TYPE_PIFF && (currentControlProfile->throttle.fixedWingTauMs > 0)) { //fixed wing TPA with filtering fpVector3_t vForward = { .v = { HeadVecEFFiltered.x, -HeadVecEFFiltered.y, -HeadVecEFFiltered.z } }; float groundCos = vectorDotProduct(&vForward, &vDown); - int16_t throttleAdjustment = currentControlProfile->throttle.tpa_pitch_compensation * groundCos * 90.0f / 1.57079632679; //when 1deg pitch up, increase throttle by pitch(deg)_to_throttle. cos(89 deg)*90/(pi/2)=0.99995,cos(80 deg)*90/(pi/2)=9.9493, + int16_t throttleAdjustment = currentControlProfile->throttle.tpa_pitch_compensation * groundCos * 90.0f / 1.57079632679f; //when 1deg pitch up, increase throttle by pitch(deg)_to_throttle. cos(89 deg)*90/(pi/2)=0.99995,cos(80 deg)*90/(pi/2)=9.9493, uint16_t throttleAdjusted = rcCommand[THROTTLE] + constrain(throttleAdjustment, -1000, 1000); tpaThrottle = pt1FilterApply(&fixedWingTpaFilter, constrain(throttleAdjusted, 1000, 2000)); }