PR - Danger Zone Framework - #105
Conversation
|
@greptileai could you have a look? |
Greptile SummaryThis 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.
Confidence Score: 3/5This 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
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
Reviews (1): Last reviewed commit: "Make `get_current_danger_zone()` return ..." | Re-trigger Greptile |
| static const uint16_t DZ_CHECK_BUFFER_SAMPLES = | ||
| (DZ_BUFFER_MAX_WINDOW_MS / 1000) * DZ_UPDATE_RATE_HZ; |
There was a problem hiding this comment.
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.
| 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; |
| // // Mission resumption after exiting Zone 4 | ||
| // if (level < 4 && danger_zone_last_level >= 4) { | ||
| // } |
There was a problem hiding this comment.
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!
0ce8a98 to
80d2249
Compare
- Added library directory and files files necessary for compilation - Implemented the Checks classes
…xt and current zone
- 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
… to prevent truncation
fef073c to
6ace3e4
Compare
Implementation of the danger zone framework.