-
-
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
Ez landing by PID input Error Limit method, with auto-disarm #13488
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! |
Some initial thoughts (I'll try to get some testing in later):
|
b9dbc23
to
171ae81
Compare
Hmm... overflowing ITCM RAM, probably the acc.accADC code... what to do about that? |
some initial impressions when testing los with a tiny whoop and auto disarm off
I think the last point is due to a problem with PID based approaches that I overlooked before. |
4657fab
to
171ae81
Compare
Thanks for testing, @tbolin ! I agree with all your observations. Could I suggest that you test with a 'limit' of 10? It is a lot less bouncy at 10 than at 25. 25 is barely noticeable in normal flight, I think it is too high for whoops in particular. Mostly, controlling P and D is quite easy. In your logs you'll see that they contribute little or nothing to the bouncing. The problem is iTerm. If the machine already has some substantial iTerm on initiation of EzLanding, and that iTerm is maintained (as in this PR), then the pre-existing motor differential from iTerm will continue. This generates lift, making the quad a bit 'weightless', which adds to bouncing and skidding. And, if during the impact, or on landing, there is a period of time with error, limiting the input error magnitude isn't as effective in limiting the outcome for an accumulator like iTerm. That's why if whoop was stuck to the wall by iTerm, if we maintain iTerm, it will stay stuck. At the same time, maintaining iTerm's effectiveness while in flight is what keeps the quad's attitude stable when doing drops and dives. For P and D, limiting the 'input' side is really effective. But iTerm is a significant problem. Before 'giving up' on this approach, I'll look more at iTerm. While this approach appears to interfere less in flight, it doesn't provide the same benefits on landing, that's for sure. However, as I said before, please try lower 'limit' values - eg 10. PS: I realised that AntiGravity boosts both P and I temporarily at the time of a throttle chop, and needs to, in order to maintain stability in such situations. We boost P about 4-6 times and iTerm input gain by 2-3 times. Hence if a a user comes in to land, hovers with some decent throttle amount, and then chops throttle hard, they would get an AntiGravity boost on P and I just at the time frame that they impact the ground! And if there is an impact 'wobble' around that time, it can lead to a significant amount of I being added, which then (with this code) won't go away. Unfortunately we need AntiGravity in flight for faster quads at high speed. Perhaps whoop bouncing would be a whole lot less with AntiGravity off. Wall sticking would also be much worse with AntiGravity on, since the P and I boost would coincide with the user's instinctive throttle chop. Whether AntiGravity plays a big part in stabilising whoop flight, that I don't know. But it's worth turning it off to check this aspect. |
@tbolin OK so I sort of felt a bit negative after your report, and kind of figured this was crap :-) |
I know that feeling, didn't mean to sound harsh but I was a bit tired. I thought about it a bit more and I don't think a PID side anti bounce solution will ever work that well
However if one of the front arms touches down first we instead get
So despite limiting each axis to 10% we get a worst case bounce as if we had 20%, and that's without adding in the I think it's possible to make a mixer side version that retains the i term, but I'm not sure the effort is worth it before there is a very clear and easily testable case where keeping the i term makes a difference compared to the current ezlanding. |
As for the auto disarm, I haven't tested it yet but I have some ideas: Maybe it could require that a button is held to trigger, then it could be made more sensitive while also guaranteeing that there isn't any false positives and mid air disarms. When you mentioned props getting stuck in grass I remembered some code I wrote a while ago that would disarm the quad if the props were stopped. It used the P and D term from dynamic idle and was fast enough for it not to hurt when I jammed my finger into of the propps on my tiny hawk. |
Thanks for your comments. Please test the current state of the branch on defaults. I think works much better than before now iTerm accumulation is much slower. At least in terms of landing. P and D are not much of an issue except in very uncommon circumstances. It was iTerm that was the problem. I was allowing it to change too fast. By suppressing it further, that problem is much less of an issue. Just because it is possible to get a lot of P even at 10% doesn't mean it's bad. I moved back to 15% though. P stabilises the quad on landing impacts, as does D. They are both fast enough to stop the quad toppling over if you land a bit sideways; the do the 'acute' corrections to stop it rolling over. It was the accumulated iTerm that over-did things and then caused a problem. The only issue perhaps is that if you actively fly in at an angle to the ground, landing like a jet plane, you may have accumulated iterm while in the air. This code will maintain that iTerm on landing, tending to maintain that angle, and that may not be what you want. If you come in fast like that over grass, auto-disarm is your friend, and the threshold should be low enough to trigger in that setting. The new disarm threshold is less intrusive, it only disarms in strong bounce situations. This PR remains unable to deal with a whoop after it is stuck on the wall. Making a mode switch that temporarily cut motor output would be an OK way to deal with that. You have time to hit a switch in those cases. The main reason for auto-disarm is that it is so much quicker than any manual method. It can be difficult to line up a perfect landing and then hit any switch, including disarm, at exactly the right time. The auto method is much more effective and easier. The trick is to adjust the threshold so that if the landing is good, no problem, you land nicely. But if you hit hard, and probably would topple over anyway, it disarms. For testing, set the disarm threshold at say 100 and then it will almost never trigger. Then drop down in steps of 10 until you get it happening on the hard ones. There is a threshold that really works well, coming to help you when you need it, and not otherwise, and you just need to find that value. We have "Crash Recovery Mode" already, which disarms in the event of PID system 'overload' indicating failure to control the craft. If set to disarm, that will disarm. We could enable that, but it is a bit slow, and requires very extreme errors, for what we want. |
Made a quick raw video here: |
src/main/flight/pid.c
Outdated
@@ -740,6 +741,48 @@ float pidGetAirmodeThrottleOffset(void) | |||
} | |||
#endif | |||
|
|||
// when new Rx data is received, update the maxDeflectionAbs value, so we don't do this every PID loop | |||
static float maxDeflectionAbs = 0.0f; | |||
float sendMaxDeflectionAbs(float maxRcDeflectionAbs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are several problems here.
- Function naming: the intent is not sending something, it is to get the value of a variable, so
getXYZ
is better suited. - Efficiency: This is calculating the absolute number on every function call. If you were to call this function multiple times in a loop (for example because you need this value in multiple places) the software would do many unneeded calculations. Better to just update a static variable once per loop and then just return the value when calling the function.
- Placement: This function definition belongs to the
rc.c
file (notpid.c
)
float sendMaxDeflectionAbs(float maxRcDeflectionAbs) | |
float getMaxRcDeflectionAbs(void) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The intent was that this function would run in pid.c only when rc.c had new data. It's run when rc.c's processRcCommand
gets true on isRxDataNew
, and not run at any other time. So in a sense the value is being 'sent' by rc.c, when rc.c has new data. That's why I have it the strange name, it's not a 'get' function in pid.c. So it runs infrequently, but when it does it updates the static value of maxDeflectionAbs
in pid.c.
You know more than I do of these things, I thought that the function sendMaxDeflectionAbs
was defined in rc.h, run in rc.c, and then in pid.c had to be instantiated again in order to work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These (inverse) dependencies are usually problematic. And it gets really ugly when mutexes are used (which may be BF case in future). In a lot of cases, it is easier to recalculate the value.
Quite clean implementation is to queue event from RC to PID. PID can then process received events at desired time, acting accordingly. But no such mechanism is available yet.
In this case, it may be enough to call pidNotify_RC()
after RC values are updated and recalculate the value there (using getMaxRcDeflectionAbs() call to get values from RC).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So... in rc.c
we make a static boolean for pidNotifyNewRcData
or something, and set this true in rc.c / processRcCommand()
on receipt of new data.
And the relevant EzLanding code then sits inside a check for if (pidNotifyNewRcData ()
, which checks every loop for true. If true when run from PIDs, they do the EzLanding stuff, and in rc.c where it was called, it gets set to false, waiting a transition to true?
Seems complex but would work. I'll try.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try a solution of that kind. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ctzsnooze : It is possible, but complex.
The idea is to make public (callback) function pidNotify_RC
(or pidNotify_onRcEvent
, pidNotify_onRcChange
etc.) in pid.c, that will update PID state after called by RC. Actual PID/ezLanding function will use internal PID-related state (maxDeflectionAbs, but there may be better name). Eventually, there may be more centralized/generic mechanism to handle this, but it will be easy to refactor.
In rc.c
, just call pidNotify_RC
when RC data change (in processRcCommand).
The idea is that RC code only knows that PID wants to be notified, but doesn't care about details. And PID/ezLanding code updates self when necessary, using public RC function, but not caring much about RC internals.
float accMagnitude = fabsf(acc.accADC[axis]) * acc.dev.acc_1G_rec; | ||
// *** annoying to need to normalise accADC here, should normalise acc.accADC once, in accelerometer.c, not everywhere we use it | ||
if (isAirmodeActivated() && maxDeflectionAbs < pidRuntime.ezLandingThreshold * 0.5f) { | ||
if (accMagnitude > pidRuntime.ezLandingDisarmThreshold) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can trigger on high accelerations (which might be intended and not meant to be detected as crashes). Acceleration is not a good measure for detecting collisions. Jerk - the derivative of acceleration - is better suited for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Earlier versions used the method in GPS Rescue, with root sum of squares, and that worked well. I didn't want to use a method that we knew worked reliably enough in GPS Rescue. This code would definitely make an easy place to test whether derivative of acceleration would be useful. Some raw accelerometer data can be very noisy, and that may affect the derivative and cause false triggering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Went back to sort(sum of squares) - 1, and check for. disarm only once per loop.
src/main/flight/pid.c
Outdated
return maxDeflectionAbs; | ||
} | ||
|
||
static void disarmOnImpact(const int axis) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be dependent on the axis.
static void disarmOnImpact(const int axis) | |
static void disarmOnImpact(void) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, you're right. Fixed.
src/main/flight/pid.c
Outdated
// value should be highe enough to avoid unwanted disarms in the air on throttle chops | ||
// note that unlike GPS code, this just compares one axis at a time, to reduce CPU load | ||
// for the Z axis, 1G gets added while 'flat', but that won't matter much given we are looking around 8G mostly | ||
float accMagnitude = fabsf(acc.accADC[axis]) * acc.dev.acc_1G_rec; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the acceleration's magnitude is the square root of the squared sums of all axes.
float accMagnitude = fabsf(acc.accADC[axis]) * acc.dev.acc_1G_rec; | |
float accMagnitude = sq(acc.accADC[X]) + sq(acc.accADC[Y]) + sq(acc.accADC[Z]); | |
accMagnitude = sqrtf(accMagnitude) * acc.dev.acc_1G_rec; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, but this code is called three times per axis - 9 times per pid loop.
And I think that if we look at each axis individually, the worst case is returning 0.7 when the vector is 1.0. That's not a big deal I think.
Otherwise I should take the disarm code out of the per-axis loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Solved by doing the calculation once per PID loop.
Hi Chris I saw your video come up in my feed and it was enjoyable, and I will aim to test this PR ASAP |
Tested and I agree that it's less bouncy. Still have a bit more bounce than I like. Especially hits on the yaw axis seems to make the quad freak out. Much more than on roll and pitch.
Such as D-term runaways?
Or, you know keep the current ezlanding code in there. If I want to hit a switch I can just disarm and rearm.
I can't say I see a lot of I-term accumulation before landings in the logs. And sliding seems to be a common use for ezlanding |
@tbolin... thanks again. The idea of this PR is that if it freaks out it should disarm. Setting a disarm threshold around 100 (10G) works well. Dterm runaways are totally hidden by this code. They would need to be extraordinarily bad at default limit of 15 deg/s error to make the quad take off. But of course as soon as throttle is raised, the quad would go to the moon. Dropping throttle again it should stop. I had fun skating around on the floor with this code, it worked well. I did notice iTerm buildup in master when motors were limited, and on slowly raising a stick the quad would turn in not quite the direction I wanted as the motors gave some 'bite' to the existing accumulated iTerm. This PR definitely doesn't do that, all stick inputs on the ground feel 'normal'. For stuck whoops, we could perhaps make a specific bit of code and tack it on to any ezLanding approach. We could perhaps use an ezLanding type check for sticks are centred and throttle down, and while sticks are in that state, look for motormix being maxed out for more than 1s, and constrain motormix to like 15% after that, while sticks are down. When sticks are up, off you go. Something like that should get it to fall off the wall and have no adverse effects otherwise, I can't think of many situations where motormix should be stuck high for a whole second at zero control input. People who do freestyle won't tolerate significant flight issues, and would probably not use an ezLanding method that interfered with flight. People who just cruise around but don't want to break a valuable quad on landing can accept code that does interfere with flight. With the PID option, if the limit is down around 7deg/s it barely bounces, but gets loose in drops and chops. At 25-30 it bounces a lot but is barely noticeable in flight. The user can decide what matters most, and its fairly easy to adjust accordingly. At this point, I don't know the best defaults for this code on different platforms, or if auto-disarm is safe. I do like the auto-disarm, and think a lot of people would like it also, but it can be added to either type of ezLanding code easily enough. I am at this point only throwing this code option up for people to experiment with. How we should move forward depends on user feedback, and whether this is actually an improvement or not. That's very unknown at this point. |
d042929
to
b4df043
Compare
In the example I calculated above I show that a 15 deg/s limit could give an effective throttle of 30% if it affects both pitch and roll, probably more if yaw somehow also get involved. That is also assuming that P and D are added before being limited. With P and D limited separately it can go up further. I also noticed that the i term windup prevention seemed to work well in most cases. I almost starting to think that the i-term prevention alone could be used. Might be possible to use independently or together with the mixer based version too. In flight 3 in this log I basically flew up a little bit, came down, and dropped the stick back to neutral, making it oscillate at about 25 Hz for a little while In flight 5, around 15.780, there is an event where EZ Land factor is at 14%, but the yaw i-term increases quickly (after first having been zeroed for a short while?). As I remember it there was a collision, the quad suddenly made a violent reaction and flew away in a direction I didn't expect, collided with something else and I had to disarm. This is what I was referring to when I said I found the yaw bouncy. I think there are some more similar events in the log. Flight 1 around 9.7 looks similar. I haven't tested the auto disarm yet since I wanted to test the bounciness first, but I am looking forward to it. |
Here is a log from a 3.5" freestyle build I used the defaults: ez_landing_threshold = 25 ez_landing_limit = 15 ez_landing_disarm_threshold = 0 If I understand it correctly I was looking at the PID error, to see how the landing bumps differed in peak value to some hard stop flips and rolls... Bump landings can be from 400 d/s to 900d/s and a hard flip can be up to around 400d/s Can the limiting be based on the rate of change of the pid error? Bumps are much sharper...I wouldn't want to limit my control of the quad or sacrifice Anti Gravity for this.... |
Hi Chris I really like this so far, see what you think about if its limiting the flight manoeuvres It's cool the way it kind of 'dead ducks' the landing here is a flight test on a 3.5" freestyle. I can try it on a 5" later...after going back and forth a bit - I decided to test the default values. In the PR flashes with limit at 5 when the default is 15. It took me a while to get my head around the parameters:
With regard to testing PR - is it possible we can separate the 'method of action discussion' - with test directions? For simple testing instructions we need:
This needs to be repeated at the bottom of any technical discussion and updated / repeated as it develops ...would make it way quicker and easier...then we can read the method of action and development story - which is interesting - but often needs a different time. Anyway I look fwd to ripping it proper on a 5"....! |
Thanks @SupaflyFPV. To explain the factors:
In flight, with a 5in race quad, battery below, in springy / bouncy grass, I found the PR at this point to work amazingly well. I honestly could detect no difference in flips, drops, inverted drops, the kinds of things that we want stability with all sticks zero. I had no disarms in flight, despite trying to do hard inputs at zero throttle (hard flip stops etc). Nor any detectable issues with the iTerm resetting itself in the air when sticks were hard zero. Most landings over grass were good. If I landed nice and smooth, the auto-disarm would not kick in; it would land gracefully and hold attitude well. If it bounced, usually it would be stabilise on the second bounce. If the bounce was bad, or if I drove it into the grass on an angle, iit disarmed. That's kind of what I wanted. Ideally if I drive it into the grass on an angle, I want it to disarm before it flips itself over. The only 'bad' thing was that I then started landing 'hard' knowing it would disarm for me :-) That's a bit wicked. Occasionally it would not disarm when I wanted; I need to test a lower threshold. With the higher limit of 30 as in this PR it would bounce a bit more. I When testing, please enable auto disarm, fly over grass, and find the lowest threshold value at which you felt your hangs and drops were no longer stable. It could be as low as 10. We want the lowest value that gives stable hangs and drops and zero throttle behaviour, because the lowest limit value will give the least 'reaction' to bounces on landing. |
OK that makes sense So I need to test for behaviour in the air at zero sticks....the only time I can think of I might do that is an inverted yaw spin - changing from one yaw direction to another...I see what you mean, we're not asking much of the pid controller usually in this case... I'd quite like to test the auto disarm on something with ducts...but might try some hard landing with a lower value. I assume this feature is to aid crash landings and avoid prop-chop around the floor? They are a bit longer but maybe the names: ez_landing_stick_threshold would make things easier |
6dab544
to
dae9d4d
Compare
I hate unit tests
also change defaults a little
dae9d4d
to
182e818
Compare
This PR is for evaluation of an alternative approach to 'ExLanding'.
** WARNING: The auto-disarm function is enabled in the PR by default, and only works if AirMode is active. Disable it with
set ez_landing_disarm_threshold = 0
if you aren't game to test that feature. **TESTING:
a) Testing the ezLanding stuff
First, do some gentle LOS landings over grass. Watch the landing behaviour. Gentle landings will not disarm. The quad may bounce but should retain its attitude. Harder landings, or landings while drifting, will probably bounce, and may topple over. If auto-disarm is enabled, it should disarm on bad landings. If auto-disarm is not enabled, and if the quad topped over and gets stuck inverted, or just gets stuck in grass, etc, the motors will spool up like usual, and you'll have to manually disarm. EzDisarm is actually pretty cool; I'd suggest testing with it on, to get a feel for what it does, and when it will vs won't auto-disarm for you.
Note that the disarm threshold is adjustable. It is defaulted to 2.5 times higher than the normal GPS Rescue disarm threshold, and uses the same maths. Note that ezDisarm is NOT intended to auto-disarm every time! In a gentle landing, it won't trigger, and the quad should sit nicely on the ground in a stable manner. EzDisarm is intended to disarm only if the landing goes bad. If you think it is too sensitive, increase the threshold until it only kicks in with bad enough landings where you really want it to disarm the quad immediately for you.
b) Testing the ezDisarm stuff
Assuming you've enabled ez_disarm, and use AirMode, test for unwanted disarms in-flight. Do this over soft grass at low altitude. Do some flips and rolls LOS at low altitude and check it doesn't self-disarm unexpectedly. Do a throttle pump and chop to zero, let it drop, lift throttle before it hits the ground. All these should feel normal and there should be no disarming.
If it does disarm mid-air from a hard throttle chop, leave throttle at zero, quickly toggle to Disarm and back to Arm, then throttle up and you'll be OK. I apologise in advance if something bad happens. Note that the disarm code needs AirMode to be active, and AirMode requires 25% throttle after arming, so you shouldn't immediately disarm again on re-arming, even if the quad is falling in a bad way. I think. This remains to be tested.
I think the most likely, and unpleasant, time for an unwanted disarm would be a hard throttle chop suddenly to zero at high speed. We have AntiGravity to keep the quad stable in these situations, but most quads will wobble quite a bit when we do this. This code weakens AntiGravity significantly, or at least I think it probably does, so you could get a significant wobble on chopping throttle hard to zero, and that could cause an unwanted disarm at speed, which might not be very pretty. Again, don't panic, leave throttle at zero and disarm/re-arm quickly.
It may be possible to code it so that most of AntiGravity corrections still happen, but I haven't explicitly examined that, and in any case, AntiGravity isn't perfect, or may not be enabled, so please take care when testing high speed throttle chops.
I didn't have anything bad happen to me but I'm definitely concerned that it might happen to you.
** How does it work **
Our current approach to ezLanding involves constraining throttle and/or motor output when sticks are centred and throttle is low. This work quite well, but has drawbacks.
This approach involves PID limitation, while retaining full motor authority.
The code can be disabled by setting the
ez_landing_threshold
is set to zero in the CLI.The optional simple auto-disarm for rougher landings can be disabled by setting it's threshold to zero. It uses the same detection technique as GPS Rescue, but with a default threshold set 2.5 times higher than for GPS Rescue.
When enabled:
ez_landing_limit
deg/sez_landing_limit
deg/s; accumulated iTerm does not change.ez_landing_limit
, in practice resulting in a constraint on the amount of D that is similar to the constraint on P via incoming error.Nothing else is changed. PIDsum and Motor output is not limited.
Even under full exLanding conditions, P and D will be normal for small movements and small errors, and will not react strongly to large errors or movements, eg impacts. As soon as the sticks rise above zero or await from centre, you quickly get full authority back. As little as 10% away from centre, or 10% throttle, provides almost totally normal 'feel' and strong opposition to un-commanded movements. Accumulated ITerm will stay approximately the same, and will only change relatively slowly. A large spike from an impact will not immediately change the accumulated iTerm, or result in a strong reaction from P or D.
Because accumulated iTerm is retained, and because errors in flat drops are often quite small, a flat drop, or inverted hang, or a dive at zero throttle stay relatively stable.
On the ground, the quad feels entirely 'normal'.
D-resonant flyaways on arming won't happen until the pilot lifts throttle; on cutting throttle, they should stop (didn't test this, but I think so).
If the pilot needs more stability in drops or dive type situations, increasing the
ez_landing_limit
value will help a great deal, however the amount of bounce or reaction to impacts on landing will be greater. If EzDisarm is enabled, however, bad landings should be a lot less damaging, and less hazardous for bystanders.Most race quads with batteries underneath are very unstable and difficult to land on grass without them leaning over and getting their props stuck, or without bouncing off the grass and flipping the quad over. Even with effectively zero P and D, they still bounce and fall over; high-performance quads are very 'light' and 'bouncy' due to high power to weight ratio and relatively high minimum rpm. So it's very hard to get a perfect landing, especially while drifting sideways. The EzDisarm function will be quite helpful for racers I think, and while racing throttle is never zero so you won't notice any drawback at all.
Flat bottom freestyle quads tend to land better, so a higher
ez_landing_limit
may still result in better landings, even with a higher than default EzDisarm threshold.Generally, iTerm turns out to be the main cause of landing problems. However I kind of like how this code retains the iTerm, since in a clean landing the quad just kind of sits still and perches, balancing on an underslung battery quite easily.
Anyway have a fly and see what you think.
I found that with the defaults, only antiGravity situations like hard throttle chops tended to wobble badly. But my quad wobbled quite a bit anyway in those situations. Definitely first do some hard throttle chops with normal code, or with ezLanding disabled, before testing it, otherwise you'll blame it for what the quad does anyway.
Please test with a beater quad, and let me know how you go. Haven't tested on whoops but should be fine on whoops.