Skip to content

PR - Danger Zone Framework - #105

Draft
atharva-airbound wants to merge 20 commits into
airbound-developfrom
pr-airbound-danger_zone
Draft

PR - Danger Zone Framework#105
atharva-airbound wants to merge 20 commits into
airbound-developfrom
pr-airbound-danger_zone

Conversation

@atharva-airbound

Copy link
Copy Markdown

Implementation of the danger zone framework.

@atharva-airbound

Copy link
Copy Markdown
Author

@greptileai could you have a look?

@greptile-apps

greptile-apps Bot commented Jun 15, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces the Danger Zone framework — a 5-level escalating failsafe for quadplane VTOL flight that disables weathervaning (zone 2), relaxes attitude control (zone 3), triggers an auto-bailout to QLOITER (zone 4), and deploys the parachute + disarms (zone 5) based on pitch error, control effort, and tilt-motor saturation metrics evaluated at 50 Hz.

  • The core AP_DangerZone library is self-contained and vehicle-agnostic; zone definitions, metric getters, and actions live in ArduPlane/danger_zone_config.cpp. The ring-buffer / check infrastructure in DZ_Check.cpp is well-structured.
  • Attitude-controller changes in AC_AttitudeControl add pitch setpoint relaxation, yaw rate disabling, and roll gain suppression triggered by the zone 3 flag; the pitch relaxation uses a Euler to_euler/from_euler conversion that is numerically degenerate at the ±90° pitch where a tailsitter hovers.
  • The zone state machine and ring buffers have no reset path between arm/disarm cycles, so a vehicle that lands after reaching zone 4 may immediately re-enter zone 4 or trigger a parachute action at the start of the next flight.

Confidence Score: 3/5

This PR introduces emergency flight-safety actions (mode change to QLOITER, parachute deployment, disarm) driven by a new zone state machine — three issues in that path need resolution before this is safe to fly.

The zone-4 QLOITER mode switch has no fallback if GPS is unavailable; a silent failure at the moment the aircraft is in an extreme attitude leaves it without a recovery path. The zone state and ring buffers are never reset between arm/disarm cycles, so a vehicle that entered zone 4 during a flight could immediately re-trigger the same emergency actions at the very start of the next flight. The pitch-setpoint relaxation in zone 3 uses Euler to_euler/from_euler at the ~90° pitch angle where tailsitters hover, which is numerically degenerate and could produce an uncommanded yaw or roll impulse instead of the intended pitch reduction. Together these three issues affect the core safety path of the framework.

ArduPlane/danger_zone_config.cpp (zone 4 bailout and missing reset on disarm), libraries/AC_AttitudeControl/AC_AttitudeControl.cpp (Euler gimbal lock in pitch relaxation), and libraries/AP_DangerZone/AP_DangerZone_config.h (global default-on enabling).

Important Files Changed

Filename Overview
ArduPlane/danger_zone_config.cpp Zone table and vehicle actions. Zone 4 QLOITER bailout has no fallback if mode switch fails (GPS absent). Zone state and ring buffers are not reset on disarm.
libraries/AC_AttitudeControl/AC_AttitudeControl.cpp Pitch relaxation applies euler to_euler/from_euler at potential 90° pitch (gimbal lock territory for tailsitters). Yaw rate disabling with _dz_z3_active is unconditional within the attitude controller.
libraries/AP_DangerZone/AP_DangerZone_config.h AP_DANGERZONE_ENABLED defaults to 1 globally with the comment 'can be tightened later' — this unconditionally adds new parameters and RAM usage to all AC_AttitudeControl consumers.
libraries/AP_DangerZone/AP_DangerZone.cpp Core zone state machine: evaluates entry/exit checks, advances by one level per call. Logic is sound; no reset mechanism between flights.
libraries/AP_DangerZone/DZ_Check.cpp Ring buffer and check evaluation logic. Buffer is never reset between arm/disarm cycles; stale statistics could affect zone decisions on re-arm.
libraries/AP_DangerZone/DZ_Check.h DZ_CHECK_BUFFER_SAMPLES uses integer division; could undersize buffer if window_ms is changed to a non-multiple of 1000. Static globals add ~28 KB RAM for all windowed checks.
libraries/AC_AttitudeControl/AC_AttitudeControl.h Adds RELX_TC/RELX_ANG/RELX_EN params and _dz_z3_active/_dz_z3_factor fields to base class shared by all vehicles (Copter, Sub, etc.) because AP_DANGERZONE_ENABLED defaults to 1 globally.
libraries/AC_AttitudeControl/AC_AttitudeControl_Multi.cpp Adds update_roll_gain_suppression() called only when tailsitter is enabled; cleanly gates roll I-term reset and motor factor on _dz_z3_active.
ArduPlane/parachute.cpp New parachute_release_with_disarm() correctly retains alt_min check and calls existing parachute_release(). Pre-existing double landing-gear deploy in parachute_manual_release() is unchanged.
ArduPlane/RC_Channel.cpp RC parachute switch now bypasses parachute_manual_release() for tailsitters, calling parachute_release_with_disarm() directly; altitude check is preserved.
ArduPlane/tailsitter.cpp Zone 2 weathervane gain ramp uses linear_interpolate over 500 ms; correctly resets weathervane on zero gain. Zone 3 flag is set each output() cycle.
libraries/AP_Motors/AP_MotorsTailsitter.cpp Roll thrust suppression via _roll_gain_suppression_factor applied before mixing; straightforward and correctly bounded.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Z1["Zone 1 – Normal VTOL\n(baseline)"]
    Z2["Zone 2 – Weathervane disabled\n(gain ramped to 0 over 500 ms)"]
    Z3["Zone 3 – Attitude relaxed\n(pitch setpoint → 0, yaw rate disabled,\nroll gain suppressed)"]
    Z4["Zone 4 – Auto-bailout\n(mode → QLOITER)"]
    Z5["Zone 5 – Emergency\n(parachute + disarm)"]

    Z1 -->|"control effort > 0.1"| Z2
    Z2 -->|"pitch err mean > 20° OR\npitch oscillation range > 10° OR\nabs(att pitch) > 45°"| Z3
    Z3 -->|"raw pitch < 40° for 200 ms OR\ntilt saturation same-dir for 100 ms"| Z4
    Z4 -->|"raw pitch < -15° for 100 ms OR\ntilt saturation same-dir for 500 ms"| Z5

    Z2 -->|"mean pitch err < 5° AND\npeak pitch err < 10° AND\ncontrol effort < 0.08 (5 s window)"| Z1
    Z3 -->|"mean pitch err < 10° AND\npeak pitch err < 15° (5 s window)"| Z2
    Z4 -->|"mean pitch err < 20° AND\npeak pitch err < 30° (5 s window)"| Z3
    Z5 -.->|"no exit – terminal"| Z5

    style Z5 fill:#ff4444,color:#fff
    style Z4 fill:#ff8800,color:#fff
    style Z3 fill:#ffcc00
    style Z2 fill:#aaffaa
    style Z1 fill:#ddffdd
Loading

Reviews (1): Last reviewed commit: "Make `get_current_danger_zone()` return ..." | Re-trigger Greptile

Comment thread libraries/AC_AttitudeControl/AC_AttitudeControl.cpp Outdated
Comment thread ArduPlane/danger_zone_config.cpp
Comment thread ArduPlane/danger_zone_config.cpp Outdated
Comment thread libraries/AP_DangerZone/AP_DangerZone_config.h
Comment thread libraries/AP_DangerZone/DZ_Check.h Outdated
Comment on lines +36 to +37
static const uint16_t DZ_CHECK_BUFFER_SAMPLES =
(DZ_BUFFER_MAX_WINDOW_MS / 1000) * DZ_UPDATE_RATE_HZ;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Integer division in DZ_CHECK_BUFFER_SAMPLES can silently undersize the buffer

(DZ_BUFFER_MAX_WINDOW_MS / 1000) * DZ_UPDATE_RATE_HZ truncates before multiplying. With the current defaults (10000 / 1000 = 10 × 50 = 500) the result is exact, but any future change to a non-multiple-of-1000 window — e.g., 9500 ms — would yield 9 × 50 = 450 samples, silently covering only 9 000 ms of data instead of the requested 9 500 ms. The full() guard would then incorrectly report a complete window when it is not. Use (DZ_BUFFER_MAX_WINDOW_MS * DZ_UPDATE_RATE_HZ) / 1000 to prevent this.

Suggested change
static const uint16_t DZ_CHECK_BUFFER_SAMPLES =
(DZ_BUFFER_MAX_WINDOW_MS / 1000) * DZ_UPDATE_RATE_HZ;
static const uint16_t DZ_CHECK_BUFFER_SAMPLES =
(DZ_BUFFER_MAX_WINDOW_MS * DZ_UPDATE_RATE_HZ) / 1000;

Comment thread ArduPlane/danger_zone_config.cpp Outdated
Comment on lines +195 to +197
// // Mission resumption after exiting Zone 4
// if (level < 4 && danger_zone_last_level >= 4) {
// }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Commented-out mission-resumption block should be tracked in an issue, not left in source

The block // Mission resumption after exiting Zone 4 contains placeholder logic that is intentionally unimplemented. Leaving commented-out code of this kind in safety-critical flight code makes it hard to distinguish deliberate stubs from unfinished work. Recommend removing the block and tracking the feature in a ticket, or at minimum adding a // TODO(#<issue>): reference.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@atharva-airbound
atharva-airbound force-pushed the pr-airbound-danger_zone branch from 0ce8a98 to 80d2249 Compare June 25, 2026 09:42
@atharva-airbound
atharva-airbound changed the base branch from airbound-develop to pr-airbound-4575-rc7-fixes July 13, 2026 07:14
Atharva Sawant and others added 20 commits July 30, 2026 14:30
- Added library directory and files files necessary for compilation
- Implemented the Checks classes
- Registered AP_DangerZone in the build system
- Registered the update loop to run at 50Hz
- Initialized danger_zone.cpp with a skeleton for the zones and conditions
- Add RANGE stat selector
- Remove hysteresis condition
- Add a `DZ` message containing the current zone and satisfied entry and exit conditions for each update
- Add entry and exit bitmasks in DZ_Check that are set on every update
- Keep a running sum for the buffer to reduce time complexity of mean calculations
- Assign ringbuffer for only Window and Oscillation checks
- Remove the duration_ms value from Theshold checks
- Keep cache of 3 max/min values, and check against them when pushing to the cache
- Recompute the max/min when evicting the oldest cache entry
- Check for current danger zone level in tailsitter.cpp and disable weathervane gain above zone 2
- Linearly interpolate the gain over 500ms for the transition
- Scale desired pitch down (#75)
- Disable yaw rate (#75)
- Suppress roll rate gains (https://github.com/AirboundInc/ardupilot/tree/pr-add_roll_gain_suppression)

Note: Merged on top of the 67ff5118 commit when rebasing on top of 4.5.7.5-rc4
(cherry picked from commit 7d25d82)
- Implement metrics and conditions for zones 4 and 5
- Implement autobailout for zone 4, with a mode reason
- Implement autoparadeploy for zone 5
… of the index

- Rename danger_zone.cpp to danger_zone_config.cpp
- Also reset the module state when disarmed or not in VTOL mode
- Check current zone's entry conditions before returning to the previous zone, to avoid returning to the current zone on the next update
- Add a dwell timer to return to the zone we returned from, to prevent constant "flickering" between zones
…witch hysteresis timer

- `DZ_ENABLE` can enable/disable/only log Danger Zone level switching
- `DZ_HYST_TIMER` is the hysteresis timer for switching back to the previous zone (defaulted to 0)
- Expose `DZ_ENABLE` value in a function and modify existing vehicle code to use it to gate zone switching
@atharva-airbound
atharva-airbound force-pushed the pr-airbound-danger_zone branch from fef073c to 6ace3e4 Compare July 30, 2026 09:00
@atharva-airbound
atharva-airbound changed the base branch from pr-airbound-4575-rc7-fixes to airbound-develop July 30, 2026 09:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant