Terrain AGL Hold — terrain following for fixed wings (builds on #11438) - #11785
Terrain AGL Hold — terrain following for fixed wings (builds on #11438)#11785MartinovEm wants to merge 55 commits into
Conversation
…ature/inav_terrain # Conflicts: # docs/Settings.md
…ature/inav_terrain
…ature/inav_terrain # Conflicts: # .gitignore
…ature/inav_terrain
…rd is not in idle
…add new DEBUG LOG type "terrain"
# Conflicts: # src/test/unit/CMakeLists.txt
…in-unit-test fix(terrain): build terrain unit test
4b9e4ef to
3b656e4
Compare
Adds a third sdcardVTable_t implementation for the SITL target that reads/writes 512-byte blocks in a disk-image file on the host (--sdcard=<image>), following the SPI/SDIO drivers contract (deferred completion callbacks from poll()). Enables USE_SDCARD and USE_TERRAIN for SITL, so asyncfatfs and the terrain tile reader run unchanged in the simulator. Without --sdcard the virtual FC behaves like one with an empty card slot.
…es and climb lookahead
Adds terrain_nav.{c,h}: a strict wrapper around the terrain module for
future navigation consumers. Success is always an explicit boolean and
values are written only on success - no sentinel, stale or substituted
value ever reaches a caller. Provides current AGL, terrain height at an
arbitrary location (scheduling the block load on cache miss), terrain
height at the GPS origin, and a bounded climb-lookahead that walks the
grid along a bearing, accumulates achievable climb and reports the worst
height deficit together with an explicit count of unavailable samples.
Built entirely on the terrain module's public utilities; no existing
files changed apart from the build list. Nothing in flight code calls
this API yet - flight behavior is unchanged.
FW 3D Cruise only, opt-in RC box, health-gated, all commands through the single altitude-target funnel. Engage captures current AGL (below minimum: slew-limited climb to it); data loss freezes the target with an OSD warning, resume after 3 s healthy; pitch stick pauses the hold and re-captures on release; directional lookahead climbs early for rising terrain within the global per-cycle block budget (cache - 2); nav_max_altitude always wins with TERRAIN VS MAX ALT / TERRAIN PULL UP warnings. New settings: terrain_nav_min_agl (60 m default), terrain_nav_lookahead (1000 m, 0=off). SITL tile cache set to 5 to mirror the small-cache board tier.
…ning The climb lookahead scans along the course over ground; when the heading estimate is invalid the cog can be stale, so the lookahead now switches itself off (same skip as the config and low-speed gates) and the pilot gets a new lowest-priority OSD warning - the ceiling and data-loss warnings always override it. The reactive hold keeps tracking unchanged (the same degraded mode as terrain_nav_lookahead = 0). No new settings.
…LIMB info + TERR flymode indicator
…mand step, no deadband-edge writer flap)
…capped lookahead horizon
…d threat only, PULL UP survives below-min captures, pass-at-minimum clear rule, TAWS callout texts Below the minimum the escape-test shortfall now counts only the real terrain-relative deficit (the altitude term is clamped out there; above the minimum it keeps acting as the honest cushion), so the automatic climb shows TERRAIN AUTO CLIMB TO MIN instead of a false TERRAIN AHEAD. A capture below the minimum no longer extinguishes an active PULL UP - the alarm clears only at/above the minimum, as always. TERRAIN AHEAD clears when the escape test passes with the aircraft at/above the minimum, both sustained 2 s; a returning threat re-fires the series (the spare-margin clear could never be met riding exactly at the floor). OSD texts become "TERRAIN AHEAD!" and "TERRAIN! PULL UP!".
…-min capture hands over to the auto-climb info A capture below the minimum (low engagement or a deep stick release) starts a clean alarm phase again: the automatic climb shows TERRAIN AUTO CLIMB TO MIN, and TERRAIN! PULL UP! returns only when the climb is failing (losing height against the best achieved), when the pilot pushes below the margin, or when the floor is breached after having been reached.
… cap-uncover fast clear
…annel + auto-climb info alternation
At a 30 m floor the alarm point (floor - 5 m) minus the worst-case map error budget (30 m on steep slopes) minus the tracking band leaves no real clearance. 50 m is the lowest floor where the worst-case stack still clears with margin. Default unchanged at 6000.
…format The terrain data layer now stores heights as a 10-bit packed heightOffset[] plus an int16 heightBase instead of a plain int16 height[x][y] grid. Read the four interpolation corners through getHeightOffsetByIndex() + heightBase, scaled by TERRAIN_HEIGHT_OFFSET_RESOLUTION_M (2 m), the same way terrain.c decodes a sample; the interpolation math is unchanged.
The blackbox log-number stat compiles on SITL only once USE_SDCARD is enabled (added for SITL SD-card support in this branch). On 64-bit hosts int32_t is not long, and clang on macOS errors on the format mismatch with warnings-as-errors. Cast the argument to match the format.
4e7ec2e to
bfa9a96
Compare
|
RAM / Flash usage vs. base branch — commit
|
|
Test firmware build ready — commit Download firmware for PR #11785 244 targets built. Find your board's
|
|
ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing |
PR Summary by QodoAdd Terrain AGL Hold (terrain-following Cruise) backed by SD terrain tiles
AI Description
Diagram
High-Level Assessment
Files changed (44)
|
Code Review by Qodo
1. Async seek mis-handled
|
As an FPV pilot flying a field with hills around it, I kept coming back to the same worry: plain Cruise holds a fixed altitude — fine over flat ground, but around those hills it can quietly become a problem, since the aircraft holds its height above home, not above the ground rising ahead of it. The terrain data is already on the SD card — so I decided to work on a Cruise that follows it.
Terrain AGL Hold is basically CRUZ with a moving target — in 3D Cruise, one new mode box makes the altitude target follow the terrain data under the aircraft: you hold height above the ground, not above home. Downhill the plane descends with the valley, uphill it climbs with the ridge. Box off — instant stock CRUZ.
▶ Video — Flights 1–2: floor work and turns over the flat field (AtomRC Beluga, SpeedyBee F405 Wing, real SD terrain tiles).
▶ Video — Flight 3: crossing a ridge that rises ~150 m above the takeoff field — three passes, hands off the pitch stick, no warnings needed.
What it does
One navigation cycle — inputs, health gate, the moving target, the alarm ladder, and the single gate into the altitude target path (the stock controller, untouched).
TERRAIN AGL HOLD— fixed wing only, active only inside 3D Cruise (NAV COURSE HOLD + NAV ALTHOLD). It is a modifier: everything else stays stock Cruise.TERRAIN AUTO CLIMB TO MIN). Why 50 m is the lowest the firmware will accept: the floor has to absorb everything going wrong at once — worst-case map error on steep slopes (~±30 m; typical source agreement is ~6 m, measured against ICESat-2 and radar-altimeter data), the aircraft's natural ±5–8 m tracking breathing, and the alarm margin (the floor alarm fires 5 m below the floor). At a 50 m floor that worst-case stack still clears the ground with a few meters in hand.TERRAIN AHEAD!tens of seconds before any reactive alarm could fire.TERRAIN AUTO CLIMB TO MIN):TERRAIN NOT READYTERRAIN AUTO CLIMB TO MINTERRAIN AHEAD!TERRAIN! PULL UP!(blinks)TERRAIN! TURN AWAY!(blinks)TERRAIN VS MAX ALTnav_max_altitudeTERRAIN LOST - ALT FROZENTERRAIN LOOKAHEAD OFFSafety design
nav_max_altitudeclamp downstream. No PID or controller code is modified anywhere.nav_fw_auto_climb_rate— no separate terrain climb setting. Set it to what your aircraft can genuinely sustain: the terrain escape warning trusts that number.NB: set
nav_fw_manual_climb_rateequal tonav_fw_auto_climb_rate(they default to 300/500). Matched, an auto-climb stays smooth if you touch the pitch stick — and pulling won’t climb any faster, since the auto-climb is already at that rate. Left unmatched, you may see a small, harmless nose-ease.TERRAIN LOST - ALT FROZEN(it will not descend blindly), 3 s of healthy data resumes. Proven closed-loop on a deliberately corrupted card.Why a separate mode, and not SURFACE
A fair question: INAV already has "terrain following" — SURFACE mode. Why not extend it? Because under the same family name, the two do different jobs with different physics:
A pilot carrying SURFACE expectations into a map-based mode (or vice versa) would be carrying the wrong safety assumptions — that is exactly what a distinct name prevents. The two never stack: this mode refuses to engage while SURFACE is active. Converging the pilot-facing "terrain following" concept over both sources someday — happy to discuss (see the open question below); the safety rules stay per-source either way.
New settings (only two)
terrain_nav_min_aglterrain_nav_lookaheadHow to test (bench + first flight)
.TERtiles; terrain is enabled by default only on F7/H7/AT32-class targets per Error414/feature/inav terrain #11438). Enable error414's data layer as in Error414/feature/inav terrain #11438:set terrain_enabled = ON(it defaults OFF), with.TERterrain tiles on the SD card — you can generate them for your area with this map generator (.TER (INAV Terrain)is its default output); the Configurator's built-in Map Generator tab switches to.TERin feat(map-generator): switch terrain output to .TER version 50 (10-bit packed) inav-configurator#2708.The two
terrain_nav_*settings are CLI-only (simple enough that a Configurator page isn't planned). Defaults are sane; for a first test you can skip bothsetlines entirely.OSD: the stock Altitude element shows height above your home point, not above the ground. To see the terrain height above ground (AGL) in flight, add the Rangefinder OSD element — with no rangefinder fitted, it shows the terrain-derived AGL. The flight-mode field shows TERR while the hold is engaged.
3. Important: set
nav_fw_manual_climb_rateequal tonav_fw_auto_climb_rate(defaults are 300/500 — unequal). If manual < auto, grabbing pitch during an auto-climb commands the lower rate and the nose visibly eases.4. Ground check outdoors: GPS fix ·
sd_info= Ready · the OSD height-above-ground element reads ≈ 0 on the ground.5. First flight: fly 3D Cruise as usual, get comfortably HIGH over FLAT ground, then flip TERRAIN AGL HOLD on — watch it hold height above the ground; flip it off and on; grab the pitch stick (it yields, release re-captures). Only when that is boring, go lower or toward terrain — and never dive at a hill on purpose; the message table above says what each warning wants from you. Panic rule: box off = stock cruise instantly; the pitch stick always wins.
Verification — honest levels
Flight 2: floor work, the push-dive ladders, the bank-vs-sag curve.
The SD timing proof: nav never blocks, the data layer’s lock does its job.
The campaign-wide alarm discipline: every fire below the floor, every clear at 60.
Tested on: SPI-SD F405 Wing + SITL — the flight evidence below was flown on the earlier int16/9.1 base; on the current rebased base the layer is verified by clean build, unit tests 120/120 and SITL (incl. the firmware AGL matching an independent int16 reference 519/519 on
.TERtiles). Not yet measured on SDIO-SD boards (H7 class) — the design is driver-independent (cache-only reads, async IO, health gating), but "at least as good on SDIO" is reasoning, not measurement. SDIO testers very welcome.Integration notes
c177c2e27: direct-read cache, 10-bit.TERtiles, 2 m steps). Re-verified there: clean build, unit 120/120, SITL scenarios incl. the engage decode check (519/519 vs an independent int16 reference). Hardware/field evidence on this base is still pending — F4 no longer builds terrain by default, so it will come from an H7.maintenance-10.xholds boxId 60 / permanentId 69 for AUTO SPEED; this PR's box is boxId 61 / permanentId 70.Roadmap (after this proves itself)
Terrain-relative waypoints · terrain-aware RTH · geozone awareness — deliberately later, on top of this foundation.
Credits: error414's terrain data layer made all of this possible — this PR only reads what #11438 provides. He saw the feature before anyone else and has been testing it since.
One personal note to close: I fly these hills every week. The flights above were the first time I crossed that ridge with my hands off the pitch stick — the plane just followed the ground, said nothing, and had nothing to say. That quiet is the feature.