V1.14.3 Takeoff mode without GPS: sometimes no takeoff, sometimes climbs far above MIS_TAKEOFF_ALT

Hi all,

Holybro S500, Pixhawk 6X, PX4 v1.14.3. Indoors, no GPS fix, baro height (EKF2_HGT_REF = 0), Holybro H-Flow over DroneCAN (EKF2_OF_CTRL = 1, EKF2_RNG_CTRL = 1, EKF2_RNG_A_HMAX = 1.0). Armed by RC switch, then switched to Takeoff mode.

Takeoff mode behaves inconsistently. The climb seems to depend on the baro altitude at the moment of takeoff, not on MIS_TAKEOFF_ALT:

Log A: https://logs.px4.io/plot_app?log=f391d795-a2be-4c0d-b547-93456d4f7cf5

Log B: https://logs.px4.io/plot_app?log=bc72cb2b-e861-494a-9e06-0534ee803ba6

Log C:https://logs.px4.io/plot_app?log=be21f60a-10e4-4ed2-a00d-298c53df1aa3

Across 24 attempts the same pattern repeats:

  • When the local altitude had drifted above MIS_TAKEOFF_ALT on the ground, the vehicle did not take off.
  • When it had drifted below 0, the vehicle climbed by MIS_TAKEOFF_ALT plus the drift.

Every attempt logs [navigator] Using minimum takeoff altitude: X m.

Questions

  1. In v1.14.3, when no home altitude is available (no GPS), is the takeoff target MIS_TAKEOFF_ALT as an absolute local altitude instead of relative to the current altitude? Is that why it fails or overshoots after baro drift?
  2. Is this fixed in v1.15 and later, where the target seems to be current altitude + MIS_TAKEOFF_ALT?
  3. I want to use the H-Flow rangefinder as the height reference instead of the barometer (indoors, flat floor, flying below 3 m). Which parameters should I set in v1.14.3? Is this list correct, and is anything missing?
  4. EKF2_HGT_REF 2 (range sensor) currently 0
  5. EKF2_RNG_CTRL 2 (always) or 1 (conditional)? currently 1
  6. EKF2_BARO_CTRL 1 keep enabled, or set 0?
  7. EKF2_RNG_A_HMAX ? currently 1.0 m
  8. EKF2_RNG_A_VMAX ? currently 1.0 m/s
  9. EKF2_MIN_RNG 0.13 (rangefinder reading when on the ground)
  10. EKF2_RNG_POS_X 0.10
  11. EKF2_RNG_NOISE ?
  12. EKF2_RNG_GATE ?
  13. MPC_ALT_MODE 0, 1 or 2?
  14. UAVCAN_RNG_MIN 0.08
  15. UAVCAN_RNG_MAX 30

Thanks!

This is a useful controlled dataset, especially with 24 attempts and three representative ULogs.

The numbers suggest a possible altitude-reference mismatch rather than simply a controller tuning issue.

For example, in Log C:

MIS_TAKEOFF_ALT = 0.18 m
Local altitude on ground = -3.31 m
Observed climb = 3.48 m

The difference, 0.18 - (-3.31) = 3.49 m, is remarkably close to the observed climb.

I would investigate whether the takeoff altitude setpoint is being constructed in a local/global reference that is inconsistent with the vehicle’s current estimated altitude.

Before concluding this is a v1.14.3 bug or fixed in v1.15+, I would compare the exact takeoff implementation across release tags and inspect the three ULogs around mode activation:

  • local position z and z_reset_counter

  • local position setpoint z

  • global altitude/home validity

  • vehicle status and navigation state

  • distance sensor measurements and EKF height fusion status

The first question is whether the incorrect altitude target already exists at Takeoff mode entry, before the controller begins climbing.

For the rangefinder configuration, the PX4 v1.14 EKF documentation lists EKF2_HGT_REF=2 and EKF2_RNG_CTRL=2 as a typical indoor flat-terrain configuration, with barometer aiding remaining enabled. However, I would validate rangefinder health and fusion behaviour before recommending a flight test.

I would avoid tuning the altitude controller or changing several EKF parameters simultaneously until the reference-frame question is resolved.

If useful, I would be interested in comparing the three ULogs and identifying the exact point where the altitude setpoint diverges from the expected relative takeoff height.

Hi, thanks for sharing this issue.

I have been investigating the PX4 v1.14.3 GPS-denied Takeoff behaviour using the three available ULog recordings and the corresponding PX4 source code.

One potentially relevant finding is an altitude-reference mismatch between Navigator and FlightTaskAuto when a valid global altitude reference is unavailable.

In all three recordings, vehicle_local_position.z_global was false near Takeoff activation. In PX4 v1.14.3, FlightTaskAuto::_evaluateGlobalReference() sets _reference_altitude to zero when z_global is false. The target altitude is subsequently converted using:

tmp_target(2) = -(current.alt - _reference_altitude);

The recordings show different initial local Z offsets:

  • Log 1: approximately +1.04 m — upward movement

  • Log 2: approximately −4.13 m — no successful ascent

  • Log 3: approximately +3.31 m — substantial upward movement

The resulting target directions appear consistent with the observed differences in Takeoff behaviour.

This suggests that the interaction between the Navigator takeoff altitude and the local altitude reference may be worth investigating, particularly when operating with optical flow and a rangefinder without GPS.

I want to emphasize that this is a source-code-supported hypothesis, not yet a confirmed root cause. The offline analysis is complete, but a controlled PX4 v1.14.3 SITL reproduction is still pending.

Could the PX4 maintainers clarify whether this altitude-reference behaviour is expected in GPS-denied Takeoff mode, and whether any related fixes have been introduced in later versions?

Relevant source files:

I would be happy to contribute further validation results and a regression test if this helps narrow down the issue.

Hi @yigitcan-ozturk, thanks for the analysis. You were right about the v1.14.3 height reference. I’ve moved to v1.17.0. The height problem is fixed, but Takeoff without GPS has two other problems.

Setup (v1.17.0)

  • S500, Pixhawk 6X, H-Flow over DroneCAN, no GPS, indoors
  • EKF2_HGT_REF = 2, EKF2_RNG_CTRL = 2, EKF2_BARO_CTRL = 1, EKF2_OF_CTRL = 1, EKF2_MIN_RNG = 0.14, MPC_ALT_MODE = 0, MIS_TAKEOFF_ALT = 1.0, COM_POS_FS_EPH = 5
  • Armed by RC switch in Altitude/Position mode, then switched to Takeoff

Height: fixed

  • Every attempt logs Using default takeoff altitude: 1.0 m, and the target is current altitude + MIS_TAKEOFF_ALT.
  • In the good attempt (log 32) the drone climbed to 1.00 m by the rangefinder and held 0.92–1.09 m for about 40 s.

Problem 1: Takeoff moves sideways towards the EKF origin

Whether the takeoff worked depended only on how far the drone was from where it was powered on:

In every failed attempt, the trajectory setpoint headed towards local (0, 0), within 1°, from the first sample of Takeoff, while the drone was still on the ground.

  • Home was set correctly at the arming spot (log 38: home x −0.94, y +3.27), so the target is the EKF origin, not home.
  • position_setpoint_triplet.current shows lat = 0.0, lon = 0.0. vehicle_local_position shows xy_global = false and ref_lat/lon = NaN.

Reading the v1.17.0 source:

  1. MissionBlock::set_takeoff_item() sets item->lat/lon = get_global_position()->lat/lon, which is 0/0 with no global position.
  2. With xy_global = false, FlightTaskAuto::_evaluateGlobalReference() sets ref_lat/lon = 0, so lat/lon 0/0 is projected to local (0, 0).

This seems to match issue #21524 (Local Position only Takeoff Problems for Multicopter · Issue #21524 · PX4/PX4-Autopilot · GitHub). PR #23845 fixed the same behaviour for Land by sending NaN lat/lon when the global position is invalid.

Problem 2: Takeoff refused with “No valid local position estimate”

After one failed attempt, Takeoff could no longer be selected at all:

  • Log 20: during a failed takeoff (the sideways pull above), the vehicle tipped forward to about −76° pitch and stayed at about −70°. Optical flow fusion stopped and the EKF went into dead reckoning.
  • Log 21: on the next arm, eph was already 3.1 m.
  • Logs 22–31: eph stayed at 6.77 m, above COM_POS_FS_EPH = 5.
  • Logs 29–31: switching to Takeoff was refused. QGC showed “No valid local position estimate”, and the log says Switching to Takeoff is currently not available. failsafe_flags.local_position_invalid = 1, but local_position_invalid_relaxed = 0, so Position mode was still allowed (logs 22–28 flew Position mode normally).
  • Optical flow was fused 100% of the time in logs 22–31, but eph never came down.
  • Only a reboot cleared it (log 32: eph 0.4 m).

Questions

  1. Is it expected that Takeoff without a global position targets the EKF origin horizontally instead of the current position? Is #21524 still open for Takeoff in v1.17?
  2. Should Takeoff::set_takeoff_position() set current.lat/lon to NaN when the global position is invalid, as PR #23845 does for Land, so FlightTaskAuto locks the current xy position?
  3. With flow-only horizontal aiding, is it expected that eph never decreases once it has grown (6.77 m held for about 8 minutes while flow was fused), so a reboot is the only way to recover?
  4. Is it intended that Takeoff needs the strict local position check while Position mode accepts the relaxed one? What do you recommend instead of raising COM_POS_FS_EPH?
  5. Until there’s a fix, is powering on at the takeoff spot (and rebooting after any tip-over) the recommended workaround? Is QGC “Set Estimator Origin” a safe alternative?
  6. For indoor flight over a flat floor without GPS, is EKF2_HGT_REF = 2, EKF2_RNG_CTRL = 2, EKF2_BARO_CTRL = 1, MPC_ALT_MODE = 0 the recommended v1.17 configuration?

Logs (20-Oct, v1.17.0)

Problem 1, sideways pull towards the EKF origin:

Problem 2, “No valid local position estimate”:

Thanks!

Hi Rathi,

Thank you for the exceptionally detailed investigation and for providing multiple comparative flight logs.

I reviewed the relevant PX4 v1.17.0 source code, as well as issue #21524 and the merged PR #23845.

1. Takeoff targeting the local origin

Your analysis is strongly supported by the source code.

In v1.17.0, MissionBlock::set_takeoff_item() still assigns latitude and longitude directly from the global position, without checking xy_global.

Meanwhile, FlightTaskAuto::_evaluateGlobalReference() substitutes a zero-valued geographic reference when global XY is unavailable.

This makes your reported unintended projection toward local (0, 0) a credible explanation for the observed behaviour.

2. Proposed Takeoff correction

Your proposed NaN approach is consistent with the existing Land correction in PR #23845.

Importantly, FlightTaskAuto::_evaluateTriplets() already contains logic to lock the current local XY position when the incoming latitude or longitude is non-finite.

I would therefore consider adding an xy_global validity check in the Takeoff path a reasonable candidate fix, subject to SITL regression testing.

3. Persistent EPH after loss of optical-flow aiding

The persistent EPH of 6.77 m, despite optical-flow fusion resuming, deserves separate investigation.

Successful optical-flow fusion does not necessarily guarantee a reduction in the estimator’s reported absolute horizontal position uncertainty.

I would inspect the EKF covariance evolution, optical-flow innovation consistency, reset counters and any remaining aiding-source limitations before concluding that a reboot is fundamentally required.

4. Strict versus relaxed local-position validity

Your observation that Position mode remains available while Takeoff is rejected is important.

I would not recommend increasing COM_POS_FS_EPH simply to bypass this condition, particularly given the previously observed tip-over.

It would be useful for the maintainers to confirm whether the different validity requirements are intentional for this operating mode.

5. Temporary workaround

Starting the vehicle at the intended takeoff location may avoid the observed origin-offset trigger, but it should not be considered a verified safety fix.

I would also avoid treating QGC’s Set Estimator Origin as a workaround without validating the resulting coordinate-frame behaviour.

Given the reported −76° pitch event, I would recommend reproducing and validating any proposed correction in SITL before further physical Takeoff-mode testing.

6. Height-reference configuration

Your rangefinder-primary configuration appears consistent with the successful flat-floor indoor tests you reported. However, the height-reference configuration and the horizontal Takeoff-target bug should be treated as separate issues.

Suggested next step

I think this now warrants a focused GitHub issue, or a reopening/follow-up to #21524, with the v1.17.0 source references and the comparative logs.

A minimal regression test should verify that a multicopter with valid local XY but no global position holds its current horizontal position during automatic takeoff, even when displaced from the estimator origin.

Your measurements provide a strong basis for such a test.

Best regards,
Yiğitcan Öztürk
PAMILANGA LTD