Skip to content
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

Open
wants to merge 21 commits into
base: master
Choose a base branch
from

Conversation

ctzsnooze
Copy link
Member

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:

  • P is constraining error within ez_landing_limit deg/s
  • I has its rate of change is reduced by limiting the incoming error ez_landing_limit deg/s; accumulated iTerm does not change.
  • D cannot exceed 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.

Copy link

Do you want to test this code? You can flash it directly from Betaflight Configurator:

  • Simply put #13488 (this pull request number) in the Select commit field of the Configurator firmware flasher tab (you need to Enable expert mode, Show release candidates and Development).

WARNING: It may be unstable. Use only for testing!

@ctzsnooze ctzsnooze requested a review from limonspb March 31, 2024 06:24
@ctzsnooze ctzsnooze added this to the 4.6 milestone Mar 31, 2024
@tbolin
Copy link
Contributor

tbolin commented Mar 31, 2024

Some initial thoughts (I'll try to get some testing in later):

  • I like the idea of an optional auto disarm, but I think it would be better as a separate PR. Now both the code and testing efforts gets intermingled.
  • Initially the idea that not adjusting the already accumulated i-term sounds good, but after thinking abut it a bit more I'm not sure it will make much of a difference. I'll try to elaborate when I'm fully awake.
  • Wouldn't it make more sense to limit the sum of the P and D terms, rather than individually?
  • It's much harder to reason about how much lift the quad can currently generate for a given set of ezLanding settings compared to either of the previous versions.
  • (not just about this PR but, I'm starting to doubt some of our earlier reasoning about more and less stability and bounciness. Both, especially bounciness, seems to be closer to either/or phenomena. On a quad with a hover throttle just bellow 20% I can't tell a difference between an ez_landing_limit of 5% and 15%, I'll have some more testing to do but I suspect that going even a little bit above hover throttle will be very noticeable).

@ctzsnooze
Copy link
Member Author

Hmm... overflowing ITCM RAM, probably the acc.accADC code... what to do about that?

@tbolin
Copy link
Contributor

tbolin commented Mar 31, 2024

some initial impressions when testing los with a tiny whoop and auto disarm off

  • After the first landing the i-term started winding up and the quad started to slide across the floor with sticks centered and throttle at zero (confirmed by looking at the log). Landing in it self was not bouncy though.
  • Throttling down doesn't seem to allow the quad to un-stick after it has vacuumed up to a wall anymore.
  • Keeping debug field 1 as effective throttle would have made analysis easier. It's a great way to check how much
  • Touch and go skidding along the floor was a lot bouncier than with the mixer based EzLanding implementation. The quad bounced more and would react strongly to getting snagged.
  • The quad felt bouncier when colliding with objects. Looking at the log there are several moments where ez land factor is at 25% and the motor difference is at 100%, indicating that EzLanding is not doing anything to reduce thee bouncines at that point.

I think the last point is due to a problem with PID based approaches that I overlooked before.
The output will be limited per axis, so to have a guarantee the same authority as a mixer based approach on an axis the maximum allowed throttle increase will be three times as high on a PID based approach.

@ctzsnooze
Copy link
Member Author

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.

@ctzsnooze
Copy link
Member Author

@tbolin OK so I sort of felt a bit negative after your report, and kind of figured this was crap :-)
So I went for a swim down at the beach and after that I figured I'd try reducing the rate of iTerm accumulation by a factor of about 5 from what it was.
And discovered that I'd wrecked the disarming in some recent commits, so I fixed them. And improved the calculation of the Acc error for disarming - it wasn't correctly subtracting 1G from the vector, so that instead of zero meaning 'no impact', 1 meant 'no impact'.
And then I went wandering the house testing thresholds with a quite aggressive 3in.
Interestingly I think the Acc is slow or filtered or something. If I bounce on carpet, it disarms quite readily, even with a threshold of 10. If I bounce clean and straight down on hard tiles, it just bounces up, and doesn't disarm.
But overall I thought it was now very very good. Didn't seem to have the bad behaviours I saw in earlier testing. I did reduce the default limit to 15 and set the disarm threshold to 100, but the key thing was to not let iTerm accumulate too much.
Anyway I'll recommend it to you now, have a try on your whoop. I think landing should be quite nice. And there was really no problem with bouncing, anything bad would disarm, but it did have to be fairly bad.
It's a nice test bed for impact disarm testing.
Its annoying that it overflows on some boards.

@tbolin
Copy link
Contributor

tbolin commented Apr 1, 2024

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
Say that the P + D term is limited (and from my experience they contribute a lot to the immediate bounce) per axis to 10%.
If the quad touches down with both front arms first the motor output becomes

  • FR := 1 * -0.1 = -0.1
  • FL := 1 * -0.1 = -0.1
  • RR:= -1 * -0.1 = 0.1
  • RL:= -1 * -0.1 = 0.1
    Total motor range: 0.2
    Minimum effective throttle: 0.1

However if one of the front arms touches down first we instead get

  • FR := 1 * -0.1 + -1 * 0.1 = -0.2
  • FL := 1 * -0.1 + 1 * 0.1 = 0.0
  • RR:= -1 * -0.1 + -1 * 0.1 = 0.0
  • RL:= -1 * -0.1 + 1*0.1 = 0.2
    Total motor range: 0.4
    Minimum effective throttle: 0.2

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.

@tbolin
Copy link
Contributor

tbolin commented Apr 1, 2024

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.
I did have some problems with false disarms though, but if combined with the button method (or just worked on a bit more) it might be good at protecting ESCs, motors, and the grass if a quad falls over during landing.

@ctzsnooze
Copy link
Member Author

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.

@ctzsnooze
Copy link
Member Author

Made a quick raw video here:
https://www.youtube.com/watch?v=4LTCOkUZVgA

@@ -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)
Copy link
Member

@KarateBrot KarateBrot Apr 2, 2024

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.

  1. Function naming: the intent is not sending something, it is to get the value of a variable, so getXYZ is better suited.
  2. 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.
  3. Placement: This function definition belongs to the rc.c file (not pid.c)
Suggested change
float sendMaxDeflectionAbs(float maxRcDeflectionAbs)
float getMaxRcDeflectionAbs(void)

Copy link
Member Author

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?

Copy link
Contributor

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).

Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Contributor

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) {
Copy link
Member

@KarateBrot KarateBrot Apr 2, 2024

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.

Copy link
Member Author

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.

Copy link
Member Author

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.

return maxDeflectionAbs;
}

static void disarmOnImpact(const int axis)
Copy link
Member

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.

Suggested change
static void disarmOnImpact(const int axis)
static void disarmOnImpact(void)

Copy link
Member Author

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.

// 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;
Copy link
Member

@KarateBrot KarateBrot Apr 2, 2024

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.

Suggested change
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;

Copy link
Member Author

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.

Copy link
Member Author

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.

@SupaflyFPV
Copy link
Contributor

SupaflyFPV commented Apr 2, 2024

Made a quick raw video here: https://www.youtube.com/watch?v=4LTCOkUZVgA

Hi Chris I saw your video come up in my feed and it was enjoyable, and I will aim to test this PR ASAP

@tbolin
Copy link
Contributor

tbolin commented Apr 2, 2024

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.

P and D are not much of an issue except in very uncommon circumstances.

Such as D-term runaways?

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.

Or, you know keep the current ezlanding code in there. If I want to hit a switch I can just disarm and rearm.

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.

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
https://www.youtube.com/watch?v=LzSZ-DyS90w (not my video, just found it on youtube)

@ctzsnooze
Copy link
Member Author

@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.

@tbolin
Copy link
Contributor

tbolin commented Apr 3, 2024

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.

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
no_i_term_acrov2_001.zip
The quad responded by promptly flipping over on the spot as if it had the props on the wrong way. Probably a user error, just never seen the quad do that before.

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.
Just realized that it is the anti yaw spin function kicking in.

I haven't tested the auto disarm yet since I wanted to test the bounciness first, but I am looking forward to it.

@SupaflyFPV
Copy link
Contributor

SupaflyFPV commented Apr 3, 2024

Here is a log from a 3.5" freestyle build

I used the defaults:

ez_landing_threshold = 25
profile 0
Allowed range: 0 - 50

ez_landing_limit = 15
profile 0
Allowed range: 1 - 100

ez_landing_disarm_threshold = 0
profile 0
Allowed range: 0 - 250

btfl_001.bbl.zip

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....

@ctzsnooze ctzsnooze changed the title Ez landing by PID method with auto-disarm Ez landing by PID input Error Limit method, with auto-disarm Apr 6, 2024
@SupaflyFPV
Copy link
Contributor

SupaflyFPV commented Apr 6, 2024

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.

EZTest2.bbl.zip

It took me a while to get my head around the parameters:

  • ez_limiting: 'More' (harder limiting) is a lower number- I would have expected higher number = more

  • ez_limiting_threshold: More sensitivity is usually lower threshold - I'm still slightly confused but it seems like a higher number = more easily triggered limiting activity


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:

  • What is more and and less of a behaviour/parameter
  • flashed settings should be defaults to avoid confusion
  • on and off setup
  • ideally we have the parameters in the OSD so we can tweak all parameters with a lipo connected and quickly work thru min/max settings and get a balance...

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"....!

@ctzsnooze
Copy link
Member Author

Thanks @SupaflyFPV.
I've re-factored and changed defaults slightly, after flying my 5in. Disarm is at 110 and limit is at 30.

To explain the factors:

  • ez_landing_threshold is the stick position in percent at which the EzLanding effects end. Above that stick position, EZL has no effect at all. Hence, with default of 25, any stick position more than 25% out, the quad behaves entirely normally. The EzLanding behaviour starts at 25% stick position, with zero effect there, and gets stronger when sticks are both close to zero. So if you want to make it work over a very narrow stick range, you would reduce this value. 25% keeps the transition smooth, but 15% flies well also.
  • ez_landing_limit, in this PR, is the maximum amount of error allowed through into the PIDs when the EzLanding effect is maximal. If set to 30, it means that errors up to 30 deg/s will always be handled entirely normally. Since most hang or drop error rates are lower than this, hangs and drops are barely affected, even with throttle hard zero and sticks centred. In fact, most of them rarely have errors that exceed 15 in a well tuned quad. So a limit of between 10 and 30 is OK. The lower the limit value, the more the PIDs are suppressed because their inputs are limited. The naming is correct. It's like pid_sum_limit. Below it, things are normal. Anyway when any stick moves outwards from zero, this limit is relaxed. At the point where ezLanding first kicks in (stick angle greater than 25 degrees by default), the limit is 500 deg/s of error, which is usually sufficient to saturate the motors). When sticks are out more than 25% (or whatever your stick threshold is), there is no limit on error, like normal.
  • ez_disarm_threshold is a value in G * 10 for which the smoothed accelerometer value triggers a disarm. Default is 110 or 11G's. This works mostly at the right time. Disarm can only happen when sticks are less than 5% out from zero (sticks must be hard zero/dead centre). Also, the code looks for less severe bounces, and zeroes iTerm when they happen. The idea here is that for medium bumps we want iTerm to be zeroed out, if it was high, since otherwise it will be slow to go away.

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.

@SupaflyFPV
Copy link
Contributor

Thanks @SupaflyFPV. I've re-factored and changed defaults slightly, after flying my 5in. Disarm is at 110 and limit is at 30.

To explain the factors:

  • ez_landing_threshold is the stick position in percent at which the EzLanding effects end. Above that stick position, EZL has no effect at all. Hence, with default of 25, any stick position more than 25% out, the quad behaves entirely normally. The EzLanding behaviour starts at 25% stick position, with zero effect there, and gets stronger when sticks are both close to zero. So if you want to make it work over a very narrow stick range, you would reduce this value. 25% keeps the transition smooth, but 15% flies well also.
  • ez_landing_limit, in this PR, is the maximum amount of error allowed through into the PIDs when the EzLanding effect is maximal. If set to 30, it means that errors up to 30 deg/s will always be handled entirely normally. Since most hang or drop error rates are lower than this, hangs and drops are barely affected, even with throttle hard zero and sticks centred. In fact, most of them rarely have errors that exceed 15 in a well tuned quad. So a limit of between 10 and 30 is OK. The lower the limit value, the more the PIDs are suppressed because their inputs are limited. The naming is correct. It's like pid_sum_limit. Below it, things are normal. Anyway when any stick moves outwards from zero, this limit is relaxed. At the point where ezLanding first kicks in (stick angle greater than 25 degrees by default), the limit is 500 deg/s of error, which is usually sufficient to saturate the motors). When sticks are out more than 25% (or whatever your stick threshold is), there is no limit on error, like normal.
  • ez_disarm_threshold is a value in G * 10 for which the smoothed accelerometer value triggers a disarm. Default is 110 or 11G's. This works mostly at the right time. Disarm can only happen when sticks are less than 5% out from zero (sticks must be hard zero/dead centre). Also, the code looks for less severe bounces, and zeroes iTerm when they happen. The idea here is that for medium bumps we want iTerm to be zeroed out, if it was high, since otherwise it will be slow to go away.

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
ez_landing_mixer(or pd/gain)_limit

would make things easier

@ctzsnooze ctzsnooze mentioned this pull request May 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

Successfully merging this pull request may close these issues.

None yet

6 participants