EKF3 vertical velocity/position runaway with BARO + no VELZ, while RangeFinder contains the position error

Hi,

I am investigating a repeatable vertical estimator problem on a 3.5" 6S cinewhoop running ArduCopter 4.7.1.

I am sharing three original DataFlash logs showing two different behaviours depending on the vertical position source.

DataFlash logs

Log 01 – BARO runaway, EK3_ALT_M_NSE=0.5
01_BARO_RUNAWAY_ALT_M_NSE_0.5.bin
Download Log 01 from Google Drive

Log 02 – BARO runaway, EK3_ALT_M_NSE=2.0
02_BARO_RUNAWAY_ALT_M_NSE_2.0.bin
Download Log 02 from Google Drive

Log 03 – RangeFinder comparison / bad VD after landing
03_RF_ONLY_BAD_VD_AFTER_LANDING.bin
Download Log 03 from Google Drive

The vehicle has multiple EKF3 source sets configured.

During the BARO events discussed below, the selected source set uses BARO for POSZ and no VELZ.

The RangeFinder comparison uses RangeFinder for POSZ, Optical Flow for VELXY, and no VELZ.

I would appreciate some help understanding what is happening inside EKF3.

Hardware

  • FC: Flywoo GOKU H743 Pro Mini / STM32H743
  • Dual ICM42688P IMUs
  • External IST8310 compass
  • FlyFish M10 GPS
  • MicoAir MTF-02P RangeFinder / Optical Flow
  • AM32 ESCs with ESC telemetry

The relevant configured source sets are:


SRC2:
POSXY = None
VELXY = Optical Flow
POSZ  = RangeFinder
VELZ  = None

SRC3:
POSXY = None
VELXY = None
POSZ  = Barometer
VELZ  = None

For the BARO tests:


EK3_HRT_FILT = 2
GND_EFFECT_COMP = 1

Log 01 – clearest BARO runaway

01_BARO_RUNAWAY_ALT_M_NSE_0.5.bin

This log uses:


EK3_ALT_M_NSE = 0.5

During the relevant event, the BARO source set is selected.

The EKF vertical velocity (XKF1.VD) starts developing a significant error. XKF1.PD then begins to diverge even though the physical altitude indicated by the available measurements does not justify the EKF altitude change.

As the discrepancy grows, XKF4.SH eventually exceeds 1.

In this log, SH starts exceeding 1 at approximately 52.95 s.

About 5 seconds later, at approximately 57.95 s, PD makes a very large discontinuous correction of roughly 7.5 m, which appears consistent with the EKF height timeout / ResetHeight() path.

The concerning part is that the large height correction does not appear to recover the vertical velocity state. VD remains abnormal and subsequently becomes much worse.

The sequence appears approximately:


VD starts becoming incorrect
        ↓
PD diverges
        ↓
height innovation grows
        ↓
SH > 1
        ↓
height fusion becomes unhealthy/rejected
        ↓
~5 seconds
        ↓
large PD correction/reset
        ↓
VD is still incorrect
        ↓
vertical solution diverges again

EK3_ALT_M_NSE=0.5 was intentionally tested to see whether giving the BARO observation substantially more weight would prevent the divergence.

It did not.

Log 02 – independent BARO occurrence with ALT_M_NSE=2

02_BARO_RUNAWAY_ALT_M_NSE_2.0.bin

This is an independent occurrence using:


EK3_ALT_M_NSE = 2.0

Again, the BARO source set is selected during the relevant event.

This log is included mainly to show that the behaviour in Log 01 is not specific to the experimental EK3_ALT_M_NSE=0.5 setting.

Again, VD becomes incorrect and the vertical EKF position subsequently diverges.

So simply changing the BARO observation noise does not appear to address the underlying problem.

The BARO runaway does not appear to start from high vibration

I initially suspected IMU vibration.

However, in the BARO logs the vertical estimator problem begins while vibration levels are still low and there is no accelerometer clipping.

For example, during the developing divergence in Log 01:


VIBE Z ≈ 3–4
Clip = 0

The very high vibration and clipping occur later, after the vehicle behaviour has already deteriorated.

Therefore, the high vibration seen near the eventual crash does not appear to be the initial trigger of the estimator divergence.

I have not ruled out a lower-frequency accelerometer/bias issue.

Accelerometer Z bias may be relevant

Looking at XKF2.AZ, EKF3 is estimating a significant Z accelerometer bias.

Across these logs it is commonly approximately:


-0.2 to -0.4 m/s²

depending on the point in the flight.

Both ICM42688P IMUs show similar behaviour, so I have not found evidence pointing to a failure of only one IMU.

I am not claiming that this bias is the cause of the runaway.

I am wondering whether the observability/correction of the Z accelerometer bias and VD becomes important when there is no independent VELZ observation.

Log 03 – RangeFinder comparison

03_RF_ONLY_BAD_VD_AFTER_LANDING.bin

During this test the selected source set uses:


POSXY = None
VELXY = Optical Flow
POSZ  = RangeFinder
VELZ  = None

I do not see the same large vertical position runaway.

However, after one landing the RangeFinder measures approximately:


~0.02 m

with the vehicle physically stationary on the floor, while EKF VD remains around:


~-0.4 m/s

for a significant period.

In other words, the EKF still appears to believe it has an upward vertical velocity while the vehicle is stationary.

The RangeFinder appears able to strongly contain the position error while the vertical velocity state can remain incorrect for some time.

The contrast between the logs appears approximately:


LOG 01 / LOG 02
BARO POSZ + no VELZ

VD becomes incorrect
        ↓
PD starts following the incorrect VD
        ↓
height innovation becomes too large
        ↓
height fusion becomes unhealthy
        ↓
large vertical runaway

versus:


LOG 03
RangeFinder POSZ + no VELZ

VD can still be incorrect
        ↓
RangeFinder strongly constrains PD
        ↓
large position runaway does not develop

My current interpretation

I do not think the evidence supports simply:


no VELZ → runaway

Normally the vertical position observation should also constrain the vertical states indirectly through the EKF.

What appears unusual in Logs 01 and 02 is that VD begins to diverge, PD follows it, and eventually the BARO height measurement becomes statistically incompatible with the EKF state.

Once height fusion becomes unhealthy, there is no independent VELZ observation to directly constrain VD.

Log 03 is particularly interesting because it shows that VD can apparently be significantly wrong without producing the same large PD runaway when the RangeFinder is used as the vertical position source.

Questions

  1. With BARO POSZ and VELZ=None, how strongly should EKF3 height fusion normally constrain VD and the gravity-aligned accelerometer bias?
  2. In Log 01, if the large PD correction at approximately 57.95 s is indeed caused by the height timeout / ResetHeight() path, is it expected that PD can be reset while an incorrect VD remains largely unaffected?
  3. Could this explain why, after the large PD correction in Log 01, the vertical solution immediately starts diverging again?
  4. Does the behaviour in Log 03, where the RangeFinder strongly constrains the position while VD remains significantly non-zero on the ground, provide a useful clue about what is happening to the vertical velocity state?
  5. Is there any known issue in Copter 4.7.1 / EKF3 involving vertical velocity or accelerometer-bias observability when there is no independent vertical velocity source?
  6. Are there relevant EKF3 changes in newer code concerning on-ground zero-velocity fusion or accelerometer Z-bias estimation that could affect this behaviour?

The three original DataFlash BIN files are available from the Google Drive links at the beginning of this post, so the actual parameters, source switching and estimator behaviour can be checked directly.

I am mainly trying to understand the estimator mechanism before changing more parameters.

Thanks.

Small additional observation from the three logs already attached above

While continuing to look at these logs, I noticed something that may be relevant to the abnormal vertical velocity (VD).

At motor start, while the vehicle is still physically stationary on the ground, both ICM42688P IMUs show a small but repeatable DC shift in the gravity-axis acceleration.

For example:

Log 01 – motor start at ~34.45 s

  • IMU0 vertical acceleration change: ~-0.079 m/s²
  • IMU1: ~-0.077 m/s²
  • XKF1.VD changes from approximately -0.020 to -0.086 m/s

Log 02 – motor start at ~35.26 s

  • IMU0: ~-0.087 m/s²
  • IMU1: ~-0.082 m/s²
  • XKF1.VD: approximately -0.017 → -0.073 m/s

The same general behaviour can also be seen in Log 03 (RangeFinder POSZ), at the first motor start around 31.85 s.

What I find interesting is that the acceleration change is seen by both independent IMUs, and VD starts responding even though the vehicle has not moved vertically.

This may help explain why the problem is particularly visible when there is no independent VELZ source, although I am not claiming that the accelerometer shift alone explains the complete BARO runaway.

Has anyone seen this kind of motor/thrust-dependent DC accelerometer shift before, especially simultaneously on two ICM42688P IMUs?

And from the EKF3 side: is a shift of roughly 0.08 m/s² expected to be adequately observable/correctable through height fusion and the Z accelerometer bias state when VELZ=None, or could it produce a persistent error in VD like the one visible in these logs?

I would be particularly interested to know whether the behaviour of VD in these logs is considered normal/expected EKF3 behaviour, or whether it indicates something worth investigating in the estimator.

Hi @Sokerer,

I don’t have many answers for you but it looks like the barometer is seeing about 7m of interference from ground effect. 4.8.0-dev (aka “master”, aka “latest”) includes enhancements from AndyP that includes configurable ground effect compensation parameters (see GNDEFF_xxx params).

In the first log it looks like the vehicle was switched to barometer during takeoff. I guess this was intentional?

It’s possible you’re seeing this issue which we’ve never been able to get to the bottom of because we’ve never had a reliable way to recreate the issue. There is a similar discussion here.

I wonder if you could do a flight with EKF Reply enabled? Simply set LOG_REPLAY = 1 and LOG_DISARMED = 1.

It’s possible with all the EKF fixes we’ve been doing that the issue is resolved in 4.8 so it would be interesting to know if you can reproduce the issue with 4.8.

I also see that perhaps the EKF origin hasn’t been set but the vehicle is flying in Loiter. That’s quite unusual and could be related. If you can reproduce the issue, could you try setting the EKF origin before flying to see if that fixes it? I’m not saying this is the permanent solution but it would tell us something about when the problem occurs.

BTW this fix also went into 4.8 recently but I don’t think it’s related because in your logs EK3_RNG_USE_HGT = -1.

Thanks very much for the report!

Thanks Randy, this is very helpful.

Yes, the switch to BARO during takeoff in Log 01 was intentional. I was specifically testing the behaviour of BARO POSZ with no VELZ, after initially using the RangeFinder.

I can reproduce the problem quite reliably, so I will enable:

LOG_REPLAY = 1
LOG_DISARMED = 1

and provide a new 4.7.1 log.

I can also repeat the same test on 4.8 for comparison.

One additional observation that may be relevant: in the existing logs, when the motors start while the vehicle is still stationary, both ICM42688P IMUs show a small DC shift in the gravity-axis acceleration, and XKF1.VD begins to move even though the vehicle has not moved vertically.

I am not assuming that this is the cause of the runaway, but since the shift is visible on both IMUs I thought it might be useful when looking at the Replay.

I will try to keep the test configuration unchanged so the 4.7.1 and 4.8 results are directly comparable.

Thanks for taking a look at this.

Hi Randy,

Before I do the next 4.7.1 flight with EKF Replay enabled, could I check one logging detail with you?

The current configuration in the existing logs is:

LOG_BITMASK = 180222
LOG_DISARMED = 1
LOG_FILE_BUFSIZE = 172

For the next flight I will set:

LOG_REPLAY = 1

and otherwise keep the configuration unchanged.

Is the current LOG_BITMASK = 180222 ideal for the EKF investigation/replay, or would you prefer any additional logging enabled before I do the test?

The vehicle can become unstable when this issue develops, so I would prefer to make sure the first Replay log contains everything you need rather than doing several flights to collect missing data.

The board is a Flywoo GOKU H743 Pro Mini and logging is to its internal ~500 MB flash storage, not to an SD card.

Thanks!

Hi @Sokerer,

Great, thanks!

Re the XF.VD, I guess you’re talking about this slightly non-zero velocity before takeoff. I suspect this is because the vehicle is vibrating and rattling against the ground so the accelerations are noisy.

Just for the record, the crazy velocities we see right at the end of the log are because the vehicle flipped over and was likely bouncing around on the ground.

Hi Randy,

I just saw your edit regarding the EKF origin — thanks, that’s very interesting.

The flights were intentionally using non-GPS sources for these tests, and I had not manually set the EKF origin before takeoff.

Since the vehicle can become unstable and potentially crash when the issue develops, I’d like to minimise the number of reproduction flights.

For the next test, would you prefer that I first reproduce the original condition with no manually-set EKF origin and Replay enabled, so that you have a Replay log of the actual failure, or would you prefer that I set the EKF origin before takeoff and test whether that prevents the issue?

I can do both if necessary, but I’d like to prioritise whichever is most useful diagnostically.

Also, before I fly again, could you confirm whether my current logging configuration is sufficient?

LOG_BITMASK = 180222
LOG_DISARMED = 1
LOG_FILE_BUFSIZE = 172

I will set:

LOG_REPLAY = 1

The board logs to its internal ~500 MB flash rather than an SD card.

Is LOG_BITMASK = 180222 sufficient for what you need from the Replay, or should I enable anything else first?

Thanks!

Hi @Sokerer,

I think those logging parameters are fine. The LOG_REPLAY = 1 should give us everything we need.

I think we can keep the number of crashes to a minimum by running the tests in this order until the problem is reproduced:

  1. try Copter-4..8.0-dev (aka “latest”) and set the origin
  2. Copter-4.8.0-dev again but this time without the origin set
  3. Copter-4.7.x with the origin set
  4. Copter-4.7.x without the origin set

I suspect the problem will appear at step 2.

Hi Randy,

I completed the first test with ArduCopter 4.8.0-dev (e3d4be86), with the EKF Origin explicitly set before the flight and LOG_REPLAY=1.

I initially thought the flight was reasonably normal and only noticed the problem after landing, when I was unable to take off normally again even with a very high throttle-stick input.

After inspecting the log in detail, however, the vertical estimator problem was already clearly developing well before landing, shortly after the SRC2 → SRC3 transition.

Configuration

Relevant EKF source configuration:

SRC2:
POSXY = None
VELXY = Optical Flow
POSZ  = RangeFinder
VELZ  = None

SRC3:
POSZ  = Barometer
VELZ  = None

The Lua script starts on SRC2 and switches to SRC3 after takeoff once its BARO_READY conditions are satisfied.

Relevant parameters:

BARO1_THST_SCALE = -265
BARO_THST_FILT   = 1
EK3_ALT_M_NSE    = 2
EK3_HRT_FILT     = 2
LOG_REPLAY       = 1

Note about EK3_ALT_M_NSE: I am currently using EK3_ALT_M_NSE=2 intentionally for safety. In previous tests I used EK3_ALT_M_NSE=0.5; when the vertical estimate diverged, ArduPilot reacted much more aggressively and the resulting behaviour led to a violent crash. For this reason I am not repeating these tests with EK3_ALT_M_NSE=0.5.

Timeline

59.692 s   ARM
74.400 s   liftoff detected by Lua
76.925 s   SRC3 ready
76.926 s   SRC2 -> SRC3
~107 s     physical touchdown
110.892 s  DISARM
121.892 s  second ARM

Divergence starts after SRC2 → SRC3

Shortly after the source switch, the EKF altitude begins to separate strongly from both the rangefinder and the logged barometric altitude:

time       RF distance     BARO.Alt      -XKF1.PD
75.99 s      0.67 m         0.95 m         1.04 m
76.49 s      0.80 m         1.02 m         1.17 m
76.99 s      0.84 m         1.11 m         1.29 m
77.99 s      0.87 m         1.14 m         1.72 m
78.49 s      0.87 m         1.08 m         2.00 m
78.99 s      0.92 m         1.09 m         2.32 m
79.99 s      1.01 m         1.28 m         2.90 m

This seems important because the barometer and rangefinder remain reasonably close to each other while the EKF vertical position state moves away rapidly.

Hover / normal flight

During approximately 82–100 s, BARO and RF continue to agree quite well:

median RF                    ~1.15 m
median BARO.Alt              ~1.26 m
median (BARO - RF)           ~+0.12 m
median |BARO - RF|           ~0.12 m
median (-PD - RF)            ~+1.79 m

BARO/RF correlation over this interval is approximately 0.92.

Some representative samples:

time       RF          BARO.Alt       -XKF1.PD
82 s       1.19 m       1.38 m          3.52 m
85 s       1.45 m       1.47 m          3.51 m
90 s       1.07 m       1.17 m          2.82 m
100 s      1.08 m       1.21 m          2.75 m

At ~85 s, for example, RF and BARO differ by only about 2 cm, while the EKF vertical position is more than 2 m away.

The descent makes the problem particularly clear

During the physical descent, both the rangefinder and BARO indicate that the vehicle is descending, but the primary EKF3 vertical states indicate the opposite:

time       RF        BARO.Alt     XKF1.PD      XKF1.VD
100 s      1.08 m      1.21 m      -2.746 m     -0.305 m/s
102 s      0.75 m      0.99 m      -2.708 m     -0.186 m/s
104 s      0.51 m      0.62 m      -2.847 m     -0.159 m/s
105 s      0.33 m      0.49 m      -2.893 m     -0.176 m/s
106 s      0.16 m      0.32 m      -2.966 m     -0.224 m/s
107 s      0.04 m        --        -3.114 m     -0.282 m/s
108 s    touchdown        --        -3.238 m     -0.402 m/s
109 s    on ground        --        -3.453 m     -0.356 m/s
110 s    on ground        --        -3.626 m     -0.285 m/s

Since NED uses positive Down, negative XKF1.VD means that the EKF is estimating upward velocity.

So during the actual descent:

RF:       ~1.08 m -> ~0.16 m     (descending)
BARO.Alt: ~1.21 m -> ~0.32 m     (descending)

-PD:      ~2.75 m -> ~2.97 m     (estimated altitude increasing)
VD:       remains negative        (estimated upward velocity)

Over approximately 100–106 s, the median BARO-RF difference is about +0.13 m, the median absolute difference about 0.14 m, and their correlation is approximately 0.94.

This appears to be more than a fixed altitude offset: the EKF vertical velocity state itself is inconsistent with the observed direction of motion.

BARO behaviour

I also checked the actual logged BARO message rather than using CTUN.BAlt as a proxy.

The raw pressure is significantly affected by thrust, as expected on this vehicle, but BARO_THRUST_COMP is active. During the flight the correction CPress - Press is approximately +46 Pa.

With BARO1_THST_SCALE=-265, the resulting BARO.Alt tracks the rangefinder surprisingly well during the hover and most of the descent.

There is a strong BARO transient very close to touchdown (~107 s), but the EKF divergence had already been present for roughly 30 seconds by then, so this touchdown transient cannot explain the start of the divergence.

Second takeoff attempt

After disarm, Lua switched back to SRC2. I then armed again and tried to take off.

The vertical EKF state had changed somewhat during the disarmed period, but was still inconsistent with the vehicle sitting on the floor:

time        XKF1.PD      XKF1.VD
121.99 s     -1.899 m     +0.664 m/s
124.99 s     -2.081 m     +0.465 m/s
126.99 s     -2.538 m     +0.068 m/s
128.99 s     -2.942 m     -0.167 m/s
130.99 s     -3.185 m     -0.279 m/s

VD eventually becomes negative again and PD starts moving toward increasing estimated altitude even though the vehicle is still essentially on the floor.

During this second takeoff attempt I definitely increased the throttle-stick input, eventually to around 1800 PWM, but CTUN.ThO remained roughly around 0.16–0.18, close to MOT_THST_HOVER=0.17.

The rangefinder shows a few very small lifts from the floor, but the vehicle could not take off normally.

Main observation

The main behaviour I can see in the log is:

SRC2 -> SRC3
      |
      v
RF and BARO continue to describe a physically plausible trajectory
      |
      |    while
      v
EKF3 PD/VD progressively diverge

During descent:
RF + BARO  -> descending
EKF3 VD    -> estimated upward velocity
EKF3 PD    -> increasing estimated altitude

The full Replay log is available below so the development team has the original data needed to investigate it.

Full ArduCopter 4.8.0-dev + EKF Origin + Replay log:

Download the full Replay BIN log

Please let me know if you need any additional logs or a specific controlled test.

Which PRs would help this indoor build

This isn’t vanilla 4.7.1. The log reports ArduCopter V4.8.0-dev (e3d4be86), a master build on a FlywooH743Pro, so it
already has everything the SFD README lists as merged (AGL KF, zero-velocity fusion, GNDEFF_*, the height datum
reset). It also runs Lua scripts (“OSM V5”, “V6”) that switch the EKF between source sets: SRC2 (flow, height from the
rangefinder) on the ground, and SRC3 (height from the baro) from about 1 m up.

What went wrong

The whole session fails in one chain, and each step feeds the next:

  1. The height climbs on the ground at idle. As soon as the motors spin at idle (56 s), raw IMU.AccZ steps from -9.996
    to -10.209 m/s² with the vehicle sitting still. The EKF height rises from 0.02 to 0.67 m while the rangefinder
    reads 0.02 m and the baro is flat.
  2. That makes the ground-effect gate trip in the hover. AP_GroundEffect records its takeoff height-above-ground at
    throttle-up, when that height already read about 0.55 m. At a real 1 m hover the gate therefore sees about 0.5 m,
    which equals GNDEFF_ALT. The commanded descent speed (PSCD.DVD) sits at 0.0000 to 0.05 m/s in the hover, so a
    descent counts as “commanded”. Together these set touchdown_expected on and off through the hover, and each time
    it’s set the baro innovation is pinned at -0.5 (XKF3.IPD reads exactly -0.50 through most of 83 to 103 s).
  3. On the baro set the EKF runs 2 m high. After the switch to SRC3 at 71.6 s the EKF climbs to 3.6 m while the baro
    reads 1.5 m and the rangefinder 1.4 m. The vehicle then hovers with the EKF at 2.8 m and the rangefinder at 1.0 to
    1.2 m. The accelerometer reads 0.3 m/s² more in hover than at rest (-10.30 vs -9.996), and the accel bias estimate
    (XKF2.AZ) is still moving (-0.18 to -0.44) when it lands.
  4. The terrain offset learns that error. XKF5.TOfs goes from -0.59 to -2.81 m. The vehicle lands with the EKF reading
    3.4 m.
  5. The error is frozen in for the next flight. After disarm the script switches back to SRC2, where the rangefinder is
    the height source. Master’s EstimateTerrainOffset() does no update at all in that case (activeHgtSource ==
    RANGEFINDER), and the rangefinder height is fused as rng - terrainState. So the EKF sits 2.8 m high on the ground,
    arms at 1.9 m, and flight 2 hovers at a real 0.2 m while the EKF reads 3.2 m. That is the “aborted takeoff”: the
    controller believed the vehicle was at 3 m.

The log has no logger drops (DSF.Dp = 0) and LOG_REPLAY=1 is set, so it can be replayed.

PRs that help

PR: #34362 AP_GroundEffect: stop touchdown_expected latching in cruise
What it addresses here: Step 2. Its 0.05 m/s deadband on the descent test removes the hover samples here (DVD 0.0000
to
0.0498 from 83 to 94 s), so the -0.5 floor stops holding the EKF high. It does not cover 96 to 102 s, which was a
real descent.
How sure: Strong. It’s the same defect the PR was measured on (log7). Replay can’t show it, because the gate runs
before the DAL.
────────────────────────────────────────
PR: #33359 AGL KF for the rangefinder height switch, used with EK3_RNG_USE_HGT
What it addresses here: Steps 3 to 5. It does natively what the Lua script does by hand: rangefinder height near the
ground, baro above, keyed off the AGL KF. Because the switch happens inside one source set, the baro offset is
tracked across it instead of carrying a terrain offset between sets.
How sure: Strong on the design; flown on the SFD indoor quad (log281). It needs EK3_OPTIONS bit 3 (AglKfForOptflow;
that’s the upstream numbering), and the scripted source switching has to go.
────────────────────────────────────────
PR: #33478 fuse AGL KF velocity as a vertical velocity observation
What it addresses here: Step 3. SRC3 has no vertical velocity source, so height above the rangefinder band is
dead-reckoned against a baro weighted at R = 4. This PR gives it one: on Lucid v2 (log41) it held 36 s hands-off at
13 cm.
How sure: Medium. It needs EK3_AGL_ABIAS_P at 0.3, and the known cost is that it takes authority away from the baro.
────────────────────────────────────────
PR: #32471 hover Z-bias learning (vibration rectification)
What it addresses here: Step 3’s 0.3 m/s² hover offset, applied as a stored correction from takeoff instead of
re-learned every flight.
How sure: Medium. The number matches what it corrects, but it needs one learning hover with a good height reference,
and it doesn’t model the idle-spin shift on the ground.

What no PR fixes, including the SFD branch: step 5 itself. Nothing resets terrainState when the height source becomes
the rangefinder, or while the vehicle is landed. The SFD branch has the same skip at AP_NavEKF3_OptFlowFusion.cpp:149.
It only bites when source sets switch height source mid-session, which is exactly what this user’s scripts do. It’s
worth a small master PR: reset the terrain state to PD + rngOnGnd on a height-source change to the rangefinder, or
while landed. Replay of this log would show it directly.

Not the fix here: #32232 (this rangefinder reads Good at 0.02 m, so its substitution never runs), #32972 and #32768
(the height source at takeoff is the rangefinder, not the baro).

Advice for the user now, on their current build

  • Stop changing the height source between sets. Either give SRC2 and SRC3 the same POSZ, or use EK3_RNG_USE_HGT in
    place of the script’s switching. Until then, reboot between flights: a disarm does not clear the -2.8 m offset.
  • Set EK3_OPTIONS bit 3 so master’s AGL KF is used for flow.
  • Check vertical tuning separately. PSC_D_ACC_P is 0.017 against a default of 0.05, which is low.

Hi @Sokerer,

Txs very much for the log. I’ve had an initial look and what seems odd to me is that the EKF altitude does not converge towards the barometer altitude during the hover. Instead it maintains about a 2m difference which actually grows during the descent.

We don’t see a runaway of the altitude though which makes me think that the barometer must be being fused but its effect is not strong enough. My guess is that it is a combination of these things:

  1. EK3_SRCx_VELZ = 0 (None) meaning we are not getting any velocity sensor input so the EKF must completely rely on the altitude to correct both position and velocity.
  2. EKF3_ALT_M_NSE = 2.0 (the default) which may not be strong enough to completely overcome the accelerometer offset (probably caused by vibration)
  3. “touchdown expected” is oscillating between true and false - whenever its true the barometer reading will be deweighted.

The good news is we can test these theories now that we have a reply log, thanks!

@Sokerer,

Another thing we’ve noticed is that the EK3_SRC3_YAW = 0 (None) which is unusual in that EK3_SRC1/2_YAW = 1 (Compass). I wonder if that was intentional or not.

This came up as we reviewed potentially related PRs and found PR 32136. We don’t think it’s related though because it is already included in 4.7 (and higher).

Hi Randy,
yes, this is intentional in the sense that SRC3 is dynamically prepared by our Lua script before switching to it.

The script copies POSXY, VELXY, VELZ and YAW from the currently active low-altitude source set into SRC3, and only changes POSZ to Barometer.

So although the stored/default EK3_SRC3_YAW is 0, when switching from SRC2 the script sets EK3_SRC3_YAW = 1 (Compass) before activating SRC3.

Thanks for pointing it out — I should have mentioned that the SRC3 parameters are modified dynamically by the script.

Update after looking further into Randy’s notes

Following Randy’s comments, I looked more closely at the EKF takeoff_expected / touchdown_expected logic and reconstructed those states during the flight.

One important result is that the vertical divergence starts before the false touchdown_expected events.

Lua switches to SRC3 at ~76.93 s with the runtime configuration:

POSXY=None
VELXY=OpticalFlow
POSZ=Baro
VELZ=None
YAW=Compass

The first false touchdown_expected=true occurs at ~88.51 s, while the vehicle is still hovering, with RF around 1.2 m.

By then, however, the EKF vertical solution has already diverged substantially. In the baseline (EK3_ABIAS_P_NSE=0.02), just before the first touchdown pulse:

ΔPD ≈ -1.56 m
RF change ≈ +0.36 m
relative error vs RF ≈ +1.20 m

So the false touchdown detection can modify the subsequent Baro fusion (increased observation variance / ground-effect handling), but it does not appear to be the cause of the initial vertical runaway.

I also found a likely explanation for the strange behaviour after the second arm.

At that point Lua had already switched back to SRC2 / POSZ=RangeFinder / VELXY=OpticalFlow.

With RNGFND1_MAX=6 m, EKF3 calculates the Optical Flow height-control limit as:

max(6 * 0.7 - 1.0, 1.0) = 3.2 m

In this SRC2/RangeFinder configuration the relevant margin is effectively:

3.2 + PD

During the second flight PD reaches approximately -3.2 m. At the same time I see the pilot increasing throttle stick up to 1800–1995 PWM, while PSCD.DVD progressively approaches zero and CTUN.ThO remains around hover.

The transition follows the PD=-3.2 m threshold very closely.

This is strongly consistent with the corrupted EKF vertical position causing the Optical Flow height limit / AC_Avoid path to suppress further climb demand. I cannot prove the actual AC_Avoid branch execution from the BIN because the pre/post-avoidance climb-rate is not logged.

An important detail is that this particular PD=-3.2 m condition applies to SRC2 with POSZ=RangeFinder. With SRC3/Baro, EKF3 compensates using terrainState, so the corresponding limit is based approximately on:

3.2 - estimated_HAGL

So I now see the second-flight behaviour mainly as a downstream consequence of the already incorrect vertical EKF state.

The main unresolved question therefore remains the original one: why do VD/PD run away so strongly after switching to SRC3/Baro, while the RangeFinder indicates a much smaller real vertical displacement?

Did you not see this?

Yes — I see it now. I had read that point, but I had not connected it correctly to what I later found in the second flight.

Your original explanation already identified the stale terrainState surviving the switch back to RangeFinder as the upstream problem.

What I found afterwards is a possible downstream mechanism explaining why the second flight specifically settles around the apparent 3.2 m EKF height: with MaxD=6 m, the OF height-control limit is 3.2 m, and in SRC2/POSZ=RangeFinder the margin tracks 3.2 + PD. As PD approaches -3.2 m, PSCD.DVD approaches zero despite increasing stick.

So I think I was rediscovering the downstream consequence of the Step 5 problem you had already pointed out.

Also, following Randy’s notes I reconstructed touchdown_expected; one additional detail I found is that the SRC3 divergence is already substantial before the first false touchdown pulse, so those pulses can affect the later Baro fusion but do not appear to initiate the runaway.

Thanks — I had missed the connection between those pieces.

Hi Andy,

thanks again for the advice you gave me earlier.

I’ve now implemented both of your PRs:

#33359 – AP_NavEKF3: use the AGL KF for the optical-flow rangefinder height switch and observation

#34362 – AP_GroundEffect: stop touchdown_expected latching in cruise

and I’ve also removed the Lua EKF source switching, so the source set is no longer being changed dynamically by the script.

With these changes, for the first time I can finally fly the copter reasonably well indoors. The difference is huge compared to where I started, so thank you again for pointing me in the right direction.

There is still one behaviour I’d really like your opinion on.

In this log:

the second landing is strange.

After the copter has already reached the ground and the throttle stick is at minimum, the FC still commands some motor output for a while. Looking at the log, it seems to me that this is mainly attitude correction rather than collective throttle.

Around that landing I see the horizontal position estimate drift away from the Loiter target, and the controller ends up requesting roughly -20 deg of pitch even though the copter is already on the ground.

The part that made me suspicious of the optical flow is that, very close to the ground, the flow innovations become very bad/intermittent. During that landing the flow innovation test ratio is very low at about 9 cm, then saturates as the range gets down to roughly 5 cm, and later briefly becomes acceptable again while the vehicle is already almost on the ground.

The resulting sequence appears to be roughly:

optical flow becomes unreliable near the ground
→ XY estimate moves away from the Loiter target
→ Loiter requests a strong horizontal correction
→ desired pitch reaches about -20 deg
→ attitude control keeps driving some motors even though the throttle stick is already at minimum

Does that interpretation look reasonable to you, or am I reading the log incorrectly?

Do you think there should be some protection against using optical flow in the last few centimetres before touchdown, or is there already another mechanism in ArduCopter that should normally prevent this?

Also, now that the Lua source switching has been removed and these two PRs are in the build, if you notice anything else in this log that you would change in the current setup, I’d be very interested in your advice.

Thanks again for your help.

Yes I have a PR for this: https://github.com/ArduPilot/ardupilot/pull/34292

Please can you put a comment on the two PR’s that helped you? It helps persuade @rmackay9 that these fixes are necessary :face_with_tongue:

Incidentally the behaviour you are seeing sounds like it has not detected the landing which could be caused by a number of things.

Thanks Andy, I really appreciate it.

Yes, I’ll add a comment to both PRs.

They made a very significant difference on this build. I wouldn’t say the vehicle is fully stable yet, because I still have some issues to work through, but the behaviour improved dramatically and it is now much more flyable indoors than before.

I’ll also have a look at #34292. What you say about the landing not being detected fits very well with what I saw in the log — after touching the ground the vehicle was still behaving as if it was flying and continued generating attitude corrections.

Thanks again for taking the time to look at this.

@Sokerer,

It’s really great to hear that you tested @andyp1per’s PR and they helped! As Andy says, adding your voice to the PRs will help get them in.

I’m actually already convinced that AndyP’s PR add value. The issue is that we need to make sure they don’t break existing functionality or introduce issues around the edge cases. That part is difficult and AIs can’t do this properly (yet).