-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Attenuate i-term based on pidsum limit #13506
base: master
Are you sure you want to change the base?
Conversation
Do you want to test this code? You can flash it directly from Betaflight Configurator:
WARNING: It may be unstable. Use only for testing! |
fixes #13486 |
This PR is fascinating! It has caused me think about fundamental things that few have thought about for a long time :-). Thank you! Background.... The current Usually motorMix reaches around 1.0 at a pidSum of 500. If the iterm_windup factor is at default of 85%, iTerm cannot accumulate to more than 425, approximately, if pidsum_limit is 500, since If pidsum_limit is set, like pidSum on yaw, to 400, and if yaw is the sole source of PID activity, then motorMix for that axis can't exceed 0.8. This means motorMix on that axis doesn't quite reach the required value of 0.85, and hence iTerm can still accumulate, possibly all the way to the iTerm_limit of 400! I think that the reason why the iTerm limit is 400 on yaw is to leave a bit of 'wiggle room' for unimpaired movements on the other axes, in situations where the yaw pidSum is very big. A limit of 400 means that yaw cannot saturate the motors completely. pidSum can still change up or down by another 100 before the motors saturate. Otherwise I'm not very sure why the limit on pidSum is set lower than roll and pitch. This PR provides an alternate path for suppression of iTerm growth, per axis, in proportion to the pidSum_limit for the axis. This would prevent that possibility of large increases in iTerm on yaw during constrained yaw moves. The main downside is that PR does maths every PID loop on each axis, and derives two very similar numbers but multiplies and constrains both of them separately (four times in all per PID loop). Also I realised that our separate Hence this suggestion.... Perhaps we calculate
and then make a per-Axis pidSum based limiter, as suggested here, but use iTerm_limit as the limit factor, returning a value that goes from 1 through zero to some negative value. Then we get the lowest of these, multiply by the threshold inverse, and limit to 0-1... something like this:
This should return the same values as the PR code, with very much simpler maths. On an F7X2 the above code saves 24 ITCM bytes compared to master, instead of adding 240bytes. Since our existing iTerm_limit is 400, and since axisCi will be zero anytime pidSum reaches 400, and since iTerm is always less than PIDsum, we have a functional limiter on iTerm at 400, that's why new don't need a constraint to iTerm_limit anymore. And functionally if P is already say 100, then iTerm can't grow higher than 300. This will help avoid adding a lot of iTerm on impacts where both P and D can be high, and in the same direction. Because motorMix and pidSum are closely linked, it may be possible to remove the iterm_windup / motorMix maths entirely. Large heavy yaw-sluggish machines could then get effective yaw iTerm windup suppression - and indeed on other axes - by using a lower iTerm_limit, instead of using iterm_windup. That would be a nice simplification. |
Found a bug while testing the modified values. As iTerm_windup approaches 100, there should be less and less effect. This is achieved by having the However, in the specific case of To avoid this, the code needs to be:
|
@tbolin please rebase - as it should build now. |
84c42b6
to
8b20cb0
Compare
done! |
How about implementing suggestion from Chris? We are about to release. |
itermWindupPointPercent is limited to between 30 and 100 (in cli https://github.com/betaflight/betaflight/blob/8b20cb096943f82eab1108a606fd6d6959bfe963/src/main/cli/settings.c#L1135C142-L1135C165), so the way I read the code setting itermWindupPointPercent = 100 is the only way to turn the limit off. Not intuitive but I suspect it is working as intended. If setting *itermWindupPointPercent * to 100 should effectively make the attenuation a yes/no the default should be a lot larger than 0. |
I'm not sure this code results in exactly the same behavior. There are some cases where the output is not limited but the i-term would still be attenuated (e.g. if i_term_limit < pid sum < pid_sum_limit then the i-term would still change with the PR code, but be attenuated with the suggested code, as far as I can tell), I don't have an opinion on which behavior is preferable in the long run, but I think the the PR behavior changes the attenuation behavior less while still fixing the issue. Larger changes should be a separate PR IMO. |
Agree with separating fix and improvement here. |
Hi again - yesterday I had time to flight test the modifications I suggested previously. Tested with iTerm limit down to 100. Unable to detect any change in flight performance, or PIDs, though I don't think I ever came close to hitting any iTerm or pidSum limits in-flight. Has been bench tested and definitely works. This squashed branch on my GitHub shows my suggestion for changes from master. This branch shows one commit of changes over this PR code. This redraft prevents iTerm winding up in excess of iTerm limit, since the amount that can be added to iTerm goes to zero as pidSum approaches its limit. If there is no P or D or FF, iTerm can reach the iTerm limit. Otherwise, if other PIDs are active, iTerm accumulation falls, ultimately to zero a value if its contribution to pidSum would exceed the iTerm limit. This prevents iTerm windup above iTerm limit under any conditions. So it's basically a kind of dynamic iTerm limit, and will prevent iTerm windup under conditions where previously iTerm would wind up. I think this is likely to fly a lot better than before, and should prevent iTerm windup in susceptible machines quite well. The only 'quirk' is that there is an interaction between the user's iterm_windup value and the nature of the limiting. There is a 'slope' to the limiting behaviour that depends on the value of iterm_windup - a kind of soft-knee limiter. This contrasts with, and is preferable to, the hard limiting of the old code.
I like the idea of having a softer knee rather than hard limiting. We have a soft limit for the motorMix element in dynCi. With defaults I don't think anyone will notice the soft limiting of iTerm, and I think that generally it would be better like that. The soft knee have some practical effect only if iTerm weight is very low, and if also iterm_limit is low. For example, setting an iTermLimit of 100 with iterm_windup of 30 means iTerm will only accumulate normally while pidSum is less than 30, which is a very low value. It will then be attenuated to zero when pidSum is 100. Keep in mind that an iTermLimit of 100 constrains iTerm to no more than 20% of the motor range, which is a very strong constraint. If they want to us iTerm limit at such values in the new code, they probably won't want to use iterm_weight at extreme values. Note that iterm_weight is itself influenced by pidSum. In fact if pidSum approaches 500, motorMix will approach 1.0. These two factors effectively achieve very similar results. In logging there is not a lot to choose between them. The benefit of having both factors is that if P alone was to push pidSum to 350, with motorMix at 70%, the dynCi factor would be normal, and in the old days, iTerm itself could still accumulate to 400, whereas the pidSum factor method would limit iTerm to only 50 while P was that high. So we do need both factors, for sure. This redrafting requires less ITCM RAM than master, and involves fewer computations, and with the added benefit of soft-knee limiting, makes the modifications very attractive. I didn't update the unit tests. @tbolin please test with your windup-prone whoop. The debugging changes are temporary for review purposes; the factors can be seen in sensors tab with a whoop. |
I made a crude emulation of my suggested code on Desmos. The horizontal red line shows the functional limit to iTerm for given values of iTermLimit, pidSum, and motorMix. The iterm_windup value can be entered with a slider. |
Looks like some major impact on control side. Is that just tested by one person shortly before releasing 4.5? Maybe I misunderstood the functionality of the changes, but can it lead to crashes? |
@BRNKR no, this won't cause crashes. In any case, the best way to help out is to load it onto a quad and fly it. Can be an indoors whoop for instance. Or do some validation of the debugs via sensors tab. @tbolin I haven't heard back from you, and there are people keen to release RC4 with this fix in place. We need to resolve the code. Perhaps I should make a separate PR with the alternate method I propose, so that it can be tested? I made an updated emulation to play with, which:
With this we can see how if the pidSum limit is set low, motorMix cannot reach the value needed to suppress iTerm accumulation. From doing the emulations, I think there is a second 'bug'... If the user configures pidSumLimit to be less than iterm_limit, eg to 350, then the existing method will never get any iTerm_windup attenuation, because the motors will never exceed the threshold. This also affects the pidSum method, because if it is limited below iTerm_limit, we will never get full pidSum based attenuation, either. So I think we need to add a check in pid_init.c to give precedence to the pidSumLimit, in the form of: pidRuntime.itermLimit = MIN(pidProfile->itermLimit, pidProfile->pidSumLimit); That fixes this specific issue. It clearly makes no sense to permit an iTerm limit in excess of the overall pidSumLimit. Also....
Adding the pidSum based attenuation, that problem disappears. |
On further testing, I start to wonder if we should, with this change, remove the old motorMix / dynCi method completely. motorMix considers the combined effect of pidSum on all axes, once mixed, on all motors. It isn't 'axis-specific'. The new pidSum based method is axis-specific. Emulation testing shows that on a single axis, the modified pidSum method always suppresses iTerm more strongly than motorMix. I think that iTerm windup problems should properly be considered on a per-axis basis, alone. I'll test this out. We can make the dynCi value always 1.0. The |
@ctzsnooze sorry for not getting back yo you. Life got in the way. |
Implements an additional check to prevent i-term windup if the pidsum for an axis is too low to saturate the mixer.
If the pidsum limit for the axis is >= 500, the behavior should be identical to the current mixer range only anti windup mechanism.
see #13486 for details.