Skip to content

Conversation

@shota3527
Copy link
Contributor

@shota3527 shota3527 commented Sep 27, 2025

User description

add a cli command to let fixed wing use airspeed based tpa.
It calculates the corresponding throttle value based on the airspeed. Ensure a seamless integration with the current throttle-based TPA. Turn it on with cli command and no more tunning is needed, fallback to old throttle based tpa if airspeed is not avaliable

tpa_breakpoint + (airspeed - fw_reference_airspeed)/fw_reference_airspeed * (tpa_breakpoint - ThrottleIdleValue(default:1150))
sim test ok

recommend to raise pitot_lpf_milli_hz on vitual pitot


PR Type

Enhancement


Description

This description is generated by an AI tool. It may have inaccuracies

  • Add airspeed-based TPA calculation for fixed-wing aircraft

  • New airspeed_tpa setting to enable airspeed TPA mode

  • Refactor TPA calculation to support both throttle and airspeed

  • Add airspeed validity check function for sensor health


Diagram Walkthrough

flowchart LR
  A["Throttle Input"] --> B["TPA Calculation"]
  C["Airspeed Sensor"] --> D["Airspeed Valid Check"]
  D --> E["Airspeed TPA Mode"]
  E --> B
  B --> F["PID Coefficients Update"]
Loading

File Walkthrough

Relevant files
Configuration changes
controlrate_profile.c
Initialize airspeed TPA setting                                                   

src/main/fc/controlrate_profile.c

  • Add airspeed_tpa field initialization to control rate profile reset
    function
+2/-1     
controlrate_profile_config_struct.h
Add airspeed TPA configuration field                                         

src/main/fc/controlrate_profile_config_struct.h

  • Add airspeed_tpa boolean field to throttle configuration structure
+1/-0     
settings.yaml
Configure airspeed TPA setting                                                     

src/main/fc/settings.yaml

  • Add airspeed_tpa setting configuration with description
  • Set default value to OFF
+5/-0     
Enhancement
pid.c
Implement airspeed-based TPA calculation                                 

src/main/flight/pid.c

  • Refactor TPA calculation to support airspeed-based mode
  • Add airspeed TPA calculation using reference airspeed
  • Modify calculateMultirotorTPAFactor to accept throttle parameter
  • Update PID coefficient calculation logic
+20/-17 
pitotmeter.c
Add airspeed sensor validity check                                             

src/main/sensors/pitotmeter.c

  • Add pitotValidForAirspeed function to check sensor validity
  • Include GPS fix requirement for virtual pitot sensors
+9/-0     
pitotmeter.h
Expose airspeed validity function                                               

src/main/sensors/pitotmeter.h

  • Add function declaration for pitotValidForAirspeed
+1/-0     
Documentation
Settings.md
Document airspeed TPA setting                                                       

docs/Settings.md

  • Add documentation for airspeed_tpa setting
  • Include formula and usage description
+10/-0   

@qodo-merge-pro
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
⚡ Recommended focus areas for review

Possible Issue

The new multirotor TPA now takes a throttle parameter but updatePIDCoefficients passes prevThrottle, which may represent airspeed-derived or filtered values in fixed-wing mode. Ensure that for multirotor mode the raw throttle is used so attenuation matches actual RC throttle, and verify integer vs float math does not cause unintended clamping.

static float calculateMultirotorTPAFactor(uint16_t throttle)
{
    float tpaFactor;

    // TPA should be updated only when TPA is actually set
    if (currentControlRateProfile->throttle.dynPID == 0 || throttle < currentControlRateProfile->throttle.pa_breakpoint) {
        tpaFactor = 1.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;
    }
Edge Conditions

The airspeed-to-throttle mapping can produce values below idle or above max; while comments say limits are applied in fixed-wing TPA, validate calculateFixedWingTPAFactor safely handles tpaThrottle outside [idle, max] to avoid negative or >1 factors.

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]);
Maintainability

prevThrottle now serves as a generic TPA driver (raw, filtered, or airspeed-derived). Consider renaming to a neutral term and documenting its semantics to prevent future misuse across flight modes.

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()));
        //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 {
        tpaThrottle = rcCommand[THROTTLE];
    }
    if (tpaThrottle != prevThrottle) {
        prevThrottle = tpaThrottle;
        pidGainsUpdateRequired = true;
    }

@qodo-merge-pro
Copy link
Contributor

qodo-merge-pro bot commented Sep 27, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
The TPA implementation is overly complex

The current TPA implementation is overly complex, creating a "virtual throttle"
from airspeed. It should be replaced with a more direct approach that scales PID
gains inversely with dynamic pressure (airspeed^2).

Examples:

src/main/flight/pid.c [498-503]
    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()

Solution Walkthrough:

Before:

function updatePIDCoefficients() {
  // ...
  tpaThrottle = 0;
  if (airspeed_tpa_enabled && airspeed_is_valid) {
    // Convert airspeed to a "virtual throttle" value
    airspeed = getAirspeedEstimate();
    referenceAirspeed = pidProfile()->fixedWingReferenceAirspeed;
    tpaThrottle = breakpoint + (airspeed - referenceAirspeed) / referenceAirspeed * (breakpoint - idle_throttle);
  } else {
    tpaThrottle = rcCommand[THROTTLE];
  }

  // Feed virtual throttle into the old TPA formula
  tpaFactor = calculateFixedWingTPAFactor(tpaThrottle);

  // Scale PIDs
  pidState.kP = baseP * tpaFactor;
  // ...
}

After:

function updatePIDCoefficients() {
  // ...
  tpaFactor = 1.0;
  if (airspeed_tpa_enabled && airspeed_is_valid) {
    // Directly calculate scaling factor based on dynamic pressure (airspeed^2)
    airspeed = getAirspeedEstimate();
    referenceAirspeed = pidProfile()->fixedWingReferenceAirspeed;
    if (airspeed > 0) {
      tpaFactor = (referenceAirspeed * referenceAirspeed) / (airspeed * airspeed);
    }
    // Apply user-configured TPA amount and limits
    tpaFactor = 1.0 + (tpaFactor - 1.0) * (tpa_rate / 100.0);
    tpaFactor = constrain(tpaFactor, 0.5, 2.0);
  } else {
    tpaFactor = calculateFixedWingTPAFactor(rcCommand[THROTTLE]);
  }
  // Scale PIDs
  pidState.kP = baseP * tpaFactor;
  // ...
}
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a convoluted design choice and proposes a more direct, physically-grounded, and standard industry approach, significantly improving the feature's clarity and tunability.

High
Possible issue
Prevent division-by-zero in TPA calculation
Suggestion Impact:The commit removed the direct division by referenceAirspeed and introduced a new airspeed-based TPA factor calculation using referenceAirspeed/(airspeed+0.01f), which avoids division-by-zero without needing a fallback throttle path. It thus mitigates the division-by-zero risk the suggestion identified, albeit via a different implementation.

code diff:

+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;
+}

Add a check to prevent division-by-zero when referenceAirspeed is zero in the
tpaThrottle calculation, falling back to the standard throttle value if
necessary.

src/main/flight/pid.c [501-502]

 const float referenceAirspeed = pidProfile()->fixedWingReferenceAirspeed; // in cm/s
-tpaThrottle = currentControlRateProfile->throttle.pa_breakpoint + (uint16_t)((airspeed - referenceAirspeed) / referenceAirspeed * (currentControlRateProfile->throttle.pa_breakpoint - getThrottleIdleValue()));
+if (referenceAirspeed > 0) {
+    tpaThrottle = currentControlRateProfile->throttle.pa_breakpoint + (uint16_t)((airspeed - referenceAirspeed) / referenceAirspeed * (currentControlRateProfile->throttle.pa_breakpoint - getThrottleIdleValue()));
+} else {
+    // Fallback to regular throttle if reference airspeed is not configured
+    tpaThrottle = rcCommand[THROTTLE];
+}

[Suggestion processed]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical division-by-zero risk in the new TPA calculation, which could cause a flight controller crash or loss of control.

High
Ensure a pitot sensor is detected

Add a check in pitotValidForAirspeed to ensure a pitot sensor is detected
(PITOT_NONE) before proceeding with other validity checks.

src/main/sensors/pitotmeter.c [308-316]

 bool pitotValidForAirspeed(void)
 {
-    bool ret = false;
-    ret = pitotIsHealthy() && pitotIsCalibrationComplete();
+    if (detectedSensors[SENSOR_INDEX_PITOT] == PITOT_NONE) {
+        return false;
+    }
+
+    bool ret = pitotIsHealthy() && pitotIsCalibrationComplete();
     if (detectedSensors[SENSOR_INDEX_PITOT] == PITOT_VIRTUAL) {
         ret = ret && STATE(GPS_FIX);
     }
     return ret;
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a potential edge case where the system might incorrectly use airspeed TPA if no pitot sensor is configured, improving the robustness of the check.

Medium
  • Update

@shota3527
Copy link
Contributor Author

throttle position vs tpa factor
image

@sensei-hacker
Copy link
Member

sensei-hacker commented Oct 15, 2025

This does look kinda over-complicated for historical reasons. But I'm not volunteering to refactor it, so I'm not really complaining.

We originally saw we need to reduce the PID when airspeed increases. Because the force/lift from the control surface is proportional to ( 0.5 * airspeed) ².

What we really wanted originally when TPA was introduced was the airspeed. But we often don't have the airspeed. So we used the throttle value to make a rough estimate of what the airspeed might be, in order to reduce the PID appropriately.

Now when we DO have the airspeed (the value we wanted in the first place), we're using the airspeed to simulate a throttle value, in order to estimate how much the throttle value might affect the airspeed and therefore the PID.

Kinda like:
We want to know how much Mike weighs.
The scale says he weighs 140 pounds.
Therefore we can estimate he's about six feet tall.
On average, people who are six feet tall weigh 155 pounds.
Therefore Mike probably weighs 155 pounds (an estimate originally based on the fact that he weighs 140 pounds).

Given we already know the airspeed, there's no need to mess around with estimating a "virtual throttle", then estimating how much that will affect the velocity and therefore the force.
The number we need is simply ( 0.5 * airspeed ) ² , from the lift equation.

@shota3527
Copy link
Contributor Author

shota3527 commented Oct 16, 2025

We originally saw we need to reduce the PID when airspeed increases. Because the force/lift from the control surface is proportional to ( 0.5 * airspeed) ².

I think this only explain the first half of the theory and it is not proportial but inverse propotional, The following are my thoughts, in short the angular velocity of the plane is propotional to the control surface multiplied by airspeed. If the old TPA assumes throttle is propotional to airspeed. then it is on same page with the following. And airspeed gain reduction(TPA) will need some internal variable to calculate, then why dont we use the virtual throttle value as the internal variable to maintain the compability, reducing the workload of users to shift from throttle based to airspeed based.

Relationship Between Airspeed, Control Surface Deflection, and Angular Velocity in Model Aircraft

1. Fundamental Dynamic Equation

For a single rotational axis (roll, pitch, or yaw):

dot(ω) + A·V·ω = B·V²·δ

  • ω: angular velocity (rad/s)
  • dot(ω): angular acceleration (rad/s²)
  • V: airspeed (m/s)
  • δ: control surface deflection angle (rad)
  • A, B: lumped constants (geometry, aero derivatives, inertia, air density)

Interpretation:
Angular acceleration + rate damping = airspeed² × control surface deflection


2. Steady-State Condition

In our small plane, the plane enters Steady-State quily, that is why the plane is mainly driven by FF gain
When angular acceleration is negligible:

0 + A·V·ω = B·V²·δ

Therefore:

ω = (B/A)·V·δ


3. Key Proportional Relationships

  • At fixed δ: angular velocity increases linearly with airspeed.
  • At fixed ω: required control deflection decreases as 1/V.

4. Quick Reference

  1. Constant angular rate control law
    δ ≈ K·(ω_cmd / V), where K = A / B

  2. Two-speed comparison
    For the same target angular rate ω:
    δ₂ / δ₁ = V₁ / V₂


Summary

Condition Relationship
General dynamics dot(ω) + A·V·ω = B·V²·δ
Steady state ω = (B/A)·V·δ
Constant rate control δ ∝ 1/V
Two-speed comparison δ₂ / δ₁ = V₁ / V₂

High-speed flight requires smaller control deflections to achieve the same rotation rate due to aerodynamic damping and increased control effectiveness.

@sensei-hacker
Copy link
Member

sensei-hacker commented Oct 16, 2025

I think this only explain the first half of the theory and it is not proportial but inverse propotional,
...
High-speed flight requires smaller control deflections to achieve the same rotation rate due to aerodynamic damping and increased control effectiveness.

Agreed.
The lift force from the control surface (the dynamic pressure) at a given AoA is proportional to (0.5 * V)² .
(Assuming barometric pressure etc doesn't change)

Therefore the deflection needed to generate the force is ~ inversely proportional. (Given assumptions).

So no need to bring a simulated throttle into it.

THAT said, you actually wrote the dang code. And it works, presumably. I didn't write it and probably wouldn't, so whichever way you want to write it is fine by me. I'm somewhat regretting having said anything because you do great work and my comment wasn't pointing out a real problem that needed to be fixed. It just felt awkward to do a "virtual throttle".

Ps I had mentioned inversely proportional to (0.5 * V)², I see why you said inversely proportional to V itself. I see V is probably more correct for correcting for external disturbances, whereas V² would be for initiating maneuver, for starting a a turn. (If I understand correctly).

On an unrelated topic - I wanted to let you know I'm hoping to cut an INAV 9.0RC1 in the next couple of weeks. Hopefully with airspeed PID attenuation included. I sent a build with this plus some other PRs over to James Downing for testing.

@shota3527
Copy link
Contributor Author

shota3527 commented Oct 16, 2025

I see V is probably more correct for correcting for external disturbances, whereas V² would be for initiating a maneuver, for starting a turn. (If I understand correctly).

I think V is more important for continuous control stick input , and V² is more important for everything else including disturbances and starting a turn.

I am changing my mind. considering only V, then the TPA formula can be much simplier
δ = δref * Vref / V

but have some promlems.

  1. no uperlimit, hard code or config will introduce complexity
  2. the real situation might be some where between V² and V, and the formula could not mimic that
  3. there might be deflect in the control link

While the virtual throttle method has some extra parameters to toggle, or add a new parameter/modify δ = δref * Vref / V

curiousto know how others think

@sensei-hacker
Copy link
Member

sensei-hacker commented Oct 16, 2025

I love that I have someone to discuss this with who understands it better than I do. :)

the real situation might be some where between V² and V, and the formula could not mimic that

It occurs to me that V^1.3 or V^1.4 will always be in between. I think V^1.3 would probably be a good compromise between the two.

Of course, if you didn't want to do a decimal exponent, something like (0.5 * V) ^ 2 would also give a compromise value in between V and V² 😜
(Assuming the velocity is faster than a snail).

no uperlimit, hard code or config will introduce complexity

The baseline is not having any airspeed-based attenuation at all, so IMHO capping the attenuation at 4 * vref or so would be pretty good. It's not like it explodes at the cap. It just doesn't attenuate any further. I don't think the exact cap is critical. Of course one could smooth out the saturation by multiplying by (10 - Vfactor) / (Vfactor + 8) or whatever. (Where Vfactor = current airspeed / Vref).

@sensei-hacker
Copy link
Member

sensei-hacker commented Oct 16, 2025

there might be deflect in the control link

Yeah I expect the deflection will be approximately linear to the force (0.5V)^2 given the material is reasonably sized for the application.

In the end, I suspect that's about the difference between V^1.3 and V^1.35.

@trailx
Copy link
Contributor

trailx commented Oct 16, 2025

I got to do a preliminary test flight of this today. I am particularly interested in this because I've wanted it for a couple years. Currently I fly the three PID profiles based on custom programming logic that auto changes control profile based on airspeed. I have tuned these breakpoints as shown below (ignore the best-fit curves). These profiles are tuned fairly high to provide the best performance and stabilization without inducing oscillations. I've put many hours of flight on these settings without issues, and these PIDs are well established. Also I am using the Matek digital airspeed sensor.

Airspeed TPA Program

To test this PR, I applied airspeed TPA to profile 2, and added a switch that would force that profile over the airspeed-based changes. Profile 2 was tuned for use between 45 and 55 mph with no TPA in effect. So I applied the reference airspeed at 55mph, and the TPA throttle reference (BP) at 1500, which is roughly the throttle that achieves 55mph.

I'll talk through my flight today. I set up the TPA % at 35 to start.
Airspeed TPA Initial Setup
Above is the view as I started flight. I increased throttle to understand how the current setting operated, and I ran into roll oscillations around 66 mph airspeed. In flight, I adjusted the TPA number to try to push the oscillation point to a higher airspeed. I increased it up to its maximum, 100, and it didn't seem to have much affect on the airspeed in which I encountered oscillations. Oscillations always seemed to occur around 66-70 mph.

I expected increasing TPA to progressively attenuate PIDs and delay oscillation onset to higher speeds. However, changing it from 35% to 100% didn’t significantly affect the oscillation airspeed, suggesting the attenuation scaling or mapping may not be strong enough.

I also tried moving the BP or TPA throttle reference up and down, from 1380 to 1605 and it always seemed to oscillate around the same airspeed, 69 mph-ish. I was hesitant to change this a whole lot because I didn't really understand what effects it would have on flight performance.

I never ran into any oscillations or issues at lower airspeeds, only higher airspeeds. It was hard to tell, but it seemed like it may have a little more stability when in my profile 1, than when I locked in profile 2.

Because TPA is largely an invisible adjustment behind the scenes, its difficult to fully understand and visualize what's going on inside it while flying, and because airspeed is mapped on top of throttle, it makes it difficult to understand the attenuation curve when looking at the current graphs.

If you've got any advice for how I should adjust the "TPA levers", I can do another test flight tomorrow to test anything different. My takeaway is that 100% TPA doesn't seen to be enough. I'd love to get a better idea of what the formula is that I applied, and what that looks like on top of the stepped profile program that I use today.

BTW, I am James Downing FPV on discord.

@trailx
Copy link
Contributor

trailx commented Oct 20, 2025

I did some further digging on this, and I attempted to visualize the airspeed TPA math directly to my stepped curve. I think I uncovered something.

First, I realized I had something wrong in my earlier graph. In my second step profile, my Roll-P (which is what was likely causing my roll oscillation) is actually higher than I have depicted. I have it set at 46. So I marked that on the graph below with the green dot. That's at the reference airspeed, 55 mph.

I then graphed in red and orange the two curves I flew. Starting with 35% TPA factor, and then again at 100% TPA factor. I encountered an oscillation at roughly 65mph and 70 mph. Those two points are shown purple and annotated. A rough "local max" slope line can be inferred by these two points. It shows that the ideal curve needs to be steeper than what is provided.

I found what look to be two potential solutions, both graphed below.

The preferred solution I believe is to remove the reference to idle throttle (or more accurately pull that down to 0). This is shown in the blue line. Unless I did something wrong in my math, its not doing what we want it to. Its really the cause of the slope limitation. When I set that to 0, I get a curve that looks much more like what is intended, and its very close to the "local max" slope in that airspeed range.

An alternative approach could be to increase the TPA %. I increased it to 300% and was able to create the grey dashed line. I don't think this is as ideal, but it would be preferred to at least have greater control on the effective slope.

There's also a non-zero potential I got my math wrong and translated this incorrectly, but this matches my experience. Hope this helps shed some light on what I saw.

Airspeed TPA Curve Graph

@trailx
Copy link
Contributor

trailx commented Oct 21, 2025

I need to apologize, the graph I originally produced didn’t sit right with me. The curves didn’t match the logic I expected and saw from the code and other reference material, they were far too linear looking. After re-checking the math carefully, I found some errors. The overall takeaway about why oscillation occurs was still correct, but my earlier recommendation was not.

Below is the corrected airspeed-TPA graph based on the exact formulas pulled from the code (double checked this time). The red and orange lines and purple points still represent the same things in my prior post, but they show the correct overall shape. 100% TPA attenuates more than I showed before, but not significantly more. This increased the slope of the "local max" roll-P term calculated based on the oscillation points I encountered. The low-speed is boosted much more than I calculated last time, and I show the 2x cap on the graph rather than leaving it unbounded.

image

My conclusion has changed. We simply need a higher dynamic TPA range in order to better match the curve of the dynamic pressure effects. Planes with looser tuning may not reveal these effects when testing. However on my tightly tuned Penguin, the mismatch is noticeable. If I had to make an educated guess at the best dynamic pressure curve based on the data I have available, I think it would be in the range of 200-250% TPA.

I recommend opening the bounds for the dynamic TPA factor. Instead of limiting it to 0-100%, it needs to be 0-300% at minimum, but it may make sense to open this up further to accommodate and anticipate future needs. I also recommend the removal of the lower bound of 1/2 attenuation factor. If I had a lower bound of 1/2 TPA factor, there's a chance I'd still run into an oscillation at or above 100 mph. Difficult to extrapolate this, but I'd recommend lowering the lower scaler bound to 1/4 TPA. I think the upper bound of 2X can remain.

Thanks for your time on this. I think with these small tweaks it will be ideal.

Recommendations:

  • Increase allowable TPA dynamic factor from 0-100% to 0-300%, or even as much as 500%.
  • Remove the lower attenuation limit of 1/2, replace with 1/4.
  • Keep current upper bound of 2x.

@trailx
Copy link
Contributor

trailx commented Oct 22, 2025

In speaking with Jetrell, I realized some minor clarification would be helpful.

In my last post, I referred to the "TPA dynamic factor", for lack of the correct term. The correct term is tpa_rate. So to clarify my recommendation is to raise the upper limit of tpa_rate from 100 to at least 300, with 500 giving more headroom as needed to accommodate different unforeseen setups.

Second, I recommended moving the lower attenuation limit from 1/2 to 1/4. This is a hard coded sanity limit. I added an annotation on the graph with the dotted blue lines, showing where my "theoretically ideal" 225% tpa_rate line would be otherwise truncated at 1/2 with today's limit. This could potentially create oscillations at very high speeds, 100mph+ because it didn't allow enough attenuation.

The recommendation is to adjust the following line in pid.c from:
tpaFactor = constrainf(tpaFactor, 0.5f, 2.0f);
to:
tpaFactor = constrainf(tpaFactor, 0.25f, 2.0f);

image

@sensei-hacker
Copy link
Member

sensei-hacker commented Nov 3, 2025

I'm looking to get INAV 9RC1 cut very soon so that 9.0 can be released in time to flash Christmas gifts with it.

Based on the above discussion about using an exponent of around ^1.3 or ^1.5, and the testing by @trailx , do you expect to make further refinements on this soon?

@Jetrell
Copy link

Jetrell commented Nov 3, 2025

My tests showed the current tpafactor constraint of 0.5 attenuation to be okay on planes with lower control surface throw limits, and at speeds up to around 150km/h.. But beyond that speed, or if the airplane has larger control surface throws. Then attenuating the gains by 50% was not enough.
I'd agree with James. It could do with the gains being attenuated by 75%. Which lowers them to only 25% of their tuned value, when at the TPA_breakpoint.

And due to this. I also think it might be good to have the tpafactor broken out as a user adjustable setting, within that new constraint.

Just to add a bit more. I think it would also aid attenuation/boost response time if Down Pitch was accounted for in the calculations. I noticed there was occasionally a little bit of lag in the virtual airspeed. Likely GPS velocity related.
But if it knew an attitude change was commanded. That could maybe bring on attenuation/boost faster.
When testing this. It should also be noted that fw_tpa_time_constant ideally should be set to zero, Otherwise it will add to this lag.

Another thing that would be helpful for tuning. Is if the TPA gain changes could be shown on the OSD Outputs. So the user can see the attenuated or boosted gains being adjusted real-time while in flight. This would certainly help while trying to get the base gain tune more accurate etc.

@shota3527
Copy link
Contributor Author

shota3527 commented Nov 6, 2025

@sensei-hacker
I have update the codes to use ^1.2 as default with 2x(low speed)~0.3x(high speed) limiter. and seems ok in SITL.

@shota3527
Copy link
Contributor Author

Update the pitot LPF Hz default value to 1hz, default 0.35hz is lagging

@trailx
Copy link
Contributor

trailx commented Nov 6, 2025

Looks like you changed the code to no longer map to throttle. If I followed the math right, the proposed ^1.2 default looks like its effectively really close to my proposal of 225%. Purple line is new power function. Is this power value adjustable in the air? I didn't look that far into the code, but that's typically how I tune.

image

Thanks! Looking forward to flying it. Hopefully tomorrow.

@Jetrell
Copy link

Jetrell commented Nov 6, 2025

@shota3527 Thanks for the extra commits.. You've ticked a lot more boxes with its handling.

However, I don't mean to come across ungrateful for your work here. But integrating pitch angle based on + or - from gravity vector, would help in a GNSS failure or if a reduction in airspeed accuracy occurs in aerobatic maneuvers, which Turnrate can't detect either... Or the pitot tube gets partially blocked.
My reasoning is this. With this addition, users will now likely have the base PID tune at the TPA_breakpoint, set much tighter than before. Which would cause a much higher probability of control surface oscillations, if airspeed sensor inaccuracy occurs.

It's in the case of a dive condition that the current fixedwing TPA doesn't work correctly. Because throttle can be low and airspeed will still rise in a dive.
So due to this fact. If the plane either goes into a nose downward dive, either aerobatically by a stick command, whether from right way up or inverted flight. Or a command that comes from the nav controller. Or even a loss of control leading to a nose down dive,.
This detection of pitch angle with either an increase in acceleration or a timer, should certainly indicate that airspeed will be rising. Which should be a safe fallback for sensor failure, and also fix the limitations of the current fixedwing TPA implementation. What is your view on this ?

@trailx
Copy link
Contributor

trailx commented Nov 7, 2025

I got two flights in today on this PR. I'll try to keep my notes short. Wind was between 10-15 mph according to both feel and windspeed estimator in INAV. Once again I took off into my airspeed-driven 3-profile setup. I had a switch that would lock the middle profile, which is the one that had airspeed-pid-attenuation power at 1.20. Reference airspeed was set to 55 mph, where profile 2 maxed out previously.

First flight, overall everything performed as expected. Low speeds worked flawlessly, showing excellent control at low speeds even in the wind. I did run into some oscillations in the 75+ mph range, but I noted that my pitot was under-reporting airspeed. I have both GPS 3D speed and airspeed on the OSD, and airspeed was consistently low. I've had a consistent struggle with this and am working on tuning the scale properly. Assuming this was the issue, I landed and adjusted the scale.

Second flight, the airspeed was more inline with expectations at normal flight speed, while over-estimated at low speeds. I think the FC restart in windy conditions disturbed the zero offset. That said, the oscillations were gone. I did a few steep power off and power on dives without issue.

Overall, this flew extremely well, and I'm looking forward to getting my pitot sensor dialed in a little better, and further adjusting things as needed. I'd like to try flying with the virtual pitot too.

TLDR:

  1. Ensure airspeed sensors are calibrated well, otherwise if its under-reporting airspeed, it may not attenuate well enough at higher speeds, but this isn't an issue with this PR, just a general implementation note.
  2. Being able to adjust the airspeed PID attenuation power, reference airspeed, and pitot scale in-flight via the OSD adjustments tab would be useful improvements.

Thanks for all your work on this!

@shota3527
Copy link
Contributor Author

In case of climb/dive situation, how about based on current throttle based tpa, offset the throttle value by "pitch to throttle value"(i forget the variable name but it is in navigation settings). Then hopefully no additional parameters needed.

I am also thinking about raise the default tpa value from 0, if it is also 0 in the default presets.
Then user just need to set the fw_reference_airplspeed. Then it is mostly ok.

@Jetrell
Copy link

Jetrell commented Nov 7, 2025

In case of climb/dive situation, how about based on current throttle based tpa, offset the throttle value by "pitch to throttle value"(i forget the variable name but it is in navigation settings). Then hopefully no additional parameters needed.

@shota3527 Yes, good thinking. It didn't occur to me to use nav_fw_pitch2thr parameters in this way. But it should do the job fine, being that it does produce the effect required.
I assume you would use its base function and continue to scale the pitch2throttle output all the way to +- 90° with an exponential curve.

@trailx
Copy link
Contributor

trailx commented Nov 8, 2025

pitch2thr is typically only tuned for climb performance up to the max angles because this is only used for automated flight modes. I've got pitch2thr set to 10 (not sure if that's the default or not, I don't remember). If we use pitch2thr as an unconstrained multiplier, and assume the ref throttle is set to 1500, I'd essentially max out the TPA effect at 50 degrees (50*10=500, gets you to 1000 or 2000). Or potentially get as much as a 900us effect with a vertical dive or climb. That seems excessive to me and would get truncated in many cases to max boost or min attenuation levels - for instance a power off 90deg dive would be equivalent to almost full power level? I mean, maybe, but it really depends on the plane and how well its tuned.

Notable is the physics as you dive steeper - the effect that gravity has isn't linear, especially as you get to very steep dive angles - its added effect is minimal for every additional degree of tilt. It may make sense to apply a sine function over top of the pitch2thr, roughly shown in the screen shot below (the 57.29 is just there to turn radians into degrees). This would naturally taper the pitch2thr effect at steeper angles while still compensating linearly as expected in the typical range.

It would end up something like: effective_pitch = sin(pitch_angle / 57.29) * 57.29 (I'm sure theres a better way to do the radian/deg math) which then gets to multiplied by pitch2thr.

image

@sensei-hacker
Copy link
Member

sensei-hacker commented Nov 9, 2025

It may make sense to apply a sine function over top of the pitch2thr, roughly shown in the screen shot below (the 57.29 is just there to turn radians into degrees). T

Agreed. The force of gravity accelerating the aircraft (replacing motor thrust) is proportional to the sine of the angle.
Linear is close enough up to about 30-40 degrees, which is fine for adjusting the actual throttle with nav_fw_pitch2thr - you aren't going to reduce it below zero. :)

@trailx
Copy link
Contributor

trailx commented Nov 14, 2025

I've flown the current implementation on my Penguin a few additional times since my last update, and it flies great. No more need for my stepped PID attenuation program. Today's flight had very little wind, and I was able to confirm the calibration on my airspeed pitot. With that correct, I didn't run into any oscillatory issues, but those oscillations could also have been adjusted by applying a higher attenuation power. Again, it would be nice to be able to adjust this in flight, but I don't feel its imperative for inclusion on 9.0 RC.

I am curious if the 1.20 power on Airspeed TPA will be a constant for all planes, but I do believe its a solid starting point, and luckily - its adjustable.

TPA meaning Throttle PID Attenuation does seem like a bit of a misnomer now. I'm going to propose we keep "TPA" though, since its now embedded in the lexicon of INAV/BF, but I propose we change its interpretation to mean "Tuned PID Attenuation". So Airspeed TPA meaning "Airspeed-Tuned PID Attenuation" still makes sense. Certainly more sense than "Airspeed Throttle PID Attenuation". Standard Throttle TPA can then be referred to as Throttle-Tuned PID Attenuation. I think it would be best to differentiate it as "Throttle TPA" in any documentation updates.

@rts18
Copy link

rts18 commented Nov 15, 2025

My friends and I have also been testing this feature over the last week. From what we can tell it works good.
The only issue we've found is surrounding attenuation delay when sharp turns or loops are made. And I also found attenuation was slower and sketchy if the plane was diving or climbing at a high angle greater than 70°. This virtual airspeed delay effecting gain reduction could be seen in one of the guys data logs. It wasn't much. Only about 450ms. However it was enough to cause a slight ringing or oscillation on roll and pitch traces when the gains are tuned tightly at reference_airspeed.

@trailx Again, it would be nice to be able to adjust this in flight

@shota3527 I agree with this. Tuning is noticeably more time consuming. It would be awesome if airspeed_tpa_pow was broken out in the OSD and Adjustments for inflight tuning,, as TPA_rate is. And possibly have fw_reference_airspeed broken out as TPA_breakpoint is also.

@MrD-RC
Copy link
Member

MrD-RC commented Nov 15, 2025

AFAKI, this isn't to replace TPA. But to be a better option if you can use it.

So my recommendation would be to leave TPA as TPA. Which isn't a misnomer if it remains as it was. This should be called something like SPA for Speed PID Attenuation, or APA for Airspeed PID Attenuation.

Also, adding adjustments and the appropriate OSD elements should be pretty simple. It would also be nice to see the setup to this in Configurator above or below the TPA settings.

@anti-vaxxer
Copy link

MrD-RC:
or APA for Airspeed PID Attenuation.

I was wondering why this new feature was still being called TPA if T means throttle... but yes I think APA makes better sense, or since Air Speed can be abbreviated as AS, ASPA would be another option.

@shota3527
Copy link
Contributor Author

shota3527 commented Nov 16, 2025

updated cli variable name to apa
enhanced the old tpa method with pitch angle awareness

@Jetrell
Copy link

Jetrell commented Nov 17, 2025

@shota3527 Thanks for your work on this.
I went over your commits and did my best to translate its function into tuning info for the release notes. Let me know if it covers everything you intended it to. Or I left anything out ?

https://github.com/iNavFlight/inav/wiki/PID-Attenuation-and-scaling

Edit: Have a look at the part I wrote as a caution. What is you view on this with the use of high power airplanes ? Or have I missed something in TPA/Pitch angle operation.

@trailx
Copy link
Contributor

trailx commented Nov 17, 2025

Lots of info in there @Jetrell. I don't know what the right mechanism would be for providing feedback on different parts. There are a few small items I see. Overall I appreciate the detail, though it may be bordering on too much information for the average user and may result in overload.

Since you asked specifically about the caution about planes with high thrust to weight, where did the referenced 1.3:1 power/weight ratio come from? The resolution implies that the TPA_rate would need to be adjusted, but I'd think the pitch2throttle would be the item you'd ideally adjust, right? I think if P2T is adjusted to maintain a constant speed during climb and dive, then it should be relatively well tuned. Until there's more time on it, I think the warning may be clearer if it states: while the system adjusts the PIDs proportionally through vertical pitch and dive angles, it may not be well tuned beyond climb and dive angles seen in typical autopilot flight profiles (+/- 30 deg).

Also, here are some reference images that should help visualize how the system adjusts the attenuation/gain curve depending on a few factors. I found the reference images provided previously to be really helpful to help me understand what was going on behind the scenes.

image image

@Jetrell
Copy link

Jetrell commented Nov 17, 2025

@trailx You'll notice the Wiki is broken into 4 categories at the top. The middle two are related to these changes.

  • Multicopter TPA
  • Fixedwing TPA and Pitch Angle
  • Fixedwing APA
  • Fixedwing TPA (old)

The 2 new Fixedwing categories do not over lap in their operation. This is why their operation is explained separately.
The only time the APA method will revert to the TPA and pitch method while in flight. Is if the airspeed source becomes untrusted.
The reason being. Is using gravity based pitch angle to essentially predict/augment GPS vertical velocity inaccuracy for a time, if EPV is poor. Would require INAV to have a predictive filter like AP's EKF. So I understand why Shota did not implement such a crossover function between APA and Pitch angle.. It would also require a more in depth tuning method.

The one with the Caution is Fixedwing TPA and Pitch angle category. Not APA - apa_pow / fw_reference_airspeed
I went through and explained how Pitch angle and Throttle should collectively influence dynamic PID control. The down pitch side would seem fine.
But my concern is the UP pitch side, when used on planes that can climb vertically at speed, with an obvious high throttle value required to do so.
From what I can see in the code. It will use the high pitch angle to override high throttle (TPA) in that case. Which will allow the gains to be boosted, not attenuated. Which works well for planes that can't climb vertical for long and eventually run out of thrust; this will keep the PIDs tight as the planes slows before a stall..
But for planes that can climb vertical with good speed. And the TPA_rate is set high to account for the base gains being tuned tight at the TPA_breakpoint. It will likely result in control oscillation.

I wanted to hear whats @shota3527 view on this is.

where did the referenced 1.3:1 power/weight ratio come from?

It takes a thrust to weight greater than approximately 1.3 : 1 to be able to hold a continuous vertical climb rate. Even to hold a good hover or prop hang, you require greater than 1 : 1 . Or you will have no thrust overhead to maintain it.

may be bordering on too much information for the average user and may result in overload.

Maybe. But what isn't included is bound to be asked by someone when they experience a certain condition related to how one of those methods operate.

@trailx
Copy link
Contributor

trailx commented Nov 18, 2025

@trailx You'll notice the Wiki is broken into 4 categories at the top.

I saw that, I think the layout makes sense and the separation is pretty clear. I think we're aligned on how it all works. Personally, I just absorb diagrams better than long descriptions. But I agree that nearly all of the content is necessary and helpful if someone reads it. But to a new user, it may feel abstract.
Please feel free to include the TPA and APA graphs I worked up above (or provide feedback, I can adjust them). They are just to represent how the various factors will change the adjustment, and how it all interacts. I think it would be helpful alongside your wiki.

Visually representing how pitch2throttle influences the TPA curve is tricky, but maybe we can come up with a diagram to help visualize that too.

But my concern is the UP pitch side, when used on planes that can climb vertically with an obvious high throttle value required to do so. From what I can see in the code. It will use the high pitch angle to override high throttle (TPA) in that case...

Ok, so the 1.3:1 is a general rule of thumb for planes capable of vertical flight.

I agree that high pitch angles can produce oscillations. Adding pitch2throttle certainly helps the pitch-based TPA behave better, but I still think vertical flight profiles are outside the regime this method was really intended for. While P2T will influence the effective “synthetic throttle,” it won’t fully correct behavior once you’re far beyond typical climb angles.

For the caution, maybe wording like below would address the concern more directly:

While the TPA-with-Pitch system adjusts PID gains proportionally through the full pitch range, it may produce unexpected results or oscillations at extreme nose-up attitudes (beyond 30–40 deg).

In your description of nav_fw_pitch2thr, you mention that:

“the ideal value required for the calculation of this function is 10 or 11.”

I’m curious why you believe 10-11 is ideal, because I think the optimal number will vary between different airframes. It may help to describe how users can tune this variable when integrating into pitch-based TPA:

  • If the aircraft develops oscillations during climbs, it suggests the pitch-based gain is too strong. Users could try reducing nav_fw_pitch2thr.
  • If oscillations occur during dives, it suggests the pitch-based attenuation is too weak. In this case, users could try increasing nav_fw_pitch2thr.

@shota3527
Copy link
Contributor Author

shota3527 commented Nov 18, 2025

I agree with @trailx with

  • Remove the lower attenuation limit of 1/2, replace with 1/4.
  • Keep current upper bound of 2x.

Some recent PNP models' power combinations have a very wide speed range. from 50km/h to 130km/h, even 200+km/h in the dive. I don't think the current 2x-0.5x range is sufficient in theory. but one thing needs also keep in mind is the deflection in the linkage/sarvo will reduce the throw in high speed. more Attenuation might lead to insuffcient actual throw in control surface

And I believe the default values of these may also require some adjustment.
This PR has the apa range of 2x-0.3x

@shota3527
Copy link
Contributor Author

For this document, i suggest to change the order of APA and TPA on the FW side.
https://github.com/iNavFlight/inav/wiki/PID-Attenuation-and-scaling

I think the virtual pitot-based APA is better than TPA, and it should always be used if available. I think the airspeed estimate is accurate, even GPS 3D speed along is better than TPA. Additionally, TPA has more parameters to tune. Who knows the relationship between the throttle/pitch angle/weight/inertia of the plane?
For APA, PID tune the plane at the reference air speed, and then it is almost done with the default 1.2 power.
For tpa with pitch. Maybe to go safe, instead of flight performance.
The latest code of this PR have master branch merge. The LFP Hz on the pitot are raised to reduce the delay in the pitot.

@Jetrell
Copy link

Jetrell commented Nov 18, 2025

For this document, i suggest to change the order of APA and TPA on the FW side. https://github.com/iNavFlight/inav/wiki/PID-Attenuation-and-scaling

No problem.

I think the virtual pitot-based APA is better than TPA, and it should always be used if available. I think the airspeed estimate is accurate, even GPS 3D speed along is better than TPA. Additionally, TPA has more parameters to tune.

I agree. However, due to TPA and Pitch angle being a fall back if the airspeed source fails while using APA. It should also be tuned..
As I mention here. This is the only concern I have with the TPA and pitch angle method form the last commit. The rest looks good.

The one with the Caution is Fixedwing TPA and Pitch angle category. Not APA - apa_pow / fw_reference_airspeed
I went through and explained how Pitch angle and Throttle should collectively influence dynamic PID control. The down pitch side would seem fine.
But my concern is the UP pitch side, when used on planes that can climb vertically at speed, with an obvious high throttle value required to do so.
From what I can see in the code. It will use the high pitch angle to override high throttle (TPA) in that case. Which will allow the gains to be boosted, not attenuated. Which works well for planes that can't climb vertical for long and eventually run out of thrust; this will keep the PIDs tight as the planes slows before a stall..
But for planes that can climb vertical with good speed. And the TPA_rate is set high to account for the base gains being tuned tight at the TPA_breakpoint. It will likely result in control oscillation.

@shota3527
Copy link
Contributor Author

Yes, throttleAdjusted will decrease when the pitch is up. A 2000us throttle will decrease to 1100us for calculation with the current default value, and then the PID might be too high.
I think for such a powful plane, first should be tuning nav_fw_pitch2thr. 10 or 11 is too much for such a powerful plane

@Jetrell
Copy link

Jetrell commented Nov 18, 2025

I think for such a powful plane, first should be tuning nav_fw_pitch2thr. 10 or 11 is too much for such a powerful plane

Due to this being a shared variable. We have to be cautions of how much this setting is altered, without it effecting the navigation climb/dive throttle. Meaning that the user would also have to retune all other setting related to pitch2thr for navigation.

@trailx
Copy link
Contributor

trailx commented Nov 18, 2025

My penguin is overpowered, and the default pitch2thr would result in it exiting automated flight climbs at a very high rate of speed. Right now its at 8 and climbs well, if maybe still a little too strong. (In RTH I like it to pop up pretty fast) The difficulty is the non-linearity of throttle % vs airspeed, and the users just need to be aware that the addition of the pitch calculation is probably limited somewhat in its effectivity to the automated flight pitch limits (its not, but it effectively may be).

Tuning pitch2thr so that when in automated flight the plane does high angle climbs and dives roughly at a constant speed may be a good prerequisite to tuning TPA.

@rts18
Copy link

rts18 commented Nov 19, 2025

@shota3527 After reading yesterdays conversation. I took one of my planes with a 2:1 thrust to weight out to test it with commit 00c9db0.
I specifically focused on testing the "TPA with pitch angle awareness" modification.

As it would appear in the logic and raised by Jet. The PIDs are boosted at high climb speeds, when the throttle is significantly above the tpa_breakpoint, using a high thrust output plane. This lead to control surface oscillations.

I proceeded to alter the nav_fw_pitch2thr. It showed minimal influence over this situation, as did the fw_tpa_time_constant filter.

I'm aware setting variables are a limited hardware resource. However cutting corners on such an important feature as this is not the way to go.
This feature requires its own "throttle_pitch angle" setting.
The way nav_fw_pitch2thr is used for navigation throttle, is not at all compatible with the requirements of the "TPA pitch angle" function, because of the different ways both can be tuned. As I believe @MrD-RC would agree.

@shota3527 I would like to encourage your efforts towards implementing such an overdue feature. ❤️
It makes an extreme difference to fixedwing stabilization performance. However please don't leave this feature with edge logic holes in it. Obi-wan, your our only hope.

What about adding a tpa pitch scaler variable that could be adjusted by the user if their plane has a high thrust to weight. It could reduce the effect PID boost would have on such aircraft at a high climb speed/angle, without altering the dive angle attenuation function.
You could also use barometric vertical airspeed as a secondary reference. Both the air speed sources, if GPS virtual and the barometer are unlikely to fail at the same time. If they do, the altitude estimation will also be stuffed.

I have not forgotten that APA is still the best approach. But this method is also required to work well if the air speed source fails.

How about this extrapolation? Adding fw_tpa_pitch_scaler for over powered planes. And BaroVerticalSpeedas the speed source.

{ float getBaroVerticalSpeed(void); 
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)) {
        fpVector3_t vForward = { .v = { HeadVecEFFiltered.x, -HeadVecEFFiltered.y, -HeadVecEFFiltered.z } };
        float groundCos = vectorDotProduct(&vForward, &vDown); 
        
        float pitchAdjustmentFactor = currentBatteryProfile->nav.fw.pitch_to_throttle * groundCos * 90.0f; 

        float verticalSpeed = getBaroVerticalSpeed(); 
       
        float baroAdjustment = 0.0f;

        if (fabsf(verticalSpeed) > 100.0f) 
           baroAdjustment = verticalSpeed / 10.0f; 
        }

        int16_t combinedAdjustment = (int16_t)constrainf(pitchAdjustmentFactor + baroAdjustment, -1000, 1000);
        
        uint16_t throttleAdjusted = rcCommand[THROTTLE] + combinedAdjustment;
        tpaThrottle = pt1FilterApply(&fixedWingTpaFilter, constrain(throttleAdjusted, 1000, 2000));
    }
    else {
        tpaThrottle = rcCommand[THROTTLE]; 
    }
    return tpaThrottle;
}


@trailx
Copy link
Contributor

trailx commented Nov 19, 2025

@rts18 , I don't know how tightly you have your PIDs adjusted, but did TPA+pitch work for a certain range of pitch angles? At what angles, speeds, and throttles did you run into oscillations?

How far did try you adjusting pitch2thr? I have to assume that at 0, it would not create this oscillation. I also assume that at 1, it will likely not create the oscillation. Academically, at some point, you should be able adjust the pitch-up-TPA-gain oscillation out of the system by adjusting the P2T.

But at that point - where it no longer creates oscillations on a climb - does it create other issues from pitch2thr being too low?
Does it start oscillating in a pitch-down attitude?
Does it no longer maintain airspeed when climbing in automatic flight?

Rather than going on a hunt for new solutions to fixing TPA, at what point do we try to adopt something more akin to the "basic TPA" solution that was developed by betaflight?
betaflight/betaflight#13895

I don't think we need to consider betaflight's advanced method, because APA fills that gap, arguably better.

I'm also a bit concerned that we won't find a solution to TPA that works in all situations - and APA gets held up. I don't want perfecting TPA to hold up the implementation of APA, and at least getting that improvement out to people in 9.0.

@rts18
Copy link

rts18 commented Nov 20, 2025

At what angles, speeds, and throttles did you run into oscillations?

Climbing vertical at 80 km/h @ 85% throttle. It has a 25.2v 700w power system.
Read my last post again. The goal was to find if the issue @Jetrell raised in the wiki was of concern to some users. Isn't that the reason for testing. Not just to prove your own model works. But to ensure the logic works for others as well.

Does it start oscillating in a pitch-down attitude?

No, why would it. Have you not read how the logic works. Down pitch works as it should. pitch angle overrides raw throttle and attenuates the gains if the plane is in a low throttle dive. Instead of boosting them as the old fixedwing tpa used to.

Does it no longer maintain airspeed when climbing in automatic flight?

No it won't. This was the whole point of my last post.
It will practically stall when commencing a RTH climb when nav_fw_ptch2thr is set lower than 8. And I wasn't going below 6 in case I required RTH in that flight.
This was my whole point, when using nav_fw_pitch2thr for this function as well as using it for navigation climb/dive. The functions it is being used to adjust are incompatible.
For example. If nav_fw_pitch2thr is set to a low value like 8, at a 22° climb angle. That only provides 176us of throttle increase between nav_fw_cruise_thr and nav_fw_max_thr. Which is far too little to be useful. Using nav_fw_pitch2thr at default is the minimum I would select for tuning its use for RTH or WP for most users.

I'm also a bit concerned that we won't find a solution to TPA that works in all situations - and APA gets held up. I don't want perfecting TPA to hold up the implementation of APA, and at least getting that improvement out to people in 9.0.

Dude you worry too much. What's the good of merging something that has a known and real life tested bug.
As has been said by myself and others in this thread. APA works fine if you don't encounter an airspeed source failure. Then your plane will go into the worst control surface flutter you could imagine, if the TPA and pitch angle isn't sorted. Don't forget that base gains will now be tuned far tighter then before.

When things don't get cleaned up before a merge, you end up with bugs everywhere that never get addressed.

@MrD-RC
Copy link
Member

MrD-RC commented Nov 20, 2025

My 2 cents. nav_fw_pitch2thr should not be used as an APA tuning parameter. It is a navigational tuning parameter, which is separate from the APA system.

APA can be effected by the results of nav_fw_pitch2thr. But it should have its own apa_pitch_compensation (or whatever the appropriate name is) parameter for turning the APA. It should not be tied to the navigation.

@trailx
Copy link
Contributor

trailx commented Nov 21, 2025

Does it start oscillating in a pitch-down attitude?

No, why would it. Have you not read how the logic works. Down pitch works as it should.

You're right, I wasn't thinking. And yes I fully understand how the logic works. I was really trying to understand the details of what you experienced. Down-pitch may over-attenuate, but you're right over-attenuation isn't the concern, it just becomes less stabilized. The concern is over-boost where it can generate oscillation.

I think there's another potential issue when switching to TPA and simply flying level at high throttle because it may not attenuate enough because TPA's curve is flatter than APA.

Does it no longer maintain airspeed when climbing in automatic flight?

No it won't. This was the whole point of my last post. It will practically stall when commencing a RTH climb when nav_fw_ptch2thr is set lower than 8.

It wasn't completely clear in your last post, so I wanted to understand. I've got my P2T down to 7 now and it still maintains speed in climb. I think I could probably go lower without issue. I may have my cruise throttle set proportionally higher than you to stay further away from stall.

Dude you worry too much. What's the good of merging something that has a known and real life tested bug. As has been said by myself and others in this thread. APA works fine if you don't encounter an airspeed source failure.

Maybe you're right, its my engineering brain mitigating schedule risk. To me, APA is the single most important fundamental fixed-wing feature that I've been wanting and advocating for over the last couple years. That said, TPA has never controlled gains appropriately on a fixed wing, and I don't think it will ever function as well as APA. If it did, we wouldn't even need an APA solution.

So that’s why I’m concerned about a fundamental mismatch here that may not have a solution:
APA enables tuning much higher PIDs than TPA can safely handle, but when TPA suddenly takes over during an airspeed dropout, it may lead to either over-boost at high angles of attack, or under-attenuation at high throttles. Both leading to oscillations.

But, I realized last night that there may be a simple workaround for anyone using APA. I think you can avoid almost all TPA-fallback oscillation issues by setting tpa_breakpoint to a throttle value significantly below the throttle required to maintain the reference airspeed used by APA. For example, on my penguin, looking at the graphs, I’m thinking I'll actually set it to 1000, while it flies at the ref airspeed at about 1450.

Doing this accomplishes several things at once:

  • If APA fails, throttle-TPA immediately drops into the attenuation region.
  • This prevents TPA from trying to boost already-high APA-tuned PIDs.
  • It attempts to address the known issue that TPA does not attenuate strongly enough at high throttle.
  • It also limits the boost region that would cause issues during high pitch-up flight.
  • It guarantees that fallback behavior is “safeish” (attenuated vs APA) rather than destabilizing by effectively applying scaled down PIDs across the throttle spectrum.

This basically “chops” the PID gains if APA drops out. No code changes needed, just documentation of best practices, and this only becomes an issue if people start tuning higher PIDs, I doubt theres any issue when flying defaults.

@trailx
Copy link
Contributor

trailx commented Nov 21, 2025

The following graph overlays APA’s adjustment factor with TPA’s adjustment factor on roughly equivalent X-scales between throttle (top) and airspeed (bottom) for my particular airframe. Obviously this will vary from plane to plane (and airspeed and throttle are not linear) but this gives a reasonable visual comparison and shows the fundamental flaw.

If you tune your PIDs to be very stable (but not oscillatory) at ~50 mph / 1500 μs, you can see the mismatch that appears between APA (green) and TPA (dotted red), even with 100% TPA. At any point beyond their common setpoint. TPA is simply unable to attenuate gains enough. Simply divorcing pitch2thr from TPA+pitch doesn't solve this.

On the opposite end, if you pitch up hard enough to drive throttle into a region where TPA-boost would exceed APA’s factor, then you hit the other issue we’ve been discussing, its just not as visually evident.

If you lower the TPA breakpoint to 1020 μs, you get the yellow curve. As far as I can tell, it still bottoms out at 0.5, due to the 0.5f term in the code:
tpaFactor = 0.5f + ((float)(currentControlRateProfile->throttle.pa_breakpoint - getThrottleIdleValue()) / (throttle - getThrottleIdleValue()) / 2.0f);

(Oh yeah, and I dropped the idlethrottle to 1000 in this graph, which may not be ideal... when breakpoint and idle throttle invert, the graph does strange things.)

Here's the reference graph, with what I think is a decent workaround, and the way I will set up my plane for now (yellow) to mitigate the issues we've discussed:
image

@rts18
Copy link

rts18 commented Nov 21, 2025

I think you can avoid almost all TPA-fallback oscillation issues by setting tpa_breakpoint to a throttle value significantly below

That won't have any effect on the reported condition because the throttle is still above the breakpoint no matter how far.
The inverse throttle value used when climb pitch angle is added to the equation, will still cause the same result on powerful planes that can climb at a high pitch angle, higher throttle and therefor higher speed, leading to the PIDs being boosted.
Such planes as these require this fix more than others, due to their wide flight speed envelope.

@trailx Please stop distracting from the problem with workarounds. Open a chat in the Discussions section. shota3527 is a capable developer. He will fix the issue when he has time.

There are two issues.

  • pitch2thr is not compatible for tuning of both tpa pitch angle and navigation throttle. This function requires its own setting variable.
  • TPA assisted pitch angle requires a secondary means to determine vertical velocity, as I originally mentioned.

@trailx
Copy link
Contributor

trailx commented Nov 22, 2025

Let's not discourage active participation... I agree with you that the workaround I mentioned isn't the final solution, but openly talking through the issues is part of the process of solving it. I think if we put it simply, if TPA is the fallback, the solution needs to ensure TPA's gains are never above APA's.

After more thought, I think I can boil my thoughts down to two code recommendations, and one general setup recommendation:

  • The TPA curve shouldn't bottom out at 0.5. The curve is simply too flat and thats part of the basic problem with TPA, but it only ends up with inadequate attenuation at high throttle. When I graphed it out with a low breakpoint, I realized its actually asymptotic at 0.5. Unless I read it wrong and graphed it incorrectly, I believe this is a core deficiency within TPA.
  • TPA+pitch should ignore pitch-up effects entirely and only attenuate pitch-down angles. The goal is not perfecting TPA, but to ensure its flyable as a fallback in all conditions without inducing oscillations. Boosting gains at pitch up has minimal benefit, and has the biggest chance of inducing oscillations. I don't think its needed.
  • Setup recommendation: If flying APA, I think a clean solution is still to set the TPA breakpoint low just to automatically attenuate the PIDs if airspeed source was to fail, and even it TPA is tuned poorly, the hope is simply that it won't induce oscillations.

I'd like to test the fallback functionality directly... is there a clever way to force APA to dropout via a switch? Best I can think is setting up two identical control profiles, and setting APA_pow = 0 on one of them?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants