docs: restructure into docs/, CLAUDE.md rules-only, DIR v1.2, handover v2.24
This commit is contained in:
parent
aae70776a0
commit
5551173b7a
234
CLAUDE.md
234
CLAUDE.md
@ -1,7 +1,17 @@
|
|||||||
# CLAUDE.md — Rules for Claude on the Argonaut 3 Project
|
# CLAUDE.md — Rules for Claude on the Argonaut 3 Project
|
||||||
**Version:** 3.6
|
**Version:** 3.7
|
||||||
**Updated:** 11 July 2026
|
**Updated:** 11 July 2026
|
||||||
|
|
||||||
|
**Changelog 3.7 (doc restructure):** This file is now rules-only. Current build state, open
|
||||||
|
items, and the session-by-session changelog moved to `docs/handover.md` — removed from here:
|
||||||
|
the live data-lake variable table, the FastAPI endpoint live/deferred lists, the full
|
||||||
|
ardupilot_manager FD-exhaustion narrative, and the SESSION LOG (now the handover's Changelog
|
||||||
|
section; trigger rules for what to log stay here, entries do not). All project docs (handover,
|
||||||
|
DIR, UI design, failsafe design, sim/ML plan, mission/recording lifecycle) moved into the repo
|
||||||
|
under `docs/`, git-tracked — corrected the MUST-NOT bullet and Documentation Discipline section
|
||||||
|
that said docs never go to Gitea; that policy is now reversed. Reference table updated to `docs/`
|
||||||
|
paths, filenames carry no version number (the `Version:` field inside each file is authoritative).
|
||||||
|
|
||||||
**Changelog 3.6:** Added measurement-before-diagnosis, step-back-on-break,
|
**Changelog 3.6:** Added measurement-before-diagnosis, step-back-on-break,
|
||||||
one-restart-then-verify-9001, and Cockpit widget re-import rules to Working Style.
|
one-restart-then-verify-9001, and Cockpit widget re-import rules to Working Style.
|
||||||
Corrected the Documentation discipline note that incorrectly claimed CLAUDE.md is never
|
Corrected the Documentation discipline note that incorrectly claimed CLAUDE.md is never
|
||||||
@ -13,11 +23,18 @@ sessions).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
**This file is rules only — how Claude works.** Current build state, open items, and the
|
||||||
|
session changelog live in `docs/handover.md`. Design intent ("why") lives in
|
||||||
|
`docs/design_intent_register.md`. Do not look for state here.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## PRIME DIRECTIVES — These override everything else
|
## PRIME DIRECTIVES — These override everything else
|
||||||
|
|
||||||
### 1. Research before acting. Always.
|
### 1. Research before acting. Always.
|
||||||
Before any technical decision, code change, or command:
|
Before any technical decision, code change, or command:
|
||||||
- Read project docs (handover, UI design, DIR, this file) and search past chat history
|
- Read project docs (`docs/handover.md`, `docs/ui_design.md`, `docs/design_intent_register.md`,
|
||||||
|
this file) and search past chat history
|
||||||
- Read source code and official docs before acting; GitHub source/issues are more
|
- Read source code and official docs before acting; GitHub source/issues are more
|
||||||
authoritative than official docs for Cockpit/BlueOS specifics
|
authoritative than official docs for Cockpit/BlueOS specifics
|
||||||
- Confirm facts from sources, not from memory
|
- Confirm facts from sources, not from memory
|
||||||
@ -167,29 +184,9 @@ backup because this was deferred — do not repeat.
|
|||||||
- ALWAYS use hyphens — Cockpit normalises underscores to hyphens in IDs.
|
- ALWAYS use hyphens — Cockpit normalises underscores to hyphens in IDs.
|
||||||
- HTTP POST to mavlink2rest does NOT work for data lake injection. MAVLink UDP injection
|
- HTTP POST to mavlink2rest does NOT work for data lake injection. MAVLink UDP injection
|
||||||
from external scripts does NOT work. Tested and confirmed — do not suggest them.
|
from external scripts does NOT work. Tested and confirmed — do not suggest them.
|
||||||
- Current data lake variables published by `cockpit_bridge` (19 total — 8 telemetry + 11 mode-profile flags):
|
- Current full variable list (which ones exist, source topic, meaning) is in
|
||||||
|
`docs/handover.md` §0 — not repeated here. It changes as flags/telemetry are added;
|
||||||
| Variable | Source topic | Meaning |
|
keeping one copy is the point.
|
||||||
|---|---|---|
|
|
||||||
| `external/rov-failsafe` | `/rov/failsafe` | 0=GREEN, 1=AMBER, 2=RED |
|
|
||||||
| `external/rov-depth` | `/rov/depth` | Depth in metres |
|
|
||||||
| `external/rov-voltage` | `/mavros/battery` | Battery voltage |
|
|
||||||
| `external/rov-heading` | `/mavros/mavros/compass_hdg` | Compass heading degrees |
|
|
||||||
| `external/rov-ms` | `/rov/mission/status` | Mission state integer |
|
|
||||||
| `external/rov-mp` | `/rov/mission/status` | Mission progress 0-100% |
|
|
||||||
| `external/rov-recording` | `/rov/recording/active` | 1=recording, 0=not recording |
|
|
||||||
| `external/rov-altitude` | `/ping2/altitude` | Ping2 altitude metres (-1.0=NO SONAR) |
|
|
||||||
| `external/rov-mode` | `/rov/mode/profile` | Active profile name: ROV / AUV / hybrid |
|
|
||||||
| `external/rov-mode-base` | `/rov/mode/profile` | Base mode: 0=ROV, 1=AUV |
|
|
||||||
| `external/rov-hilt` | `/rov/mode/profile` | HILT: 1=present, 0=absent |
|
|
||||||
| `external/rov-health-role` | `/rov/mode/profile` | W1: 0=advisory, 1=gate |
|
|
||||||
| `external/rov-rec-control` | `/rov/mode/profile` | Recording: 0=manual, 1=auto |
|
|
||||||
| `external/rov-nogo-gate` | `/rov/mode/profile` | No-go: 0=relaxed, 1=enforced |
|
|
||||||
| `external/rov-rec-reminder` | `/rov/mode/profile` | ARM reminder: 1=on, 0=off |
|
|
||||||
| `external/rov-return-mode` | `/rov/mode/profile` | Return: 0=operator-selectable, 1=autonomous |
|
|
||||||
| `external/rov-live-telem` | `/rov/mode/profile` | Telemetry: 0=on, 1=predive-only |
|
|
||||||
| `external/rov-nav-display` | `/rov/mode/profile` | Nav: 1=shown, 0=autonomy-only |
|
|
||||||
| `external/rov-camera-role` | `/rov/mode/profile` | Camera: 0=pilot-primary, 1=record-only |
|
|
||||||
|
|
||||||
### FastAPI backend (rov_api)
|
### FastAPI backend (rov_api)
|
||||||
- `rov_api` package provides the HTTP-to-ROS2 bridge on port 8081. Runs as
|
- `rov_api` package provides the HTTP-to-ROS2 bridge on port 8081. Runs as
|
||||||
@ -197,11 +194,6 @@ backup because this was deferred — do not repeat.
|
|||||||
- Architecture: uvicorn/FastAPI in the main thread, an embedded rclpy node
|
- Architecture: uvicorn/FastAPI in the main thread, an embedded rclpy node
|
||||||
(`RovApiNode`) spinning in a daemon background thread. HTTP handlers call thread-safe
|
(`RovApiNode`) spinning in a daemon background thread. HTTP handlers call thread-safe
|
||||||
methods on the node. Do NOT call `rclpy.spin()` more than once.
|
methods on the node. Do NOT call `rclpy.spin()` more than once.
|
||||||
- Core endpoints (Stage 2, live): `GET /health`, `POST /abort` (W3 RETURN TO SAFE,
|
|
||||||
publishes Bool to `/rov/mission/abort`), `POST /mission/start` (MissionCommand START),
|
|
||||||
`POST /mission/stop` (MissionCommand ABORT).
|
|
||||||
- Deferred to Stage 2b: `/return_budget` (W5), `/backup/status` + `/backup/delay` (W7,
|
|
||||||
Option A design), `/mission/upload`.
|
|
||||||
- **W3 abort path is vehicle-layer:** publishes Bool(true) to `/rov/mission/abort`, which
|
- **W3 abort path is vehicle-layer:** publishes Bool(true) to `/rov/mission/abort`, which
|
||||||
failsafe_monitor latches as `flag_manual_abort` → FSM NORMAL → HOLD_AND_RECOVER. This is
|
failsafe_monitor latches as `flag_manual_abort` → FSM NORMAL → HOLD_AND_RECOVER. This is
|
||||||
DIR-5 return-to-safe, NOT the mission ABORT service. Do not confuse the two.
|
DIR-5 return-to-safe, NOT the mission ABORT service. Do not confuse the two.
|
||||||
@ -209,6 +201,8 @@ backup because this was deferred — do not repeat.
|
|||||||
`sudo pip3 install fastapi uvicorn --break-system-packages --ignore-installed typing_extensions`.
|
`sudo pip3 install fastapi uvicorn --break-system-packages --ignore-installed typing_extensions`.
|
||||||
The `--ignore-installed typing_extensions` flag is required — the Debian-managed
|
The `--ignore-installed typing_extensions` flag is required — the Debian-managed
|
||||||
typing_extensions has no pip RECORD file and blocks the install otherwise.
|
typing_extensions has no pip RECORD file and blocks the install otherwise.
|
||||||
|
- Which endpoints are live vs. deferred is current build state — see `docs/handover.md`
|
||||||
|
§0 / Phase Task Status, not here.
|
||||||
|
|
||||||
### Deploy files (version-controlled)
|
### Deploy files (version-controlled)
|
||||||
- systemd units and launch wrappers live outside the workspace but are version-controlled
|
- systemd units and launch wrappers live outside the workspace but are version-controlled
|
||||||
@ -220,25 +214,17 @@ backup because this was deferred — do not repeat.
|
|||||||
- `deploy/README.md` documents locations, pip deps, and install procedure.
|
- `deploy/README.md` documents locations, pip deps, and install procedure.
|
||||||
- `/home/ubuntu/ros2_ws` is a symlink to `/data/ros2_ws` — both wrappers rely on it.
|
- `/home/ubuntu/ros2_ws` is a symlink to `/data/ros2_ws` — both wrappers rely on it.
|
||||||
|
|
||||||
### Recording architecture (DIR-9a — ARM-bounded black-box)
|
### Recording architecture (mechanism — intent is DIR-9a, `docs/design_intent_register.md`)
|
||||||
- Recording is a **single ARM-bounded tier** for black-box/debug/ML/fault-finding via
|
|
||||||
`recording_manager`. **AUV:** starts on ARM, stops on DISARM. **ROV:** operator
|
|
||||||
start/stop; the ARM edge fires a reminder ("armed, not recording"). Keyed to the
|
|
||||||
`recording_control` flag, not the mode. (Supersedes the old continuous DIR-9 model.)
|
|
||||||
- **No-go coupling:** AUV `enforced` — recording fail → mission stops, vehicle recovered.
|
|
||||||
ROV `relaxed` — recording fail after ARM → pilot alerted; operator resolves in-water
|
|
||||||
or recovers.
|
|
||||||
- Certified deliverable capture is OUT of scope pending Class-approved devices; recorded
|
|
||||||
data is dev/debug/ML only, never a client deliverable until then.
|
|
||||||
- The recorder manages a `ros2 bag record` subprocess — MCAP format, zstd compression,
|
- The recorder manages a `ros2 bag record` subprocess — MCAP format, zstd compression,
|
||||||
500MB bag splitting, 10GB free-space retention on `/data`.
|
500MB bag splitting, 10GB free-space retention on `/data`. Bags to
|
||||||
|
`/data/bags/dive_<timestamp>/` on NVMe.
|
||||||
- **`foxglove_bridge` is visualisation only — it does NOT record MCAP.** Correct this
|
- **`foxglove_bridge` is visualisation only — it does NOT record MCAP.** Correct this
|
||||||
wherever the old "MCAP via foxglove_bridge" claim appears.
|
wherever the old "MCAP via foxglove_bridge" claim appears.
|
||||||
- Bags written to `/data/bags/dive_<timestamp>/` on NVMe.
|
- **Storage lever (no reliable in-field server backup):** LOWER PUBLISH RATES on heavy
|
||||||
- No camera in the topic set currently — recording ~2.5 MB/s (nav/MAVLink only).
|
streams (keep topics present for debug) is the primary lever; ARM-bounded runs
|
||||||
- **Storage (no reliable in-field server backup):** primary lever is LOWER PUBLISH RATES
|
self-limit; retention cap tunable; NVMe expansion later. Topic/rate review is open.
|
||||||
on heavy streams (keep topics present for debug); ARM-bounded runs self-limit; retention
|
- Current implementation status (recording_manager vs. governing DIR-9a) is tracked in
|
||||||
cap tunable; NVMe expansion later. Topic/rate review is an open action.
|
`docs/handover.md`, not here — known drift exists, see there.
|
||||||
|
|
||||||
### Operating-mode profiles (DIR-1/2, mode_profiles.yaml)
|
### Operating-mode profiles (DIR-1/2, mode_profiles.yaml)
|
||||||
- One widget set; behaviour is set by flags from a mode profile. Dominant axis is HILT
|
- One widget set; behaviour is set by flags from a mode profile. Dominant axis is HILT
|
||||||
@ -260,11 +246,15 @@ backup because this was deferred — do not repeat.
|
|||||||
|
|
||||||
### BlueOS / MAVLink router
|
### BlueOS / MAVLink router
|
||||||
- **MAVLinkRouter** is the confirmed router. Do NOT recommend MAVP2P — BlueOS itself
|
- **MAVLinkRouter** is the confirmed router. Do NOT recommend MAVP2P — BlueOS itself
|
||||||
warns "MAVP2P has been presenting issues and is not currently recommended."
|
warns "MAVP2P has been presenting issues and is not currently recommended." Tried and
|
||||||
- MAVP2P was tried and reverted. Do not suggest it again.
|
reverted once already — do not suggest it again.
|
||||||
- **ardupilot-manager FD exhaustion — CORRECTED ROOT CAUSE (9 Jul 2026).** The earlier claim that this was "fixed permanently" by `/etc/docker/daemon.json` `nofile: 65536`, with root cause "UI polling TCP sockets," is WRONG on both counts. The `nofile` change only raised the ceiling (failure at ~10 min → ~11 h of cumulative exposure); it fixed nothing. The leaked FDs are `/dev/i2c-1` handles, NOT TCP sockets. Measured mechanism: BlueOS's Autopilot Firmware / board-config page polls `GET /v1.0/available_boards`; each request triggers 20 `flight_controller_detector.linux.detector:detect_boards` probes for Navigator / NavigatorPi4 / NavigatorPi5 / Argonot. Each Linux-board probe opens `/dev/i2c-1` and never closes it on failure. We run a Pixhawk 2.4.8 on USB serial (PlatformType.Serial), so every probe fails and every probe leaks. Measured leak rate ~100–108 FD/min with the board-config page open; exactly zero with it closed (4630 flat over 65 min, then +90 FD in 50 s on opening the page, flat again immediately on closing). Confirmed cause-and-effect, reversible. Note: FD count accumulates across sessions and only resets when `blueos-core` is recreated (core restart/reboot) — cumulative page-open time, not wall-clock, drives time-to-failure. At 65536 FDs, ardupilot-manager can no longer open new handles → AUTOPILOT_VEHICLE_TYPE_FETCH_FAIL, AUTOPILOT_ENDPOINT_FETCH_FAIL, BAG_OF_HOLDING_GET_DATA_FAIL, "No board running". Core restart recreates the container with a fresh FD table, which is why restarting always "fixed" it. This is an upstream BlueOS 1.4.3 bug; do NOT patch BlueOS (updates would revert it).
|
- **`ardupilot_manager` leaks `/dev/i2c-1` file descriptors** on every failed Linux-board
|
||||||
- **Mitigation (dev):** do not leave the BlueOS Autopilot Firmware / board-config page open. Open, act, close. Other BlueOS pages and Cockpit are unaffected.
|
probe, driven by leaving the BlueOS Autopilot Firmware / board-config page open. Do not
|
||||||
- **Mitigation (field) — NOT YET IMPLEMENTED:** operator discipline is not a field-safe control. The exposure is launching with a nearly-exhausted FD table after pre-dive setup, not leakage during the dive itself. Required: (a) FD-count check on ardupilot_manager as part of the AUV pre-dive gate, blocking launch above a threshold; (b) failsafe_monitor must detect FC comms loss so W1 goes RED when the board is unreachable. See open items.
|
leave that page open — open, act, close. **Do NOT patch BlueOS or ArduPilot** for this;
|
||||||
|
it's an upstream defect and any update would revert a local patch.
|
||||||
|
Full root-cause narrative, measurements, and field-mitigation status: `docs/handover.md`
|
||||||
|
§15a — this bug's root cause was wrongly diagnosed once already (v2.10–v2.21); do not
|
||||||
|
repeat old claims about TCP sockets or "nofile fixed it" without reading §15a first.
|
||||||
|
|
||||||
### Hardware facts (do not re-question)
|
### Hardware facts (do not re-question)
|
||||||
- **RPi5 storage:** boots from SD card (`/dev/mmcblk0`). NVMe holds DATA ONLY (`/data` —
|
- **RPi5 storage:** boots from SD card (`/dev/mmcblk0`). NVMe holds DATA ONLY (`/data` —
|
||||||
@ -317,14 +307,13 @@ backup because this was deferred — do not repeat.
|
|||||||
pushed to Gitea — never overwritten from chat history.
|
pushed to Gitea — never overwritten from chat history.
|
||||||
- UFW rules before starting new services on new ports.
|
- UFW rules before starting new services on new ports.
|
||||||
|
|
||||||
### Security — deferred to pre-field hardening pass
|
### Security — pre-field hardening pass (policy)
|
||||||
- **UFW:** currently inactive on RPi5. Deferred because there is no online exposure
|
- **UFW enable procedure (when triggered):** allow SSH (22), operator TCP (8765, 9001,
|
||||||
(Tailscale disabled, no proxy rules to the Pis). When enabling: allow SSH (22),
|
8081), and all traffic from LAN subnet 192.168.1.0/24 (covers ROS2 DDS dynamic ports —
|
||||||
operator TCP (8765, 9001, 8081), and all traffic from LAN subnet 192.168.1.0/24
|
do NOT try to pin them individually), then default deny incoming.
|
||||||
(covers ROS2 DDS dynamic ports — do NOT try to pin them individually), then default
|
- **Bundle UFW with the RPi5 SSH password change** — both gated on the same trigger:
|
||||||
deny incoming. Bundle with SSH password change. Trigger: before remote ops or field.
|
before remote ops or field deployment. Do not do one without the other.
|
||||||
- **RPi5 SSH password:** weak dev password, password auth enabled. Change to strong
|
- Current on/off status of both is tracked in `docs/handover.md` Open Items, not here.
|
||||||
password before field deployment.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -346,32 +335,39 @@ backup because this was deferred — do not repeat.
|
|||||||
- Change the failsafe priority order without design review
|
- Change the failsafe priority order without design review
|
||||||
- Present prior-session state as current running state
|
- Present prior-session state as current running state
|
||||||
- Recommend MAVP2P as the MAVLink router
|
- Recommend MAVP2P as the MAVLink router
|
||||||
- Commit docs to Gitea — only code and README go to Gitea; docs live in Claude project knowledge
|
- Patch or diff-edit a project doc in `docs/` — always full-file replacement (see
|
||||||
|
Documentation discipline below), even though these files are now git-tracked
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Documentation discipline
|
## Documentation discipline
|
||||||
- Other project docs (handover, UI design, DIR, sim/ML plan) are maintained as COMPLETE
|
- All project docs now live in the repo under `docs/`, git-tracked, same as code:
|
||||||
replacement files uploaded to Claude project knowledge — never patches or fragments,
|
`docs/handover.md`, `docs/design_intent_register.md`, `docs/ui_design.md`,
|
||||||
never committed to Gitea.
|
`docs/failsafe_design.md`, `docs/simulation_ml_plan.md`, `docs/mission_recording_lifecycle.md`.
|
||||||
- CLAUDE.md is different: it IS version-controlled and committed to Gitea like any other
|
- They keep the **complete-file-replacement convention** even though git tracks them: full
|
||||||
repo file (normal patch/diff edits and commits apply to it, not the replacement-file
|
file rewrites on update, never patches/fragments to prose. Git diff/blame is the audit
|
||||||
workflow above).
|
trail; it is not how edits are authored.
|
||||||
|
- Filenames carry **no version number** — the `Version:` field inside each file is
|
||||||
|
authoritative, and a stale version number in a filename or cross-reference is exactly the
|
||||||
|
kind of drift this restructure (11 Jul 2026) was done to stop.
|
||||||
|
- CLAUDE.md (repo root) follows the same git-tracked convention as any other repo file.
|
||||||
|
- Claude project knowledge holds a mirror copy of `docs/` for chat context — re-upload after
|
||||||
|
any commit that changes these files.
|
||||||
|
- Server/infra docs (SymbyTech server, Harbor registry) and shopping/task lists are NOT part
|
||||||
|
of the `rov-autonomy` repo `docs/` folder — they stay project-knowledge-only.
|
||||||
- Version numbers and changelogs maintained on every update.
|
- Version numbers and changelogs maintained on every update.
|
||||||
- Only code, README files, and CLAUDE.md go to Gitea.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## SESSION LOG (bridges Claude Code → claude.ai chat)
|
## Session changelog — trigger rules (entries live in `docs/handover.md`)
|
||||||
|
|
||||||
**Why this exists:** Claude Code (VS Code) and the claude.ai chat are separate systems with
|
**Why this exists:** Claude Code (VS Code) and the claude.ai chat are separate systems with
|
||||||
no shared memory. Claude Code reads this file at session start but the claude.ai chat cannot
|
no shared memory. Claude Code reads this file and `docs/handover.md` at session start but the
|
||||||
see Claude Code's session history at all. This log is the bridge.
|
claude.ai chat cannot see Claude Code's session history at all. The Changelog section in
|
||||||
|
`docs/handover.md` is the bridge — this section holds only the rule for what goes in it.
|
||||||
|
|
||||||
**Format:** `YYYY-MM-DD — one-line summary of decision/change (commit hash if applicable)`
|
**Claude Code MUST append an entry to `docs/handover.md`'s Changelog at the end of any session
|
||||||
|
where one of these happened** — not for routine edits, refactors, or in-progress work:
|
||||||
**Claude Code MUST append an entry here at the end of any session where one of these
|
|
||||||
happened** — not for routine edits, refactors, or in-progress work:
|
|
||||||
- A design/architecture decision was made or changed (DIR-level, mode-flag behaviour,
|
- A design/architecture decision was made or changed (DIR-level, mode-flag behaviour,
|
||||||
failsafe priority, recording behaviour)
|
failsafe priority, recording behaviour)
|
||||||
- A hardware fact was confirmed or changed (wiring, storage layout, board config)
|
- A hardware fact was confirmed or changed (wiring, storage layout, board config)
|
||||||
@ -385,76 +381,12 @@ happened** — not for routine edits, refactors, or in-progress work:
|
|||||||
**Skip:** typo fixes, comment-only changes, WIP/incomplete work, anything already fully
|
**Skip:** typo fixes, comment-only changes, WIP/incomplete work, anything already fully
|
||||||
captured by the git commit message with no extra context needed.
|
captured by the git commit message with no extra context needed.
|
||||||
|
|
||||||
**Workflow:** Grant pastes new entries into the claude.ai chat when relevant; Claude folds
|
**Format:** `YYYY-MM-DD — one-line summary of decision/change (commit hash if applicable)`.
|
||||||
them into chat memory via the memory tool. This log is append-only — do not edit past
|
Append-only — do not edit past entries; correct forward with a new dated entry if something
|
||||||
entries; correct forward with a new dated entry if something needs revising.
|
needs revising.
|
||||||
|
|
||||||
- 2026-07-05 — Widget housekeeping: added W6 altitude widget, removed probe diagnostics
|
**Workflow:** Grant pastes new entries into the claude.ai chat when relevant; Claude folds
|
||||||
widget (`785ec56`)
|
them into chat memory via the memory tool.
|
||||||
- 2026-07-08 — rov_api: added GET /mode and POST /mode endpoints (validate-before-write +
|
|
||||||
/rov/mode/reload trigger); deployed and tested on RPi5, all paths verified (9109439)
|
|
||||||
- 2026-07-08 — Root-caused chronic widget "no data": cockpit_bridge (feeds Cockpit data
|
|
||||||
lake on :9001) launched but had no respawn, so a boot-time race left it permanently
|
|
||||||
dead. Added respawn=True to its launch Node. W8 mode indicator built + validated live
|
|
||||||
(render+data). W1/W8 confirmed GREEN/AUV once bridge up.
|
|
||||||
- 2026-07-08 — FLAGGED (open): recording_manager.py still implements superseded DIR-9
|
|
||||||
(continuous-from-boot recording), NOT the governing DIR-9a (ARM-bounded black-box:
|
|
||||||
AUV ARM-start/DISARM-stop, ROV operator manual + arm reminder). Design decision
|
|
||||||
exists only in handover + CLAUDE.md; code never updated. foxglove_mcap.launch.py
|
|
||||||
comments also still say DIR-9. Also: argonaut-stack.sh passes record:=${RECORD} but
|
|
||||||
rov_full.launch.py declares no such arg, so RECORD=false is silently ignored and
|
|
||||||
bags record regardless. Both deferred to a dedicated DIR-9a recorder-rework session
|
|
||||||
— do NOT wire RECORD into the current DIR-9 model.
|
|
||||||
- 2026-07-08 — Added respawn=True/respawn_delay=5.0 to failsafe_monitor,
|
|
||||||
motion_controller, ekf_filter_node, depth_node, mission_executor. Deliberately NOT
|
|
||||||
applied to: mavros_node (respawn could mask/worsen the chronic FCU-link instability
|
|
||||||
— belongs in that investigation), recording_manager (pending DIR-9a rework),
|
|
||||||
camera_node/feature_detector (perception not operational yet), foxglove_bridge
|
|
||||||
(viz-only). NOTE: failsafe_monitor respawn has a coverage gap during the 5s respawn
|
|
||||||
delay — no failsafe assessment in that window; flagged for future design
|
|
||||||
consideration.
|
|
||||||
- 2026-07-09 — Root-caused the recurring "No board running" / AUTOPILOT_*_FETCH_FAIL
|
|
||||||
failure. NOT the previously-documented TCP-socket/UI-polling cause. Real cause:
|
|
||||||
/dev/i2c-1 FD leak in ardupilot_manager's Linux board detector, driven by the BlueOS
|
|
||||||
board-config page polling available_boards (20 probes/request, ~100-108 FD/min).
|
|
||||||
Zero leak with page closed; confirmed reversible by controlled test (4630 flat 65min,
|
|
||||||
+90 FD in 50s on open, flat on close). nofile=65536 only delayed failure to ~11h
|
|
||||||
cumulative page-open time. Upstream BlueOS 1.4.3 bug — not patching. Corrected the
|
|
||||||
stale root cause in this file.
|
|
||||||
- 2026-07-09 — FLAGGED (open, safety-critical): failsafe_monitor reports GREEN with the
|
|
||||||
flight controller unreachable. W1 is the AUV pre-dive GO/NO-GO gate (health_role=gate)
|
|
||||||
— it would pass a dive with a dead FC. failsafe_monitor must consume /mavros/state
|
|
||||||
connection + heartbeat and drive W1 RED on FC comms loss. W1 status line should carry
|
|
||||||
cause text (e.g. "Vehicle Disconnected", "Heartbeat Lost", "No Board Running"). Also
|
|
||||||
flagged: no FD-count check on ardupilot_manager in the pre-dive gate.
|
|
||||||
- 2026-07-11 — RESOLVED the 2026-07-09 flagged safety-critical fault: failsafe_monitor now
|
|
||||||
drives W1 RED on FC comms loss. Four commits: (1) 86b2728 — comms loss forces
|
|
||||||
unconditional RED (was scored as one marginal parameter, only reached AMBER) plus
|
|
||||||
fcu_ever_connected latch so the startup window before any heartbeat is not reported GREEN;
|
|
||||||
cause text "Heartbeat Lost"/"Vehicle Disconnected" added to FailsafeStatus.message per
|
|
||||||
DIR-7. (2) 1b0050d — FSM recovery fix: RETURN_TO_SAFE was a terminal trap (only
|
|
||||||
HOLD_AND_RECOVER could return to NORMAL); now both recover when conditions clear. (3)
|
|
||||||
dbe9515 — cockpit_bridge publishes new data lake variable rov-failsafe-cause (substring of
|
|
||||||
message before first "|"; empty when nominal). (4) 3409ae5 — W1 shows state word on
|
|
||||||
#w1-message (state-driven, no flicker) and cause text on #w1-footer, footer restyled red
|
|
||||||
11px, blank when nominal. All four verified live end-to-end: kill mavros -> RED +
|
|
||||||
"Heartbeat Lost", restart -> recover to GREEN. Also corrected: cockpit_bridge was
|
|
||||||
publishing failsafe_state (FSM, can be 3+) not assessment_state (0/1/2) to rov-failsafe —
|
|
||||||
fixed in dbe9515's sibling commit d854641; this was the real cause of the day's "widgets
|
|
||||||
show no data" (value 3 out of W1's range), compounded by a restart-storm wedging 9001.
|
|
||||||
- 2026-07-11 — PARKED for next sessions, in priority order: (a) DOC RESTRUCTURE (do first
|
|
||||||
next session) — consolidate all current-state into the handover as single source of truth,
|
|
||||||
strip state from CLAUDE.md leaving rules only, make SESSION LOG the handover's changelog;
|
|
||||||
DIR and domain docs stay separate. Reason: current-state is smeared across CLAUDE.md,
|
|
||||||
handover and SESSION LOG, causing loss of situational awareness. (b) Mode-dependent
|
|
||||||
failsafe recovery: AUV (health_role=gate) commits to recovery on condition-clear, ROV
|
|
||||||
(advisory) holds AMBER and hands to operator to decide — requires failsafe_monitor to read
|
|
||||||
the mode profile (currently mode-blind). (c) Auto-recovery: failsafe_monitor should attempt
|
|
||||||
to re-establish the FC link itself and only escalate/hold RED if it cannot — real-world has
|
|
||||||
no operator to restore comms. (d) Dedicated "string cause" field in FailsafeStatus.msg to
|
|
||||||
replace the message-substring shortcut in cockpit_bridge. (e) Operator control surface
|
|
||||||
(start/pause/stop buttons via rov_api) and one-command stack startup+healthcheck. (f) DIR
|
|
||||||
and handover updates for the above still owed this session.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -462,15 +394,15 @@ entries; correct forward with a new dated entry if something needs revising.
|
|||||||
|
|
||||||
| Document | Purpose |
|
| Document | Purpose |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `ROV_Project_Handover_v2_21.md` | Master reference — architecture, environment, commands, status board |
|
| `docs/handover.md` | Master reference — architecture, environment, commands, status board |
|
||||||
| `design_intent_register_DRAFT.md` | DIR-1…DIR-11 (+ DIR-9a) — owner/operator intent, source of truth for *why*; recording now ARM-bounded per DIR-9a |
|
| `docs/design_intent_register.md` | DIR-1…DIR-12 — owner/operator intent, source of truth for *why*; recording ARM-bounded per DIR-9a |
|
||||||
| `ROV_Failsafe_Design_v2_0.md` | Failsafe state machine, sensor roadmap, priority order |
|
| `docs/failsafe_design.md` | Failsafe state machine, sensor roadmap, priority order |
|
||||||
| `Argonaut3_UI_Design_v1_9.md` | Cockpit widget specs (W0–W7) and confirmed data paths |
|
| `docs/ui_design.md` | Cockpit widget specs (W0–W8) and confirmed data paths |
|
||||||
| `Mission_and_Data-Recording_Lifecycle_for_AUV_ROV_Systems...md` | Lifecycle research backing DIR-9/10 |
|
| `docs/mission_recording_lifecycle.md` | Lifecycle research backing DIR-9/9a/10 |
|
||||||
| `Argonaut3_Simulation_ML_Tooling_Plan_v1_0.md` | Simulation + ML tooling: dev-PC tiers, cloud options/cost, training assets/datasets/models, PCIe Gen 3 decision |
|
| `docs/simulation_ml_plan.md` | Simulation + ML tooling: dev-PC tiers, cloud options/cost, training assets/datasets/models, PCIe Gen 3 decision |
|
||||||
|
|
||||||
> When referencing these, use the latest version present in the project folder — do not
|
> Filenames carry no version number — check the `Version:` field inside the file itself.
|
||||||
> cite a version number from memory if a newer file exists.
|
> Do not cite a version number from memory.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
466
docs/design_intent_register.md
Normal file
466
docs/design_intent_register.md
Normal file
@ -0,0 +1,466 @@
|
|||||||
|
# Design Intent Register — Argonaut 3
|
||||||
|
|
||||||
|
**Version:** 1.2
|
||||||
|
**Date:** 11 July 2026
|
||||||
|
**File:** `docs/design_intent_register.md` (repo-tracked, no version number in filename)
|
||||||
|
|
||||||
|
**Purpose of this register:** Capture operator/owner *intent* — what each behaviour MEANS and MUST/MUST NOT do — separately from mechanism (topics, code). Mechanism is recorded elsewhere. This register is the source of truth for *why*, against which any implementation is validated. Every entry dated and attributed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
| Version | Date | Change |
|
||||||
|
|---|---|---|
|
||||||
|
| 1.2 | 11 Jul 2026 | **DIR-7's FC-connectivity MUST — RESOLVED**, four commits 11 Jul 2026 (full detail in `docs/handover.md` Changelog); open-flag callout updated accordingly. **DIR-7 gains two addenda:** in-mission recovery is mode-dependent (AUV commits to recovery on condition-clear, does not auto-resume; ROV holds and hands the continue-or-recover decision to the operator); and in-mission AMBER runtime-response (AMBER means action-required, not just informational, once a mission is underway — response branches by mode, AUV's autonomous curtailment logic left unspecified pending further design). **New "Parked Design Items" section** — auto-recovery (failsafe_monitor should attempt to re-establish the FC link itself before escalating) and a dedicated `string cause` field in `FailsafeStatus.msg` (replacing the `cockpit_bridge` message-substring shortcut) — both tracked as open implementation gaps against existing DIR-7/12 intent, not new decisions. Filename de-versioned as part of the 11 Jul doc restructure — this file moved into the repo `docs/` folder. |
|
||||||
|
| 1.1 | 9 Jul 2026 | **DIR-1 corrected** — the *tether/HILT link*, not the power source, determines operating mode. A tethered vehicle may run on battery or topside supply. **DIR-7 expanded** — AUV `gate` vs ROV `advisory` health semantics; and a MUST that health assessment include flight-controller connectivity (a monitor with no FC input must never report GREEN). **DIR-9 superseded by DIR-9a** — recording is a single ARM-bounded black-box tier, not continuous-from-boot. **DIR-12 added** — vehicle health is distinct from autonomy health. |
|
||||||
|
| 1.0 | Jun 2026 | Initial register. |
|
||||||
|
|
||||||
|
> ⚠ **Known implementation drift (updated 11 Jul 2026):** `recording_manager.py` still implements
|
||||||
|
> the superseded DIR-9 (continuous-from-boot) model, NOT the governing DIR-9a — **still open.**
|
||||||
|
> `failsafe_monitor`'s DIR-7 FC-connectivity MUST is **resolved** (11 Jul 2026, see DIR-7 below);
|
||||||
|
> DIR-12's broader vehicle-health framing remains open beyond the FC-link piece (FD-headroom
|
||||||
|
> check on `ardupilot_manager` not built). The two new DIR-7 addenda (mode-dependent in-mission
|
||||||
|
> recovery, AMBER runtime-response) and both Parked Design Items are **intent recorded, not
|
||||||
|
> implemented** — see their sections.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DIR-1 — Platform is a single dual-capability vehicle (AUV + ROV)
|
||||||
|
|
||||||
|
**Source:** Grant, June 2026 session.
|
||||||
|
|
||||||
|
**Intent:** One physical platform that is *both* an AUV and an ROV. It is not two vehicles or three abstract modes. The **tether / human-in-the-loop (HILT) link determines the operating mode**:
|
||||||
|
|
||||||
|
- **No HILT link (untethered)** → operates as a pure **AUV**: fully autonomous, NO human in the loop. Mission plan + sensor data + parameter-governed rules drive everything, including aborts.
|
||||||
|
- **HILT link present (tethered)** → operates as an **ROV with AUV abilities (HILT)**: the same autonomy stack runs the mission identically, but a human operator is able to intervene.
|
||||||
|
|
||||||
|
> **CORRECTED 9 Jul 2026.** This entry previously stated that the *power source* determines the
|
||||||
|
> operating mode. That is WRONG. **Power source and operating mode are independent.** A tethered
|
||||||
|
> vehicle may run on internal battery or on topside supply; either way it is an ROV because the
|
||||||
|
> HILT link is present. The dominant axis is the link, not the volts.
|
||||||
|
|
||||||
|
**Must:** Treat autonomy as one shared path used in both modes.
|
||||||
|
**Must:** Derive mode from HILT-link presence, never from power source.
|
||||||
|
**Must not:** Implement two separate autonomy/abort flows for AUV vs ROV.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DIR-2 — HILT is an override channel, not a confirmation gate
|
||||||
|
|
||||||
|
**Source:** Grant, June 2026 session (explicit clarification).
|
||||||
|
|
||||||
|
**Intent:** In BOTH modes, the autonomy runs the same rules and **acts automatically by default**. Normal operations are identical in AUV and ROV mode. The ONLY difference in tethered/ROV mode is that the operator **can intervene when/as required** — override a rule's action, or continue manually under conditions the autonomy would otherwise abort on (an experienced pilot may safely continue where rules alone would not).
|
||||||
|
|
||||||
|
**Must:**
|
||||||
|
- Run the autonomous rule-driven path always, in both modes.
|
||||||
|
- Provide an intervention/override channel that is active only when tethered.
|
||||||
|
- Degrade safely: if the operator does nothing, the vehicle behaves exactly as an AUV.
|
||||||
|
|
||||||
|
**Must not:**
|
||||||
|
- Block or pause autonomous action waiting for human confirmation.
|
||||||
|
- Require operator input for the vehicle to act. Intervention is optional, never a precondition.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DIR-3 — Mission is a process (aerial-drone model)
|
||||||
|
|
||||||
|
**Source:** Grant, multiple sessions; consolidated June 2026.
|
||||||
|
|
||||||
|
**Intent:** The mission follows the same lifecycle as an aerial survey drone:
|
||||||
|
|
||||||
|
1. Mission is **created or uploaded to the ROV beforehand** — a process, not a button-time action.
|
||||||
|
2. Pilot **manually launches** and flies the ROV to a **safe place**.
|
||||||
|
3. Pilot **starts the mission** → ROV departs, follows the plan, records data + video/stills as instructed.
|
||||||
|
4. **Normal end:** ROV **autonomously returns to a designated safe place**; pilot takes over for recovery.
|
||||||
|
|
||||||
|
**"Safe place / safe zone":** Operator-designated location, closest to the mission start, where the ROV can safely be recovered or hold. Used both as the normal return point and as the abort return point. (Drone "home" equivalent.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DIR-4 — Mission Start / Stop (mission-layer; paired function)
|
||||||
|
|
||||||
|
**Source:** Grant, June 2026 session. **REVISED to align with AUV/ROV lifecycle research (see DIR-9).**
|
||||||
|
|
||||||
|
**Intent:** Control of the *mission* (the survey), distinct from any vehicle-safety action.
|
||||||
|
|
||||||
|
- **Start** — "go do the inspection." Begins mission-plan execution.
|
||||||
|
- **Stop** — "inspection is done, or I'm calling it off." Ends mission execution.
|
||||||
|
|
||||||
|
**RECORDING — revised per research (DIR-9):** Mission Start/Stop does NOT start/stop the recorder itself. The recorder runs continuously and independently (DIR-9). Mission Start/Stop controls *what is sampled* and *splits the MCAP bag* on a mission-phase boundary, per the active record profile (DIR-10). This avoids the "mission ran but nothing recorded" failure mode — the single most important reason mature systems (ArduPilot, MBARI LRAUV, QGroundControl, MOOS-IvP) do NOT gate the recorder on mission state.
|
||||||
|
|
||||||
|
**Must:**
|
||||||
|
- On Start: begin mission execution; split the bag to mark mission start; apply the mission's record profile (which streams sample).
|
||||||
|
- On Stop: end mission execution; split the bag to mark mission end.
|
||||||
|
- Treat "recorder not active" as a no-go for Start (DIR-9).
|
||||||
|
|
||||||
|
**Must not:**
|
||||||
|
- Command any vehicle movement. Start/Stop is mission-layer only.
|
||||||
|
- Stop or kill the recorder process on mission Stop — the recorder keeps running continuously.
|
||||||
|
|
||||||
|
**UI note:** Start/Stop is a command (mission already created/uploaded per DIR-3). *What to record* is selected at plan time via the record profile (DIR-10), with runtime override available.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DIR-5 — ROV Abort / Return-to-Safe (vehicle-layer)
|
||||||
|
|
||||||
|
**Source:** Grant, June 2026 session. **NOTE: supersedes the earlier mislabelled "manual abort" intent.**
|
||||||
|
|
||||||
|
**Intent:** A *vehicle safety* action, NOT merely a recording stop. Triggered when conditions exceed designated levels (power and others — see failsafe spec thresholds). Behaviour depends on mode per DIR-1/DIR-2:
|
||||||
|
|
||||||
|
- **AUV mode (untethered):** rule fires → vehicle automatically stops recording and returns to the closest safe place. No human.
|
||||||
|
- **ROV/HILT mode (tethered):** same rule fires and would act as in AUV mode, BUT the operator can intervene — either accept (let it return to safe) or override (continue, autonomously per mission or manually).
|
||||||
|
|
||||||
|
**Recording — revised per research (DIR-9):** On return-to-safe, the mission ends so *mission sampling* stops, but the recorder keeps running continuously through the transit and recovery (matches every mature system: keep recording through the return phase, split the bag on the phase boundary). The earlier intent "recording stops on return-to-safe" is refined: *mission-driven high-rate sampling* stops; the *always-on core recording* continues. Rationale: the return transit is itself valuable data, and stopping the recorder mid-event risks losing the record of why the abort happened.
|
||||||
|
|
||||||
|
**Must:**
|
||||||
|
- Use the DP-state (GREEN/AMBER/RED) assessment logic to drive the action.
|
||||||
|
- Stop all recording when returning to safe.
|
||||||
|
- Return the vehicle to the *closest* designated safe place.
|
||||||
|
- In tethered mode, expose alert + override capability to the operator.
|
||||||
|
|
||||||
|
**Must not:** Be labelled simply "abort" in a way that implies an emergency hard-stop. It is a return-to-safe, not an emergency surface.
|
||||||
|
|
||||||
|
**Open / staged:** HILT alert-accept-reject-override flow is multi-stage work (see staging below). Tethered-first development is a potential machine-learning opportunity.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DIR-5a — Abort is mission-bounded; outside a mission the vehicle is manual ROV
|
||||||
|
|
||||||
|
**Source:** Grant, June 2026 session (correction/refinement).
|
||||||
|
|
||||||
|
**Intent:** The autonomy, rules, and abort logic (DIR-2, DIR-5) apply **only while a mission plan is executing**.
|
||||||
|
|
||||||
|
- **No mission running** → the vehicle is **entirely manually controlled by the operator**. It operates as a plain ROV. No autonomy drives it. There is nothing to abort.
|
||||||
|
- **Abort/return-to-safe only exists within a mission.** It is a mission-context action, bounded by mission start and mission end.
|
||||||
|
|
||||||
|
**Lifecycle:**
|
||||||
|
1. Manual control → pilot launches and flies to safe place (manual).
|
||||||
|
2. Mission starts → autonomy active, rules apply, abort available.
|
||||||
|
3. Mission ends (normal return-to-safe, or abort) → back to manual control for recovery.
|
||||||
|
|
||||||
|
**Must:** Only arm autonomy/abort logic while a mission is executing.
|
||||||
|
**Must not:** Allow abort or autonomous action when no mission is running.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DIR-5b — Operator override continues the mission (alerts are early, parameter-driven)
|
||||||
|
|
||||||
|
**Source:** Grant, June 2026 session.
|
||||||
|
|
||||||
|
**Intent:** When the operator intervenes during a mission (tethered/ROV mode), the intervention **continues** the mission — it does NOT terminate or pause it. The mission context stays live throughout.
|
||||||
|
|
||||||
|
**Design goal:** Alerts should fire **early enough** that the operator can act *before* any pause or abort becomes necessary. This is **parameter-driven** — the alert thresholds (the "designated levels" in DIR-5) must give a wide enough margin for intervention ahead of a forced abort.
|
||||||
|
|
||||||
|
**Consequence:** Threshold tuning is critical. The HILT advantage depends entirely on the alert margin being wide enough to intervene before the condition forces return-to-safe. Ties directly to the config UI (staging Stage 4).
|
||||||
|
|
||||||
|
**Must:** Keep the mission running through operator intervention.
|
||||||
|
**Must not:** Drop to manual control or pause the plan as a side-effect of intervention.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DIR-6 — Emergency Surface (SHELVED — flagged for later)
|
||||||
|
|
||||||
|
**Source:** Grant, failsafe design session + June 2026.
|
||||||
|
|
||||||
|
**Intent:** Last-resort hard stop. Surfaces the vehicle regardless of surroundings. Highest risk action; must never be a first response.
|
||||||
|
|
||||||
|
**Status:** SHELVED. Not in current scope. To be reviewed later. May potentially be combined with DIR-5 return-to-safe at review time (Grant: "we could combine point 3 and 4").
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DIR-7 — DP lights linked to mission capability
|
||||||
|
|
||||||
|
**Source:** Grant, failsafe design session. **Expanded 9 Jul 2026.**
|
||||||
|
|
||||||
|
**Intent:** The GREEN/AMBER/RED DP-style states (W1) show the ROV's *ability to conduct the mission*. They are explicitly linked to mission capability. Mission start/stop and ROV abort are separate functions, but the DP state informs the abort decision (e.g. AMBER conditions → operator would stop mission and return to safe).
|
||||||
|
|
||||||
|
**Mode-dependent role (`health_role` flag):**
|
||||||
|
- **ROV (`advisory`)** — display only. The pilot sees the state and decides. A HILT operator is present.
|
||||||
|
- **AUV (`gate`)** — W1 is the **pre-dive GO/NO-GO gate**. RED blocks mission start. There is no operator once submerged, so this is the last human-visible decision point.
|
||||||
|
|
||||||
|
**Semantics (AUV, parameter-driven):** GREEN = within designated levels; AMBER = approaching a designated level; RED = terminate / do not start.
|
||||||
|
|
||||||
|
**MUST — health assessment includes the vehicle, not just the autonomy stack:**
|
||||||
|
The assessment MUST incorporate **flight-controller connectivity and heartbeat age**. A dead or
|
||||||
|
unreachable FC is a RED condition. This is not optional: in AUV mode W1 is a gate, and a gate that
|
||||||
|
cannot see the flight controller is not a gate.
|
||||||
|
|
||||||
|
**MUST — absence of data is not health:**
|
||||||
|
A monitor with no input MUST NOT report GREEN. "No data" and "good data" are different states and
|
||||||
|
must be distinguishable in both the assessment and the display.
|
||||||
|
|
||||||
|
**MUST — the state must carry its cause:**
|
||||||
|
W1's status line carries cause text (e.g. `"Vehicle Disconnected"`, `"Heartbeat Lost"`,
|
||||||
|
`"No Board Running"`, `"Systems nominal"`), not a bare colour. An operator must be able to act on
|
||||||
|
the state without opening another tool.
|
||||||
|
|
||||||
|
> ✅ **Resolved 11 Jul 2026** (was open, safety-critical, 9 Jul 2026): `failsafe_monitor` now
|
||||||
|
> drives W1 RED unconditionally on FC comms loss, with a `fcu_ever_connected` latch closing the
|
||||||
|
> startup-window GREEN gap, and carries cause text on `FailsafeStatus.message` per the MUST
|
||||||
|
> above. Four commits — full narrative in `docs/handover.md` Changelog, 11 Jul entry. This DIR
|
||||||
|
> entry is not the place for commit-level detail; it records that the MUST is now satisfied.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### DIR-7 addendum — In-mission recovery is mode-dependent (11 Jul 2026)
|
||||||
|
|
||||||
|
**Source:** Grant, 11 Jul 2026 session. Extends the advisory-vs-gate framing above from the
|
||||||
|
pre-dive gate to the in-mission runtime case.
|
||||||
|
|
||||||
|
**Intent:** When a RED (or AMBER, see next addendum) condition triggers a return-to-safe /
|
||||||
|
hold-and-recover response **during a mission**, and the underlying condition later clears,
|
||||||
|
what happens next is mode-dependent — recovery is not one behaviour:
|
||||||
|
|
||||||
|
- **AUV (`gate`)** — commits to the recovery action on condition-clear. It does **not**
|
||||||
|
auto-resume the interrupted mission. Recovery, once committed, runs to completion (the
|
||||||
|
vehicle returns to / holds at the designated safe place per DIR-3); resuming the survey
|
||||||
|
itself is a fresh, deliberate mission start (DIR-10), never an automatic side-effect of
|
||||||
|
the condition going away. There is no operator to ask, so the vehicle must not guess that
|
||||||
|
"condition cleared" means "carry on as if nothing happened."
|
||||||
|
- **ROV (`advisory`)** — holds at AMBER/RED and hands the continue-or-recover decision to the
|
||||||
|
operator. The operator decides whether to accept recovery or override and continue, per
|
||||||
|
DIR-5's existing tethered intervention model. Matches DIR-5b: an operator override
|
||||||
|
continues the mission; it does not force a recovery the operator didn't ask for.
|
||||||
|
|
||||||
|
**Must:**
|
||||||
|
- Derive the post-recovery behaviour from `health_role` (`gate`/`advisory`), never assume the
|
||||||
|
same generic "return to NORMAL" response is correct for both modes.
|
||||||
|
- AUV: treat mission resumption as a separate, deliberate action (DIR-10), never implicit in
|
||||||
|
condition-clear.
|
||||||
|
- ROV: surface the continue-or-recover choice explicitly; do not auto-decide on the operator's
|
||||||
|
behalf.
|
||||||
|
|
||||||
|
**Implementation note:** the FSM-level fix landed 11 Jul 2026 (commit `1b0050d`) makes
|
||||||
|
`RETURN_TO_SAFE` and `HOLD_AND_RECOVER` both able to return to `NORMAL` on condition-clear —
|
||||||
|
this is the **mechanism**. It is currently **mode-blind** (`failsafe_monitor` does not yet
|
||||||
|
read the mode profile), so today it behaves the same way in both AUV and ROV. Making that
|
||||||
|
mechanism mode-aware, per this addendum, is open — see Parked Design Items below.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### DIR-7 addendum — AMBER runtime-response, in-mission (drafted 11 Jul 2026)
|
||||||
|
|
||||||
|
**Source:** Grant, 11 Jul 2026 session.
|
||||||
|
|
||||||
|
**Intent:** The pre-dive semantics above ("AMBER = approaching a designated level") describe a
|
||||||
|
gate check before a mission starts. **In-mission, AMBER means something more actionable:
|
||||||
|
the vehicle CAN still operate, but SHOULD NOT continue as currently planned, and action IS
|
||||||
|
required.** It is not merely informational once a mission is underway — it is a call to do
|
||||||
|
something, not just a status light. Response branches by mode:
|
||||||
|
|
||||||
|
- **AUV (`gate`)** — no operator is present to hand the "action required" decision to. The
|
||||||
|
autonomy stack must itself decide the appropriate curtailment (continue the current leg at
|
||||||
|
reduced confidence vs. curtail and return to safe). AMBER does not by itself force an
|
||||||
|
immediate hard return the way RED does. **Exact decision logic is not yet specified** — this
|
||||||
|
addendum records the intent (AMBER demands a response, not just a display), not the
|
||||||
|
algorithm. Left as design work, not to be inferred or invented ahead of a dedicated session.
|
||||||
|
- **ROV (`advisory`)** — surfaces to the operator as "action required" per DIR-5b: alerts fire
|
||||||
|
early enough to act before a forced abort, the mission keeps running through the
|
||||||
|
intervention, and the operator decides continue-or-recover. This addendum does not change
|
||||||
|
ROV behaviour — it is already covered by DIR-5b — it clarifies that AUV needs an equivalent,
|
||||||
|
currently unspecified, autonomous response.
|
||||||
|
|
||||||
|
**Must:** Treat in-mission AMBER as requiring a response, not just a display state, in both
|
||||||
|
modes — the response mechanism differs (autonomous decision vs. operator handoff), but neither
|
||||||
|
mode may treat in-mission AMBER as purely informational.
|
||||||
|
|
||||||
|
**Must not:** Assume AMBER can be left unhandled until it escalates to RED. That defeats the
|
||||||
|
purpose of an early, actionable warning (DIR-5b).
|
||||||
|
|
||||||
|
> **Status:** Intent recorded, not implemented. AUV's autonomous curtailment logic is
|
||||||
|
> unspecified pending further design (see Parked Design Items).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DIR-9 — Recording is continuous and independently guarded — **SUPERSEDED by DIR-9a**
|
||||||
|
|
||||||
|
> ⚠ **SUPERSEDED (9 Jul 2026).** The continuous-from-boot model below is NO LONGER the governing
|
||||||
|
> intent. It was over-specified: it records hours of pre-dive bench idle, and it decouples the
|
||||||
|
> recording from the thing being recorded (the armed run). **DIR-9a governs.** This entry is
|
||||||
|
> retained for history and because `recording_manager.py` still implements it — that is drift,
|
||||||
|
> not intent. The two durable ideas carried forward into DIR-9a are: (1) the recorder is a
|
||||||
|
> **separate node** from `mission_executor`, and (2) **recorder-active is a precondition** for
|
||||||
|
> mission start in AUV mode.
|
||||||
|
|
||||||
|
### Superseded text (DIR-9, June 2026)
|
||||||
|
|
||||||
|
**Source:** AUV/ROV lifecycle research (project doc: Mission_and_Data-Recording_Lifecycle...), June 2026. Aligns owner intent with proven practice.
|
||||||
|
|
||||||
|
**Intent:** Data recording is a **dedicated, always-on process**, separate from the mission executor. It is brought active *before* any mission can start and runs continuously through manual control, mission execution, return-to-safe, and recovery. This is the dominant pattern across ArduPilot/ArduSub (pre-arm fails if logging fails), MBARI LRAUV (continuous background logging), QGroundControl (GCS-level auto-save), and MOOS-IvP (separate pLogger process).
|
||||||
|
|
||||||
|
**Two failure modes this prevents:**
|
||||||
|
1. "Mission ran but nothing recorded" — prevented by making recorder-active a precondition for mission start (hard no-go, like ArduPilot's "PreArm: Logging failed").
|
||||||
|
2. "Continuous recording filled the disk" — prevented by bag splitting (max size/duration), zstd/lz4 compression, and a retention/circular policy (delete-oldest below a free-space threshold).
|
||||||
|
|
||||||
|
**Architecture:**
|
||||||
|
- Recording runs as a **dedicated ROS2 lifecycle node ("recording manager")**, separate from `mission_executor`, managed Nav2-style and brought to `active` before missions can start.
|
||||||
|
- The recorder is a real **rosbag2 MCAP recorder** (or Foxglove SDK MCAP sink), controlled via its services (`~/pause`, `~/resume`, `~/split_bagfile`, snapshot mode).
|
||||||
|
- **CORRECTION TO PRIOR DOCS/MEMORY:** "MCAP recording via foxglove_bridge" is WRONG. `foxglove_bridge` is a live-visualisation WebSocket bridge ONLY — it does not record MCAP. The recorder and the bridge are separate nodes running alongside each other. This error must be corrected wherever it appears (handover, memory).
|
||||||
|
|
||||||
|
**Must:**
|
||||||
|
- Run the recorder continuously, independent of mission state.
|
||||||
|
- Block mission start if the recorder is not active; surface recorder health in the operator UI and treat a recorder fault like a failsafe (warn).
|
||||||
|
- Split bags on mission-phase boundaries; annotate bags with mission metadata (YAML, schema version, vehicle/firmware IDs, active profile).
|
||||||
|
|
||||||
|
**Must not:**
|
||||||
|
- Bind recorder liveness to mission start/stop.
|
||||||
|
- Use foxglove_bridge as the recording mechanism.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DIR-9a — Recording is a single ARM-bounded black-box tier (GOVERNING)
|
||||||
|
|
||||||
|
**Source:** Grant, July 2026. Supersedes DIR-9.
|
||||||
|
|
||||||
|
**Intent:** There is **one** recording tier, and it is bounded by the **armed run** — not by boot,
|
||||||
|
not by mission state. Its purpose is **black-box / debug / ML / fault-finding**. It is the flight
|
||||||
|
recorder: it covers the period in which the vehicle can move.
|
||||||
|
|
||||||
|
**Behaviour, keyed to the `recording_control` flag (not the mode name):**
|
||||||
|
- **AUV (`auto`)** — recording **starts on ARM, stops on DISARM**. No operator is present; the
|
||||||
|
vehicle records its own armed run.
|
||||||
|
- **ROV (`manual`)** — the **operator starts and stops** recording. On the ARM edge, if the
|
||||||
|
recorder is not active, the Recording Control widget raises a reminder ("armed, not recording").
|
||||||
|
Keyed to `record_reminder_on_arm`.
|
||||||
|
|
||||||
|
**No-go coupling, keyed to the `nogo_gate` flag:**
|
||||||
|
- **AUV (`enforced`)** — a recording failure is a **hard no-go**. Mission stops; vehicle is recovered.
|
||||||
|
- **ROV (`relaxed`)** — a recording failure after ARM alerts the pilot; the operator resolves it
|
||||||
|
in-water or recovers. Not a hard stop, because a human is in the loop.
|
||||||
|
|
||||||
|
**Must:**
|
||||||
|
- Bound recording to the armed run, per armed run.
|
||||||
|
- Derive behaviour from the `recording_control` / `nogo_gate` flags, never from a mode string.
|
||||||
|
- Keep the recorder a **separate node** from `mission_executor` (carried forward from DIR-9).
|
||||||
|
- Split bags, compress, and enforce a retention policy — the disk-fill failure mode from DIR-9
|
||||||
|
remains real and its mitigations still apply.
|
||||||
|
|
||||||
|
**Must not:**
|
||||||
|
- Record continuously from node startup, independent of ARM state.
|
||||||
|
- Treat recorded data as a **client deliverable**. Certified deliverable capture is OUT of scope
|
||||||
|
pending Class-approved devices. Recorded data is dev/debug/ML only.
|
||||||
|
- Use `foxglove_bridge` as the recording mechanism (carried forward from DIR-9 — it is
|
||||||
|
visualisation only).
|
||||||
|
|
||||||
|
> ⚠ **Implementation drift (9 Jul 2026):** `recording_manager.py` still implements DIR-9
|
||||||
|
> (continuous-from-boot). `foxglove_mcap.launch.py` comments also still cite DIR-9. Additionally
|
||||||
|
> `argonaut-stack.sh` passes `record:=${RECORD}` to a launch file that declares no such argument,
|
||||||
|
> so `RECORD=false` is silently ignored and bags record regardless. A dedicated DIR-9a rework is
|
||||||
|
> required. Do NOT wire `RECORD` into the current DIR-9 model.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DIR-10 — Record intent lives in the mission plan; runtime override allowed
|
||||||
|
|
||||||
|
**Source:** AUV/ROV lifecycle research + Grant's "checkboxes for what to record" requirement, June 2026.
|
||||||
|
|
||||||
|
**Intent:** *What* gets recorded is decided primarily at **plan time**, embedded in the mission, with **runtime operator override** always available. This mirrors the universal pattern (ArduPilot in-mission camera DO-items, LRAUV `Enabled<Instrument>` arguments, QGC plan-attached camera actions) where default capture is planned but the operator can always start/stop/split/add/drop a stream live.
|
||||||
|
|
||||||
|
**Representation:**
|
||||||
|
- A **`recording:` block in the mission YAML** carrying named **record profiles** (e.g. `nav_only`, `inspection_full`) and **per-stream toggles** (forward camera, sonar, CTD, navigation, etc.). This is Grant's checkbox concept — surfaced in the mission planner UI, stored in the mission file.
|
||||||
|
- At execute time, the profile translates into (a) the rosbag2 recorder's topic set and (b) any MAVLink camera `DO_*` items — the same YAML→`MISSION_ITEM` translation already done by `rov_mission`.
|
||||||
|
- A **low-rate always-on core** (vehicle state, mode, mission status, nav) records continuously regardless of profile; the profile governs the **high-rate sensor streams**.
|
||||||
|
|
||||||
|
**Must:**
|
||||||
|
- Let the operator choose record streams at plan time (checkboxes / profiles in the planner UI).
|
||||||
|
- Allow runtime override from the console (start/stop/split/snapshot, add/drop a stream).
|
||||||
|
- Keep the always-on core recording regardless of selected profile.
|
||||||
|
|
||||||
|
**Must not:** Require the operator to configure recording separately from the mission for the common case — the plan should imply its data products.
|
||||||
|
|
||||||
|
**UI note:** This is where the checkbox UI Grant described lives — in the mission planner (plan-time), with a runtime override surface on the operator console. The 3D Mission Planner (deferred to Phase 6+) is the eventual home; an interim plan-time mechanism may be needed sooner.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DIR-12 — Vehicle health and autonomy health are distinct; the operator must see both
|
||||||
|
|
||||||
|
**Source:** Grant + measured failure, 9 July 2026.
|
||||||
|
|
||||||
|
**Origin:** With the flight controller unreachable ("No board running" in BlueOS), **W1 displayed
|
||||||
|
GREEN / "Systems nominal"**. Every widget indicator was reporting on the RPi5 autonomy stack, not
|
||||||
|
on the vehicle:
|
||||||
|
- **W1** reads the failsafe assessment, which does not gate on FC connectivity.
|
||||||
|
- **W8** reads the active mode profile, sourced from a YAML file — it would show `AUV` with the
|
||||||
|
Pixhawk sitting in a drawer.
|
||||||
|
- **Foxglove "Connected: true"** means connected to `foxglove_bridge` on the RPi5, not to the vehicle.
|
||||||
|
|
||||||
|
**Intent:** "Health" is not one thing. There are two independent subjects, and conflating them
|
||||||
|
produces a display that is confidently wrong at the worst moment:
|
||||||
|
|
||||||
|
1. **Autonomy health** — the RPi5 stack: nodes alive, EKF converged, recorder active, bridge up.
|
||||||
|
2. **Vehicle health** — the FC and the platform: FC reachable, heartbeat fresh, arm state known,
|
||||||
|
sensors reporting, power sane.
|
||||||
|
|
||||||
|
**Must:**
|
||||||
|
- Assess vehicle health as a first-class input to the failsafe state (see DIR-7).
|
||||||
|
- Make the *subject* of any health indication unambiguous to the operator — a green light must
|
||||||
|
answer "green about what?".
|
||||||
|
- Treat loss of FC communications as a vehicle-health RED regardless of how healthy the autonomy
|
||||||
|
stack is. The autonomy stack being perfectly well is irrelevant if it is talking to nothing.
|
||||||
|
|
||||||
|
**Must not:**
|
||||||
|
- Allow an indicator that reads only autonomy state to be presented as overall system health.
|
||||||
|
- Allow an AUV pre-dive gate (DIR-7, `health_role: gate`) to pass without a verified FC link.
|
||||||
|
- Rely on the operator having a second tool (BlueOS, QGC) open to know whether the vehicle is
|
||||||
|
alive. In AUV mode nobody is watching; pre-dive, the gate must be self-sufficient.
|
||||||
|
|
||||||
|
**Related, same class of failure:** the BlueOS `ardupilot_manager` `/dev/i2c-1` FD leak
|
||||||
|
(`docs/handover.md` §15a) can silently exhaust the FC-management service after ~11 h of
|
||||||
|
cumulative board-config page exposure. The FD count does not reset at launch. A pre-dive gate
|
||||||
|
that checks FD headroom on `ardupilot_manager` belongs here, alongside the FC-link check — both
|
||||||
|
are "is the vehicle actually going to keep talking to me" questions.
|
||||||
|
|
||||||
|
> **Status:** OPEN. Not implemented. Safety-critical for AUV mode.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Parked Design Items (intent-adjacent, not yet actioned)
|
||||||
|
|
||||||
|
Tracked here so they aren't lost between sessions. Not full DIR entries — each is an
|
||||||
|
implementation gap against an existing DIR, recorded so the next session that touches the
|
||||||
|
relevant code knows the decision is already made in principle, just not built.
|
||||||
|
|
||||||
|
### Auto-recovery (relates to DIR-7, DIR-12)
|
||||||
|
`failsafe_monitor` should attempt to **re-establish the FC link itself** before escalating —
|
||||||
|
holding or forcing RED only if it cannot restore comms. Real-world dives have no operator to
|
||||||
|
restore the connection manually; the current implementation (11 Jul 2026 fix) correctly
|
||||||
|
detects and reports loss but does not attempt recovery on its own. This is distinct from the
|
||||||
|
FSM's condition-clear recovery (which reacts once the condition is already gone) — auto-recovery
|
||||||
|
is about the monitor actively trying to make the condition go away.
|
||||||
|
|
||||||
|
### Dedicated `string cause` field (relates to DIR-7's "state must carry its cause" MUST)
|
||||||
|
`FailsafeStatus.msg` currently carries cause information as a substring of `message`
|
||||||
|
(`cockpit_bridge` splits on the first `|`), not a first-class field. Add a dedicated
|
||||||
|
`string cause` field to `FailsafeStatus.msg` and have `cockpit_bridge` read it directly,
|
||||||
|
retiring the substring-parsing shortcut. Same intent as already specified in DIR-7; this is
|
||||||
|
purely a mechanism cleanup, not a new decision.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Proposed build staging (REVISED to align with research)
|
||||||
|
|
||||||
|
Derived from the above and the lifecycle research; tethered-first since initial development is tethered. Reordered because the recording manager is foundational — it must be active before any mission can start (DIR-9).
|
||||||
|
|
||||||
|
- **Stage 1 — Recording Manager (foundational):** dedicated ROS2 lifecycle node running a rosbag2 MCAP recorder, **ARM-bounded per DIR-9a** (not continuous — the original "continuous" wording here reflected the superseded DIR-9), separate from mission_executor. Bag splitting, compression, retention policy, health surfaced to UI. Recorder-active becomes a precondition for mission start. (Replaces the prior "MCAP via foxglove_bridge" assumption.) CREATE-THEN-BACKUP applies.
|
||||||
|
- **Stage 2 — Mission Start/Stop:** mission-layer command via the existing `MissionCommand` service (START/ABORT/PAUSE/RESUME/LOAD already implemented). Splits the bag on mission boundaries; applies record profile. Does not move vehicle, does not stop the recorder.
|
||||||
|
- **Stage 3 — Record profiles + intent in mission YAML:** the `recording:` block (DIR-10), profiles and per-stream toggles, translated at execute time. Includes the checkbox UI surface.
|
||||||
|
- **Stage 4 — Condition detection + alerting:** autonomy detects designated levels exceeded; raises alert (AUV acts; ROV surfaces alert to operator).
|
||||||
|
- **Stage 5 — Return-to-Safe + HILT override:** vehicle return-to-safe (auto in AUV, intervenable in ROV); recorder continues through transit, bag split on phase boundary.
|
||||||
|
- **Stage 6 — Config UI:** threshold parameters ("designated levels") governing Stage 4/5; also the W5 return-budget constants (DIR ties to battery floor + reserve).
|
||||||
|
- **Stage 7 (SHELVED) — Emergency Surface.**
|
||||||
|
|
||||||
|
**Note on existing code:** `mission_executor` already implements the `MissionCommand` service (START/LOAD/PAUSE/RESUME/ABORT) and failsafe integration (emergency-surface and return-to-safe both abort the mission). It does NOT control recording — correct per DIR-9. Stage 2 wires Mission Start/Stop to this existing service plus bag-split calls to the Stage 1 recorder.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implications for existing code/widgets (factual, for validation)
|
||||||
|
|
||||||
|
- The currently wired `/rov/mission/abort` (Bool → failsafe_monitor → flag_manual_abort → state machine) matches the **AUV automatic** path (DIR-5 AUV mode). It does NOT implement the tethered HILT intervention model (DIR-2/DIR-5 ROV mode), which is the near-term need.
|
||||||
|
- `mission_executor` implements the `MissionCommand` **service** (not a topic): commands START/LOAD/PAUSE/RESUME/ABORT, request `{command, mission_id, parameters[]}`, response `{success, message}`. Mission Start/Stop (DIR-4, Stage 2) drives this.
|
||||||
|
- `mission_executor` does **not** control recording — correct per DIR-9a. Recording is a separate Stage-1 manager.
|
||||||
|
- **W1 reports GREEN with the flight controller unreachable (DIR-12, DIR-7).** `failsafe_monitor` does not consume `/mavros/state` connectivity. OPEN, safety-critical.
|
||||||
|
- **`recording_manager.py` implements superseded DIR-9, not governing DIR-9a.** OPEN.
|
||||||
|
- **"MCAP via foxglove_bridge" is WRONG (DIR-9).** foxglove_bridge is viz-only. Correct this in the handover (Tools section, Section 8/11) and in memory. Recording = rosbag2 MCAP recorder, separate node.
|
||||||
|
- **W3 button label "ABORT MISSION" is misleading** per DIR-5 — implies emergency hard-stop but maps to assessed return-to-safe. Rename pending — exact wording TBC with Grant.
|
||||||
|
- **W2/W4 mission state** relates to DIR-4 (mission start/stop), mission-layer, distinct from DIR-5 vehicle action.
|
||||||
451
docs/failsafe_design.md
Normal file
451
docs/failsafe_design.md
Normal file
@ -0,0 +1,451 @@
|
|||||||
|
# ROV Failsafe & Safety Behaviour Design
|
||||||
|
**Project:** Argonaut 3 — Autonomous Inspection System
|
||||||
|
**Version:** 2.0
|
||||||
|
**Date:** May 2026
|
||||||
|
**Status:** Design — pre-implementation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
| Version | Date | Changes |
|
||||||
|
|---|---|---|
|
||||||
|
| 1.0 | May 2026 | Initial design |
|
||||||
|
| 2.0 | May 2026 | Revised per operational review: continuous self-assessment, breadcrumb path reversal, tethered vs untethered exit modes, 2s comms timeout, battery config via web UI, depth as soft limit, Gemini sonar roadmap, thruster health via power consumption |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Design Philosophy
|
||||||
|
|
||||||
|
**Asset preservation over mission completion.** A recovered vehicle can repeat a mission. A lost vehicle cannot.
|
||||||
|
|
||||||
|
**Conditional response over fixed response.** The system assesses its own capability before choosing a response. Hard-coded reactions are inappropriate for a vehicle operating inside structures where "surface immediately" may cause the exact loss it is trying to prevent.
|
||||||
|
|
||||||
|
**Safety is continuous, not reactive.** The system monitors its health at all times, not only when a failure occurs. By the time a failsafe triggers, the vehicle already has a current picture of its own state.
|
||||||
|
|
||||||
|
**Tethered and untethered operation are fundamentally different.** Exit behaviour, path planning, and safe zone definitions differ between modes and must be explicitly configured at mission start.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Continuous Self-Assessment
|
||||||
|
|
||||||
|
Self-assessment runs continuously throughout all operations — not only when a failure occurs. This eliminates the lag between trigger and response that would otherwise occur if assessment only started at fault detection.
|
||||||
|
|
||||||
|
### Assessment rates
|
||||||
|
|
||||||
|
| Mode | Rate | Rationale |
|
||||||
|
|---|---|---|
|
||||||
|
| Normal operation | 5 Hz | Low overhead, sufficient for trend detection |
|
||||||
|
| Any parameter degraded | 20 Hz | Faster response as situation develops |
|
||||||
|
| Failsafe active | 50 Hz | Maximum responsiveness during critical phase |
|
||||||
|
|
||||||
|
### Parameters assessed
|
||||||
|
|
||||||
|
| Parameter | Source | Healthy threshold |
|
||||||
|
|---|---|---|
|
||||||
|
| Attitude stability | Xsens IMU | Roll/pitch error < 5°, rate < 10°/s |
|
||||||
|
| Depth hold | Pressure sensor via MAVROS | Depth error < 0.5m from setpoint |
|
||||||
|
| Structural clearance | Obstacle sensors (see Section 6) | > 1.5m clearance in primary travel direction |
|
||||||
|
| Path to safety | Breadcrumb buffer + dead reckoning | Valid path calculable to entry point |
|
||||||
|
| Battery reserve | Dynamic return budget (see Section 5) | > 15% above minimum return estimate |
|
||||||
|
| FCU connection | MAVROS heartbeat | Heartbeat received within 2s |
|
||||||
|
| Thruster health | ESC power consumption | No thruster drawing > 150% of peer average |
|
||||||
|
|
||||||
|
### Assessment states
|
||||||
|
|
||||||
|
Three states, consistent with DP (Dynamic Positioning) reference system conventions:
|
||||||
|
|
||||||
|
| State | Definition | Minimum conditions |
|
||||||
|
|---|---|---|
|
||||||
|
| **GREEN** | Vehicle capable, all systems nominal | All parameters healthy, 5+ parameters assessed |
|
||||||
|
| **AMBER** | Vehicle functional but degraded | Core navigation intact, ≤ 1 parameter marginal |
|
||||||
|
| **RED** | Vehicle incapable of safe autonomous action | Navigation sensor failure, or ≥ 2 parameters marginal, or any safety-critical sensor lost |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Path to Safety — Breadcrumb System
|
||||||
|
|
||||||
|
### Concept
|
||||||
|
|
||||||
|
"Return to safe" does not mean surface from current position. Inside a jacket structure, surfacing from current position could drive the vehicle into a structural member or wrap the tether around a node.
|
||||||
|
|
||||||
|
Safe return means retracing the path taken since the designated entry point, in reverse.
|
||||||
|
|
||||||
|
### Entry point designation
|
||||||
|
|
||||||
|
At mission start, the operator designates:
|
||||||
|
- **Entry point** — the position at which the vehicle entered the structure or began the mission. Stored as a 3D position in the mission YAML.
|
||||||
|
- **Safe zone** — a volume (not a point) beyond the structure perimeter where the vehicle can safely ascend. Defined as a radius around the entry point (default: 5m radius, 0m to surface).
|
||||||
|
- **Operation mode** — `tethered` or `untethered`. This changes the exit strategy fundamentally.
|
||||||
|
|
||||||
|
### Breadcrumb buffer
|
||||||
|
|
||||||
|
From the moment of entry point designation, the system logs position estimates every 2 seconds to a circular buffer. Buffer size: 1,800 entries = 1 hour of operation. Older entries are discarded as new ones are added.
|
||||||
|
|
||||||
|
Each breadcrumb entry stores:
|
||||||
|
```
|
||||||
|
timestamp, x, y, z, heading, confidence_score
|
||||||
|
```
|
||||||
|
|
||||||
|
`confidence_score` reflects the quality of the position estimate at that moment — entries with low confidence are flagged and the system may skip them during path reversal in favour of the next reliable entry.
|
||||||
|
|
||||||
|
### Tethered exit (path reversal)
|
||||||
|
|
||||||
|
On a failsafe exit trigger in tethered mode:
|
||||||
|
|
||||||
|
1. Halt all forward motion immediately
|
||||||
|
2. Load the breadcrumb buffer in reverse order
|
||||||
|
3. Navigate waypoint-by-waypoint back along the recorded path
|
||||||
|
4. On reaching the entry point, move laterally to the safe zone
|
||||||
|
5. Ascend vertically to surface
|
||||||
|
|
||||||
|
**Critical constraint:** The tether exit path must follow the vehicle entry path exactly. Any deviation risks wrapping the tether around a structural member. If position uncertainty at any breadcrumb point exceeds a threshold (configurable, default 0.5m), the vehicle halts and holds, awaiting operator instruction or comms restoration.
|
||||||
|
|
||||||
|
### Untethered exit (direct to safe zone)
|
||||||
|
|
||||||
|
On a failsafe exit trigger in untethered mode:
|
||||||
|
|
||||||
|
1. Halt all forward motion
|
||||||
|
2. Use current sensor data to assess the most direct obstacle-free route toward the safe zone
|
||||||
|
3. Navigate toward the safe zone boundary
|
||||||
|
4. On entering the safe zone, ascend vertically to surface
|
||||||
|
|
||||||
|
**Advantage over tethered:** The untethered vehicle is not constrained to its entry path. It can take the most efficient route to the safe zone perimeter using live obstacle sensor data.
|
||||||
|
|
||||||
|
### Safe zone verification
|
||||||
|
|
||||||
|
Before ascending, the vehicle performs a brief check (default 5 seconds):
|
||||||
|
- Obstacle sensors confirm clearance above
|
||||||
|
- Attitude is stable
|
||||||
|
- No new failsafe conditions have triggered
|
||||||
|
|
||||||
|
If verification fails, the vehicle holds at the safe zone boundary and re-attempts after 10 seconds. After 3 failed attempts, emergency surface is triggered.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Failsafe State Machine
|
||||||
|
|
||||||
|
### States
|
||||||
|
|
||||||
|
```
|
||||||
|
NORMAL ──[trigger]──► ASSESSING ──[GREEN/AMBER]──► HOLD_AND_RECOVER
|
||||||
|
│ │
|
||||||
|
│ [recovery fails]
|
||||||
|
│ │
|
||||||
|
└──[RED]──────────► RETURN_TO_SAFE
|
||||||
|
│
|
||||||
|
[path blocked / RED worsens]
|
||||||
|
│
|
||||||
|
EMERGENCY_SURFACE
|
||||||
|
```
|
||||||
|
|
||||||
|
| State | Description |
|
||||||
|
|---|---|
|
||||||
|
| NORMAL | Mission executing, all systems nominal |
|
||||||
|
| ASSESSING | Failsafe triggered, assessment resolves in < 0.2s (data already current) |
|
||||||
|
| HOLD_AND_RECOVER | Station-keeping, monitoring for recovery condition |
|
||||||
|
| RETURN_TO_SAFE | Executing breadcrumb reversal or direct route to safe zone |
|
||||||
|
| EMERGENCY_SURFACE | Maximum ascent, no navigation — last resort only |
|
||||||
|
| MISSION_COMPLETE | Normal end state |
|
||||||
|
| ABORTED | Mission ended safely, vehicle at surface |
|
||||||
|
|
||||||
|
**Note on EMERGENCY_SURFACE:** This state is explicitly the highest-risk action the vehicle can take. It surfaces without regard for surroundings. It exists because there are conditions where all other options have been exhausted and certain loss is the only alternative. It must never be the first response to any condition.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Trigger Conditions and Responses
|
||||||
|
|
||||||
|
### 5.1 Comms loss
|
||||||
|
|
||||||
|
**Definition:** No FCU heartbeat received for > timeout period.
|
||||||
|
|
||||||
|
**Default timeout:** 2 seconds. Configurable per deployment.
|
||||||
|
|
||||||
|
**Rationale for 2s default:** In high current conditions, 5 seconds of unchecked drift may place the vehicle in an unrecoverable position relative to its tether or the structure. 2 seconds is a practical lower bound given normal comms latency.
|
||||||
|
|
||||||
|
**Response — conditional on assessment state:**
|
||||||
|
|
||||||
|
| State | Action | Hold limit |
|
||||||
|
|---|---|---|
|
||||||
|
| GREEN | Hold position, attempt reconnection | 60s then RETURN_TO_SAFE |
|
||||||
|
| AMBER | Hold position, attempt reconnection | 30s then RETURN_TO_SAFE |
|
||||||
|
| RED | RETURN_TO_SAFE immediately | — |
|
||||||
|
|
||||||
|
**Recovery condition:** Comms restored AND assessment returns GREEN → resume mission. If interrupted mid-panel, restart that panel from the beginning. Incomplete panel data is discarded — partial coverage is worse than no coverage for inspection reporting.
|
||||||
|
|
||||||
|
### 5.2 Low battery
|
||||||
|
|
||||||
|
**Definition:** Dynamic threshold based on battery profile (see Section 5 — Battery Management).
|
||||||
|
|
||||||
|
**Response — tiered:**
|
||||||
|
|
||||||
|
| Threshold | Action |
|
||||||
|
|---|---|
|
||||||
|
| Warning (25%) | Alert published to web UI. Continue current waypoint. No new panels started. |
|
||||||
|
| Return threshold (20%) | Complete current panel capture then RETURN_TO_SAFE |
|
||||||
|
| Critical (12%) | Abandon current task immediately. RETURN_TO_SAFE. |
|
||||||
|
| Emergency (8%) | EMERGENCY_SURFACE. No other action. |
|
||||||
|
|
||||||
|
**Note:** Critical and Emergency thresholds trigger RETURN_TO_SAFE and EMERGENCY_SURFACE respectively — not an uncontrolled shutdown. The vehicle uses remaining power to reach a safe position before ascending.
|
||||||
|
|
||||||
|
### 5.3 Depth exceeded
|
||||||
|
|
||||||
|
**Definition:** Measured depth exceeds configured limits.
|
||||||
|
|
||||||
|
**This is a soft limit, not an emergency.** Depth exceedance during a structured mission likely indicates a navigation error, not a catastrophic failure.
|
||||||
|
|
||||||
|
| Depth | Action |
|
||||||
|
|---|---|
|
||||||
|
| > Design depth (200m) | Log event, alert operator, continue — may be intentional |
|
||||||
|
| > Warning depth (250m) | Halt downward movement. Hold current depth. Alert operator. Await instruction. |
|
||||||
|
| > Safety limit (300m) | RETURN_TO_SAFE. Hardware safety limit — do not exceed. |
|
||||||
|
|
||||||
|
**Note:** Standard BlueROV2 Heavy depth rating is 100m. Operating beyond this requires a pressure-rated enclosure upgrade. The 200m/300m values above are design targets for a hardware-upgraded configuration and must be validated against the actual build specification before field use.
|
||||||
|
|
||||||
|
### 5.4 Obstacle detected
|
||||||
|
|
||||||
|
**Definition:** Object detected within configured standoff distance in the direction of travel.
|
||||||
|
|
||||||
|
**Default standoff:** 1.0m. Configurable per deployment (tighter in confined structures, looser in open water).
|
||||||
|
|
||||||
|
**Response — conditional:**
|
||||||
|
|
||||||
|
| Condition | Action |
|
||||||
|
|---|---|
|
||||||
|
| GREEN, stationary obstacle | Halt. Hold position. Re-plan path around obstacle. Resume. |
|
||||||
|
| GREEN, moving obstacle | Hold position. Wait up to 30s for clearance. Re-plan if not cleared. |
|
||||||
|
| AMBER | Halt. Hold. Alert operator. Await instruction. |
|
||||||
|
| RED | RETURN_TO_SAFE |
|
||||||
|
|
||||||
|
**Tethered note:** Obstacle detection in tethered mode must also consider tether routing. The re-planned path must not create tether entanglement risk even if it is geometrically clear of obstacles.
|
||||||
|
|
||||||
|
### 5.5 Manual abort
|
||||||
|
|
||||||
|
**Definition:** Operator sends abort command via web UI.
|
||||||
|
|
||||||
|
**Response — conditional:**
|
||||||
|
|
||||||
|
| State | Action | Wait limit |
|
||||||
|
|---|---|---|
|
||||||
|
| GREEN | Hold position. Await operator instruction (resume or surface). | 120s then RETURN_TO_SAFE |
|
||||||
|
| AMBER | RETURN_TO_SAFE | — |
|
||||||
|
| RED | RETURN_TO_SAFE | — |
|
||||||
|
|
||||||
|
**Rationale:** An operator abort near a structure may be a precaution, not an emergency. Holding position gives the operator the opportunity to assess and instruct next steps. If no instruction arrives within 120 seconds, the vehicle proceeds to RETURN_TO_SAFE autonomously.
|
||||||
|
|
||||||
|
### 5.6 Thruster anomaly
|
||||||
|
|
||||||
|
**Definition:** Any thruster drawing > 150% of the average power consumption of its peers over a 5-second window, or all thrusters drawing > 130% of baseline simultaneously.
|
||||||
|
|
||||||
|
**Interpretation:**
|
||||||
|
- Single thruster anomaly: possible mechanical obstruction, debris ingestion, or thruster failure
|
||||||
|
- All thrusters elevated: high current/drag environment — mission difficulty has increased
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
|
||||||
|
| Condition | Action |
|
||||||
|
|---|---|
|
||||||
|
| Single thruster > 150% peer average | Alert. Downgrade to AMBER. Increase assessment rate. |
|
||||||
|
| Single thruster drops to zero output | Alert. Re-assess station-keeping ability. May trigger RED. |
|
||||||
|
| All thrusters > 130% baseline for > 30s | Alert. Reassess mission feasibility. Notify operator. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Battery Management
|
||||||
|
|
||||||
|
Battery thresholds are not hardcoded. All parameters are configurable via the web UI and stored in `battery.yaml`. When a new battery type is fitted, only the configuration changes — no code changes required.
|
||||||
|
|
||||||
|
### Battery configuration (web UI + battery.yaml)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
battery:
|
||||||
|
label: "BlueRobotics_4S_15.6Ah"
|
||||||
|
capacity_wh: 388.8
|
||||||
|
warning_percent: 25
|
||||||
|
return_percent: 20
|
||||||
|
critical_percent: 12
|
||||||
|
emergency_percent: 8
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dynamic return budget
|
||||||
|
|
||||||
|
In addition to fixed percentage thresholds, the system maintains a dynamic return budget — an estimate of the energy required to return from current position to surface. If the dynamic budget exceeds the return threshold percentage, the dynamic budget takes priority.
|
||||||
|
|
||||||
|
**Calculation inputs:**
|
||||||
|
- Current depth (deeper = more ascent energy required)
|
||||||
|
- Distance from safe zone
|
||||||
|
- Observed thruster load over the last 60 seconds (indicator of current and drag)
|
||||||
|
- Battery discharge curve from config
|
||||||
|
|
||||||
|
**Practical effect:** A vehicle at 30m depth in calm water may safely continue at 20% battery. The same vehicle at 80m in high current may need to return at 30%. The system calculates this automatically.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Obstacle Detection — Sensor Strategy
|
||||||
|
|
||||||
|
### 7.1 Design principle
|
||||||
|
|
||||||
|
Obstacle detection uses a layered sensor approach. Each layer adds capability. The system is designed from the start to accommodate additional sensors as budget allows, without architectural changes.
|
||||||
|
|
||||||
|
### 7.2 Sensor layers
|
||||||
|
|
||||||
|
#### Layer 1 — IP cameras (current hardware, no additional cost)
|
||||||
|
|
||||||
|
Cameras contribute to obstacle awareness via:
|
||||||
|
- **Optical flow** — detects relative motion toward a surface
|
||||||
|
- **Edge detection** — high-contrast edges across multiple frames indicate proximity to a structure
|
||||||
|
|
||||||
|
**Honest limitations:** Unreliable in turbid water, darkness, or port visibility conditions. Effective as a first warning layer only. Not sufficient as a standalone safety system.
|
||||||
|
|
||||||
|
**Verdict:** Use now. Treat as supplementary.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Layer 2 — Blue Robotics Ping2 Sonar (~$280 USD)
|
||||||
|
|
||||||
|
Single-beam echosounder. Serial interface (UART/USB via BLUART adapter, or networked via BlueOS bridge).
|
||||||
|
|
||||||
|
| Attribute | Value |
|
||||||
|
|---|---|
|
||||||
|
| Range | 100m |
|
||||||
|
| Beam width | 25° (single direction) |
|
||||||
|
| Depth rating | 300m |
|
||||||
|
| Update rate | ~10 Hz |
|
||||||
|
| Interface | Serial (UART) — network via BlueOS |
|
||||||
|
| BlueROV2 integration | Native — mounting bracket included |
|
||||||
|
| ROS2 integration | ping-python library, wrappable as ROS2 node |
|
||||||
|
|
||||||
|
**Deployment:**
|
||||||
|
- Unit 1 (forward): primary obstacle detection in travel direction
|
||||||
|
- Unit 2 (downward): altimetry — distance to hull/seafloor for standoff control
|
||||||
|
|
||||||
|
**Interface note:** The Ping2 is a serial device. It can be accessed over the network via BlueOS as a bridge, but the physical connection is UART. This limits expandability. An Ethernet switch inside the ROV is recommended infrastructure even if the Ping2 is the first sonar fitted — it prepares the system for the Gemini upgrade.
|
||||||
|
|
||||||
|
**Verdict:** First acoustic purchase. Phase 3.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Layer 3 — Blue Robotics Ping360 Scanning Sonar (~$1,200 USD)
|
||||||
|
|
||||||
|
Mechanical scanning imaging sonar. 360° coverage.
|
||||||
|
|
||||||
|
| Attribute | Value |
|
||||||
|
|---|---|
|
||||||
|
| Range | 50m |
|
||||||
|
| Coverage | 360° mechanical scan |
|
||||||
|
| Depth rating | 100m standard |
|
||||||
|
| Update rate | Variable (full scan ~1-2s depending on range) |
|
||||||
|
| Interface | USB or Ethernet |
|
||||||
|
| BlueROV2 integration | Native |
|
||||||
|
|
||||||
|
**Use:** Full situational awareness. Sees threats from all directions simultaneously. Particularly valuable inside jacket structures where obstacles exist in all planes.
|
||||||
|
|
||||||
|
**Verdict:** Phase 4-5. Required before unsupervised field operations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Layer 4 — Tritech Gemini 720im Multibeam Sonar (~$8,000–$15,000 USD)
|
||||||
|
|
||||||
|
Real-time multibeam imaging sonar. The target long-term sensor for this system.
|
||||||
|
|
||||||
|
| Attribute | Value |
|
||||||
|
|---|---|
|
||||||
|
| Frequency | 720 kHz |
|
||||||
|
| Field of view | 90° horizontal |
|
||||||
|
| Range | 50m |
|
||||||
|
| Update rate | Up to 20 Hz |
|
||||||
|
| Depth rating | 300m |
|
||||||
|
| Interface | Ethernet or Tritech Serial (TSMP) |
|
||||||
|
| Size | Compact — suited to small ROVs |
|
||||||
|
|
||||||
|
**Use:** High-resolution, high-update-rate obstacle and structure detection. Real-time sonar imagery for both safety and inspection. The Ethernet interface makes it architecturally clean to integrate alongside cameras and other network devices.
|
||||||
|
|
||||||
|
**Verdict:** Phase 6+, budget permitting. Design the internal Ethernet switch infrastructure now so the Gemini slots in without hull penetration changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Future consideration — Forward-Looking Sonar (FLS)
|
||||||
|
|
||||||
|
Purpose-built forward obstacle detection at higher resolution than the Ping360 in the forward arc. Products such as the Tritech Micron and Impact Subsea ISS360 exist in this space. Price range $2,000–$8,000 USD. Evaluate after Gemini decision.
|
||||||
|
|
||||||
|
#### Future consideration — Laser rangefinding
|
||||||
|
|
||||||
|
Effective only in clear water (visibility > 5m). Port environments with turbidity make laser-based ranging unreliable. Not recommended for primary obstacle detection in the target operational environment. Note for reference only.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7.3 Sensor roadmap
|
||||||
|
|
||||||
|
| Phase | Sensors active | Capability |
|
||||||
|
|---|---|---|
|
||||||
|
| 1–2 | IP cameras | Basic visual awareness. No safety guarantee. |
|
||||||
|
| 3 | + Ping2 (×1 forward) | Basic acoustic obstacle detection. |
|
||||||
|
| 4 | + Ping2 (×1 downward) | Full altimetry. Standoff control from hull. |
|
||||||
|
| 5 | + Ping360 | Full 360° situational awareness. |
|
||||||
|
| 6+ | + Gemini 720im | High-resolution real-time multibeam. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Failsafe Priority Order
|
||||||
|
|
||||||
|
When multiple conditions trigger simultaneously, the highest priority wins:
|
||||||
|
|
||||||
|
| Priority | Condition | Override behaviour |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | Emergency surface | Always wins. No assessment. Last resort. |
|
||||||
|
| 2 | Critical/emergency battery | Overrides all responses. Initiates RETURN_TO_SAFE or EMERGENCY_SURFACE. |
|
||||||
|
| 3 | Safety depth limit exceeded (300m) | Overrides hold behaviours. RETURN_TO_SAFE. |
|
||||||
|
| 4 | Obstacle detected — RED state | Overrides comms loss hold. RETURN_TO_SAFE. |
|
||||||
|
| 5 | Comms loss | Assessed response. |
|
||||||
|
| 6 | Manual abort | Assessed response. |
|
||||||
|
| 7 | Thruster anomaly | Alert and AMBER downgrade. Informational unless escalates. |
|
||||||
|
| 8 | Low battery warning (25%) | Informational. Lowest priority. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Configuration — What Goes Where
|
||||||
|
|
||||||
|
| Item | Location | Who changes it | When |
|
||||||
|
|---|---|---|---|
|
||||||
|
| State machine logic | `failsafe_monitor.py` | Developer | Code changes only |
|
||||||
|
| Battery type and thresholds | Web UI → `battery.yaml` | Operator | Per battery swap |
|
||||||
|
| Comms timeout | `failsafe.yaml` | Operator | Per deployment environment |
|
||||||
|
| Depth limits | `failsafe.yaml` | Operator | Per hardware configuration |
|
||||||
|
| Standoff distance | `mission.yaml` | Operator | Per structure type |
|
||||||
|
| Entry point and safe zone | Web UI → mission plan | Operator | Per dive |
|
||||||
|
| Operation mode (tethered/untethered) | Web UI → mission plan | Operator | Per dive |
|
||||||
|
| Sensor enable/disable flags | `sensors.yaml` | Operator | As hardware is added |
|
||||||
|
| Breadcrumb interval | `failsafe.yaml` | Developer | Tune after pool testing |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Open Items
|
||||||
|
|
||||||
|
Items requiring resolution before autonomous pool testing:
|
||||||
|
|
||||||
|
| Item | Notes | Priority |
|
||||||
|
|---|---|---|
|
||||||
|
| Breadcrumb buffer implementation | Core to tethered exit safety. Phase 2 development. | High |
|
||||||
|
| Safe zone radius default | 5m suggested. Validate against typical jacket geometry. | High |
|
||||||
|
| Comms timeout tuning | 2s default. May generate false positives in high-latency tether. Verify in pool. | High |
|
||||||
|
| Battery discharge curve | Must be measured empirically per battery type. Not assumed. | High |
|
||||||
|
| Assessment state thresholds | Specific parameter values need pool testing to tune. | Medium |
|
||||||
|
| Thruster power baseline | Must be measured in calm water to establish normal. | Medium |
|
||||||
|
| Ping2 ROS2 node | ping-python library wrapper needs implementation. Phase 3. | Medium |
|
||||||
|
| Ethernet switch inside ROV | Infrastructure decision. Enables Gemini upgrade path. | Medium |
|
||||||
|
| Path re-planning algorithm | Required for obstacle avoidance in untethered mode. Phase 4-5. | Low (future) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Archived Items
|
||||||
|
|
||||||
|
Items parked for future consideration — not current priorities:
|
||||||
|
|
||||||
|
| Item | Notes |
|
||||||
|
|---|---|
|
||||||
|
| Tether tension sensing | Surface-side tension sensors measure cable force near the drum. Useful for human operator awareness. Less useful for vehicle autonomy stack as the vehicle cannot directly act on surface-side tension data. Revisit when considering Tether Management System (TMS) design. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*End of document — ROV Failsafe Design v2.0 — May 2026*
|
||||||
|
*Next revision triggered by: pool testing results, hardware additions, or operational review.*
|
||||||
1347
docs/handover.md
Normal file
1347
docs/handover.md
Normal file
File diff suppressed because it is too large
Load Diff
116
docs/mission_recording_lifecycle.md
Normal file
116
docs/mission_recording_lifecycle.md
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
# Mission & Data-Recording Lifecycle in AUV/ROV Systems: A Survey with Recommendations for a ROS 2 Jazzy / ArduSub / MCAP Platform
|
||||||
|
|
||||||
|
## TL;DR
|
||||||
|
- **Decouple recording from the mission executor.** Across proven systems, robust telemetry/data logging is a *continuous, independently-managed process* (ArduPilot dataflash, MBARI LRAUV background logging, REMUS), while the *mission script controls what gets sampled/enabled* — not whether the recorder is alive. Adopt the same split: a dedicated ROS 2 lifecycle "recording manager" that records continuously while the mission YAML carries per-sensor record *intent*.
|
||||||
|
- **"Stop," "abort," and "return-to-home" are genuinely distinct verbs** in mature stacks (MOOS-IvP PARK vs RETURN vs STATION_KEEP; ArduSub SURFACE vs DISARM vs AUTO/RTL; REMUS "abort mission" vs "come home"). Model them as separate commands with separate recording side-effects, and make "loaded" distinct from "running" (mission upload vs entering AUTO).
|
||||||
|
- **Embed a recording spec in the mission plan but execute it through a runtime manager.** This mirrors ArduPilot's `DO_*` camera items inside the mission and QGroundControl's separation of `.plan` from app-level video settings. Use rosbag2/MCAP service control (`/rosbag2_recorder` pause/resume/split, snapshot mode) driven by your mission executor.
|
||||||
|
|
||||||
|
## Key Findings
|
||||||
|
|
||||||
|
1. **Mission state is explicit and small in proven systems.** MOOS-IvP reduces helm state to two values (`PARK`/`DRIVE`) plus an orthogonal all-stop reason and a hierarchical mode tree (INACTIVE/LOITERING/RETURNING/STATION-KEEPING). ArduPilot/ArduSub separate *mission upload* (stored in EEPROM) from *execution* (entering AUTO mode), and treat SURFACE, DISARM, RTL and AUTO as distinct mode transitions.
|
||||||
|
2. **Loading ≠ starting, deliberately.** In both MOOS-IvP and ArduPilot the plan is staged first and only begins on an explicit command (MOOS `DEPLOY=true`/`MOOS_MANUAL_OVERRIDE=false`; ArduPilot mode change to AUTO). This separation enables pre-dive validation and instant, low-latency start.
|
||||||
|
3. **Recording is continuous and independently guarded, not gated on mission start.** ArduPilot logs from arming (or from power-on with `LOG_DISARMED`), uses circular-buffer rotation and disarm-based file rotation, and pre-arm checks *fail arming if logging fails* — i.e., "no recording" is treated as a no-go. MBARI's LRAUV logs continuously in the background while the Tethys mission script only toggles which instruments sample.
|
||||||
|
4. **Selective recording lives in two layers.** Mature systems express *what to record* in the plan (ArduPilot `DO_SET_CAM_TRIGG_DIST`, `IMAGE_START_CAPTURE`, `VIDEO_START_CAPTURE`; LRAUV `Enabled<Instrument>` arguments) but execute it through a runtime subsystem (camera manager, science behavior, recorder).
|
||||||
|
5. **Tethered/untethered duality is a first-class design axis.** Hybrid AUV/ROV vehicles (Aquabotix Integra, the NUS UnetStack+ROS2+MAVLink "virtual tether" system) surface manual override and autonomous control differently, and the operator-override-then-resume pattern is implemented as a mode transition that does not destroy mission state.
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
### 1. Mission Lifecycle Management
|
||||||
|
|
||||||
|
**MOOS-IvP (MIT, open source; used on Bluefin-9/-21 as the "backseat driver").** The IvP Helm runs as the single MOOS app `pHelmIvP`. Its highest-level state, published every iteration on `IVPHELM_STATE`, is binary: `PARK` or `DRIVE` (it also serves as the helm "heartbeat"). [Mit](https://oceanai.mit.edu/ivpman/pmwiki/pmwiki.php?n=Helm.HelmAsMOOS) Whether the vehicle actually moves is governed by an orthogonal all-stop status (`IVPHELM_ALLSTOP`, e.g. `clear`, `ManualOverride`, `MissingDecVars:depth`). The MIT documentation makes the car analogy explicit: "Just because a vehicle is not moving doesn't mean the high-level helm state is in park." [Mit](https://oceanai.mit.edu/ivpman/pmwiki/pmwiki.php?n=Lab.ClassHelmAutonomy) Above this, missions use **Hierarchical Mode Declarations**: the Charlie example mission defines INACTIVE, LOITERING, RETURNING, and STATION-KEEPING, with RETURNING ⊂ ACTIVE, [Mit](https://oceanai.mit.edu/ivpman/pmwiki/pmwiki.php?n=Helm.HelmXMissCharlie) etc. Transitions are driven by *poking* MOOS variables — `DEPLOY`, `RETURN`, `STATION_KEEP`, `MOOS_MANUAL_OVERRIDE` — typically via pMarineViewer buttons. The alpha mission's DEPLOY button posts both `DEPLOY=true` and `MOOS_MANUAL_OVERRIDE=false`; RETURN posts `RETURN=true`. [Mit](https://oceanai.mit.edu/ivpman/pmwiki/pmwiki.php?n=Lab.ClassHelmAutonomy) Crucially these are *distinct verbs*: RETURN sends the vehicle home while remaining in DRIVE; STATION_KEEP holds position; manual override parks the helm. The helm can self-park on an all-stop event. Loading is separate from running: the helm is configured by a `.moos` mission file and `.bhv` behavior file at launch and typically *begins in PARK*, entering DRIVE only on operator command — life events (behavior spawning) don't occur until DRIVE.
|
||||||
|
|
||||||
|
**ArduSub / ArduPilot (the autopilot on your RPi4).** Mission commands are uploaded and "stored on the flight controller in EEPROM and executed one-by-one when the vehicle is switched into Auto mode." This is the canonical load-then-start split. Command vocabulary is the MAVLink `MAV_CMD` set, with ArduSub's supported subset inferred from `/ArduSub/commands_logic.cpp`: `MAV_CMD_NAV_WAYPOINT`, `MAV_CMD_NAV_RETURN_TO_LAUNCH`, `MAV_CMD_NAV_LOITER_*`, plus `DO_*`/`CONDITION_*`. At any moment "at most one Navigation command and one Do or Condition command can be running." [ArduPilot](https://ardupilot.org/sub/docs/common-mavlink-mission-command-messages-mav_cmd.html) NAV commands have highest priority; pending DO/CONDITION commands are dropped when the next NAV command loads. ArduSub's *flight modes* (MANUAL, STABILIZE, DEPTH HOLD, AUTO, GUIDED, SURFACE) are the practical state machine. Distinct verbs are clearly separated:
|
||||||
|
- **SURFACE** mode (ascend, controlled) vs **DISARM** (stop motors; "When the vehicle's propeller stops turning… it will float to the surface") [Rutgers](https://rumfs.marine.rutgers.edu/about-us/marine-operations/remus-auv/) vs **RTL/RETURN_TO_LAUNCH** vs **AUTO** (run mission).
|
||||||
|
- Failsafes map conditions → distinct actions: GCS-heartbeat-loss → {Disarm, Depth Hold, Surface}; Leak → {Warn, Surface}; battery/EKF/pilot-input → {Warn, Disarm}. Failsafe actions fire once per trigger.
|
||||||
|
- ArduPilot's GCS failsafe even has an explicit "Continue with Mission in Auto Mode" option — i.e., the system distinguishes "abort to RTL" from "keep running the mission."
|
||||||
|
|
||||||
|
A practical caveat the report must flag: **missions in AUTO are not officially supported on ArduSub** ("Since missions are not officially supported in ArduSub, that may result in unstable behavior," [Blue Robotics](https://discuss.bluerobotics.com/t/set-waypoints-via-pymavlink/8716) per Blue Robotics staff), and BlueROV2 users routinely report AUTO/GUIDED failing without a valid position estimate (DVL/USBL). This is exactly why your architecture translates rich YAML → MAVLink `MISSION_ITEM` at execute time and runs autonomy on a companion computer (RPi5) rather than relying on ArduSub's native mission engine — the same "backseat driver" pattern MOOS-IvP uses with Bluefin's Standard Payload Interface.
|
||||||
|
|
||||||
|
**Commercial AUVs (REMUS, Bluefin, Gavia, IVER).** REMUS is programmed via the Vehicle Interface Program (VIP); the path can be modified in-mission over an acoustic link, and the REMUS Ranger deck unit issues discrete emergency verbs — explicitly "abort mission" and "come home" as *separate* commands, encoded in the Compact Control Language (CCL), "an extensible set of messages that includes typical AUV commands to start or abort missions plus status messages." Bluefin's behavior control system "facilitates dynamic insertion, removal, and modification of mission elements during execution," [Marine Technology News](https://www.marinetechnologynews.com/blogs/bluefin-21-auv-700467) and integrates third-party autonomy (MOOS-IvP) through the Standard Payload Interface, where the payload issues heading/depth/speed requests as a backseat driver.
|
||||||
|
|
||||||
|
**ROS 2 lifecycle nodes as the state-machine substrate.** ROS 2 managed nodes provide exactly the unconfigured→inactive→active→finalized state machine with transitions (configure/activate/deactivate/cleanup/shutdown/error) coordinated by an external manager. Nav2's `lifecycle_manager` (services `manage_nodes`, per-node `get_state`/`change_state`) demonstrates ordered bring-up/shutdown and pause/resume of a whole subsystem — directly applicable to managing both autonomy and recording nodes.
|
||||||
|
|
||||||
|
### 2. Data Recording Architecture
|
||||||
|
|
||||||
|
**The dominant pattern is continuous, independently-guarded recording — not mission-gated recording.**
|
||||||
|
|
||||||
|
- *ArduPilot/ArduSub.* Onboard dataflash logging is controlled by `LOG_BACKEND_TYPE`, `LOG_BITMASK`, and `LOG_DISARMED`. By default a new log starts on arming; `LOG_DISARMED=1` logs from power-on. [ArduPilot](https://ardupilot.org/sub/docs/common-downloading-and-analyzing-data-logs-in-mission-planner.html) Limited flash uses a circular buffer (oldest log overwritten; if only one file remains, logging stops rather than corrupting). `LOG_FILE_DSRMROT` rotates the file on disarm. [ArduPilot](https://ardupilot.org/sub/docs/common-downloading-and-analyzing-data-logs-in-mission-planner.html) Critically, the **pre-arm check fails arming if logging fails** ("PreArm: Logging failed" when the SD card isn't writable) [Loghat](https://loghat.app/blogs/ardupilot-prearm-error-messages-library) — the system refuses to run a mission it cannot record. Streaming logs over MAVLink "requires the dataflash logging to be started, typically when the vehicle is armed." [ArduPilot](https://ardupilot.org/mavproxy/docs/modules/dataflash_logger.html) An RC aux function (164) can pause/resume the continuous streaming messages mid-flight without stopping event logging.
|
||||||
|
- *MBARI LRAUV.* Per the MBARI mission documentation (docs.mbari.org/lrauvmissions) and the Meckler TethysL report, routine data logging is a continuous background vehicle function; the mission script governs *which instruments sample* (see §3), not whether the logger runs. The vehicle "runs a mission script that invokes appropriate AUV behaviors," [NOAA](https://repository.library.noaa.gov/view/noaa/68124/noaa_68124_DS1.pdf) under state-configured layered control, with a guaranteed underlying `Default.tl` state that invokes `Guidance:GoToSurface`/`Guidance:Wait` beneath any mission.
|
||||||
|
- *QGroundControl.* Recording is a *ground-station, app-level* concern decoupled from the mission: telemetry `.tlog` is auto-saved per flight ("Save log after each flight"), [QGroundControl](https://docs.qgroundcontrol.com/master/en/qgc-user-guide/settings_view/general.html) an optional 1 Hz CSV is written alongside, [QGroundControl](https://docs.qgroundcontrol.com/Stable_V4.3/en/qgc-user-guide/settings_view/csv.html) and *video recording* is configured entirely separately (Video Source / Video Recording settings, with a max-storage cap after which "the oldest video files will be removed"). [QGroundControl](https://docs.qgroundcontrol.com/Stable_V5.0/en/qgc-user-guide/releases/release_note_stable_v3.html) The mission `.plan` file does not contain the video recording configuration.
|
||||||
|
|
||||||
|
**Why this split exists — the two canonical failure modes:**
|
||||||
|
1. *"Mission ran but nothing recorded."* Guarded by making recording a precondition for operation (ArduPilot's pre-arm logging check) and by running the recorder as a continuously-active managed node with health monitoring, independent of mission start.
|
||||||
|
2. *"Continuous recording filled the disk."* Guarded by bag/file splitting (`--max-bag-size`, `--max-bag-duration`), circular-buffer/retention policies (ArduPilot flash rotation, QGC oldest-video deletion), compression (MCAP zstd/lz4), and snapshot/ring-buffer modes.
|
||||||
|
|
||||||
|
**Where recording control should live architecturally.** The evidence favors a *dedicated recording manager* separate from the mission executor, with the mission *plan* supplying intent and the *ground station* able to override. ArduPilot puts logging in the autopilot core (guarded, always-on); MBARI puts it in the vehicle system layer beneath missions; QGC puts operator-facing capture in the GCS. None put the *liveness* of recording inside the mission script.
|
||||||
|
|
||||||
|
**ROS 2 / MCAP specifics.** Since the ROS 2 Iron Irwini release (May 2023), rosbag2 defaults to the MCAP storage plugin (the default `--storage-id` was switched from sqlite3 to mcap in ros2/rosbag2 PR #1160, a community effort with Open Robotics and Apex AI). MCAP is append-only and recoverable — per rosbag2 maintainer james-rms in PR #1160, "Append-only write behavior ensures that only the last few messages written can be corrupted in the case of a power outage or recorder crash. This is similar to the corruption guarantees offered by SQLite3 in WAL mode." It is self-contained (embeds message definitions) and tunable: compression options None/Lz4/Zstd with `compressionLevel` Fastest→Slowest, plus a "fastwrite" preset that "does not calculate CRCs… and does not write a message index… useful for resource-constrained robots." The rosbag2 **Recorder exposes remote-control services**: `~/pause`, `~/resume`, `~/is_paused`, `~/split_bagfile`, and a **snapshot mode** keeping an in-memory circular buffer [GitHub](https://github.com/ros2/rosbag2/blob/rolling/README.md) flushed on a `~/snapshot` trigger. These are exactly the primitives needed to drive recording from a mission executor or operator UI. Note an important MCAP gotcha for your stack: **foxglove_bridge itself is a *visualization* bridge, not a recorder** — it streams topics over the Foxglove WebSocket protocol. [ROS](https://index.ros.org/p/foxglove_bridge/) MCAP files are produced either by `ros2 bag record -s mcap`, the rosbag2 Recorder API, the Foxglove SDK MCAP sink (`open_mcap()`), or the Foxglove app/Agent recording a connection. Your "MCAP recording via foxglove_bridge" should be implemented as a rosbag2 MCAP recorder (or Foxglove SDK sink) running alongside the bridge, controlled via the recorder's services.
|
||||||
|
|
||||||
|
### 3. Per-Sensor / Selective Recording
|
||||||
|
|
||||||
|
**Two established representations, used together:**
|
||||||
|
|
||||||
|
- **Mission-plan-embedded record/sample specs.** ArduPilot encodes capture *inside the mission item stream*: `MAV_CMD_DO_SET_CAM_TRIGG_DIST` (shoot every N metres), `MAV_CMD_IMAGE_START_CAPTURE`/`IMAGE_STOP_CAPTURE`, `MAV_CMD_VIDEO_START_CAPTURE`/`VIDEO_STOP_CAPTURE`, and `SET_CAMERA_MODE`. These are DO commands associated with the preceding NAV waypoint, re-emitted by the flight stack to the camera component at execution time. MBARI's LRAUV expresses per-sensor enabling as *named mission arguments* on the `Insert/Science.tl` module: `EnabledSeabird`, `EnabledWetLabsUBAT`, `EnabledAanderaaO2`, `EnabledGliderFET`, `EnabledNeilBrown`, `EnabledRBRTridente`, `EnabledWetLabsBB2FL`, `EnabledWetLabsSeaOWL_UV_A`. [mbari](https://docs.mbari.org/lrauvmissions/missions/Insert/Science.tl/) A demo mission literally named `science_on.xml` turns sensors on as mission state. This is the strongest open, real-world precedent for embedding per-stream record/sample selection in the mission file.
|
||||||
|
- **Runtime topic-filter selection.** rosbag2 records a chosen subset via `--topics t1 t2 …`, `--all`, [GitHub](https://github.com/ros2/rosbag2) `--regex`/`--exclude-regex`, and `--exclude` patterns; auto-discovery picks up topics appearing later. [ROS](https://index.ros.org/r/rosbag2/) This maps cleanly onto "record profiles" (e.g., a *navigation-only* profile vs *full-sensor* profile) and to per-camera/sonar/CTD topic groups.
|
||||||
|
|
||||||
|
**Recommended representation for your YAML.** Embed a `recording:` block in the mission YAML listing record profiles and per-stream toggles (camera_fwd, sonar, CTD, nav), analogous to LRAUV's `Enabled<Instrument>` arguments, and translate it at execute-time into (a) the rosbag2 recorder's topic set / profile and (b) any MAVLink camera DO-items for ArduSub-side capture — exactly the same execute-time translation you already perform from YAML to `MISSION_ITEM`.
|
||||||
|
|
||||||
|
### 4. Mission Planner Integration
|
||||||
|
|
||||||
|
**Recording configuration is generally a *separate layer* from the geometric mission plan, with a thin embedded hook for capture triggers.**
|
||||||
|
|
||||||
|
- *QGroundControl.* The `.plan` file consolidates mission items, geofence and rally points; camera *actions* (photo/video mode, gimbal, trigger-by-distance) can be attached to mission items in the Plan view. But the *video recording pipeline* (source, file format, storage cap, "Disabled When Disarmed") lives in app Settings, entirely outside the `.plan`. Telemetry logging is likewise an app setting. This is a deliberate two-layer design: plan-time capture *intent* vs ground-station *recording mechanism*.
|
||||||
|
- *MOOS-IvP.* Logging is a separate MOOS process, `pLogger`, configured in the `.moos` file (e.g. `WildCardLogging = true`, `WildCardOmitPattern = *_STATUS`) [Mit](https://oceanai.mit.edu/svn/moos-ivp-aro/trunk/ivp/missions/station-keep-uuv/davis.moos) — i.e., recording configuration sits in the mission *configuration* file but in its own process block, independent of the behavior (`.bhv`) logic that defines the mission.
|
||||||
|
- *MBARI LRAUV / TethysL.* Mission scripts are validated against a schema before compilation; [mbari](https://www.mbari.org/wp-content/uploads/2016/07/Meckler_Eli.pdf) [MBARI](https://www.mbari.org/wp-content/uploads/2016/07/Meckler_Eli.pdf) sensor sampling is set inside the mission via the Science insert's arguments, while logging runs continuously beneath.
|
||||||
|
|
||||||
|
**Best practice on *where* "what to record" is decided.** Decide *default* recording at planning time (embedded in the plan/profile, so a mission is reproducible and a plan implies its data products), but always allow *runtime override* from the operator console (start/stop/split/snapshot, add/drop a stream). This is precisely the division QGC, MOOS-IvP and ArduPilot all converge on.
|
||||||
|
|
||||||
|
### 5. Human-in-the-Loop (Tethered) vs Fully Autonomous (Untethered)
|
||||||
|
|
||||||
|
**Mode duality is explicit in hybrid vehicles.** Aquabotix's second-generation Integra AUV/ROV (launched Dec 11, 2017) "can search wide areas using AUV mode (untethered) while conducting detailed inspections using ROV mode (tethered). Users can easily switch from AUV mode to ROV mode by attaching the tether to remotely control the vehicle's six degrees of freedom of motion." A 2025 IEEE paper presented at OCEANS 2025 Brest by the NUS Acoustic Research Laboratory (lead author Rajat Mishra), "Design and Demonstration of a Wireless Hybrid AUV/ROV for Subsea Inspections," builds precisely your class of stack — "a scalable software stack built on UnetStack, ROS2 and MAVLINK," replacing the physical tether with an "innovative, software-defined virtual tether," validated "in both a deepwater ocean simulator and sea trials in Singapore," motivated by the need to "rapidly adjust mission parameters in real time" [Nus](https://arl.nus.edu.sg/wp-content/uploads/2025/07/Design_and_Demonstration_of_a_Wireless_Hybrid_AUV_ROV_for_Subsea_Inspections.pdf) that pure autonomy lacks.
|
||||||
|
|
||||||
|
**Operator-override-that-resumes (not aborts).** The proven primitive is a *mode transition that preserves mission state*:
|
||||||
|
- ArduPilot's GCS failsafe option "Continue with Mission in Auto Mode" explicitly keeps the mission running rather than aborting; conversely the operator can drop to MANUAL/STABILIZE/DEPTH HOLD to take control and then re-enter AUTO. Because the mission is stored in EEPROM, re-entering AUTO resumes it.
|
||||||
|
- MOOS-IvP's `MOOS_MANUAL_OVERRIDE` parks the helm (operator takes control) without destroying the loaded behaviors; clearing it returns to DRIVE and the mission continues. The Charlie mission shows STATION_KEEP as a temporary hold injectable "by another MOOS process… connected to an acoustic modem, an Iridium satellite, or wifi interface," [Mit](https://oceanai.mit.edu/ivpman/pmwiki/pmwiki.php?n=Helm.HelmXMissCharlie) then resuming loitering.
|
||||||
|
- REMUS supports in-mission plan modification and discrete "come home"/"abort" over the acoustic link without a full restart.
|
||||||
|
|
||||||
|
**Recording continuity across mode transitions (tethered → autonomous → recovered).** The continuous-recording architecture is what guarantees continuity: because the recorder is not bound to mission state, transitions between manual and autonomous control don't interrupt the data stream. ArduPilot models the file-rotation choice explicitly (`LOG_FILE_DSRMROT`: rotate on disarm, or resume the same file on re-arm). For your stack, the analogous decision is whether a tethered→autonomous transition should split the MCAP file (clean per-phase bags via `~/split_bagfile`) or continue one bag with a mode-annotation topic. Splitting on phase boundaries is generally preferable for analysis (it avoids long gaps and makes per-phase retrieval trivial), and matches the rosbag2 maintainers' own recommendation to split bags on pause/resume boundaries.
|
||||||
|
|
||||||
|
## Recommendations (for a ROS 2 Jazzy / ArduSub / MCAP / foxglove_bridge inspection ROV)
|
||||||
|
|
||||||
|
**Architecture — adopt the "guarded continuous recorder + intent-bearing plan" split:**
|
||||||
|
1. **Run recording as a dedicated ROS 2 lifecycle node ("recording manager"), separate from the mission executor.** Bring it to `active` *before* any mission can start, and treat "recorder not active" as a hard no-go — the direct analogue of ArduPilot's pre-arm "Logging failed" gate. Manage it with a Nav2-style lifecycle manager alongside your autonomy nodes.
|
||||||
|
2. **Record continuously while armed/deployed, not gated on mission "running."** Start the MCAP recorder when the system is brought up for a dive (or on arm), independent of whether a mission is loaded or AUTO is engaged. This eliminates the "mission ran but nothing recorded" failure mode and preserves data across tethered↔autonomous transitions.
|
||||||
|
3. **Implement recording with rosbag2's MCAP recorder (or Foxglove SDK MCAP sink), controlled via services — and keep foxglove_bridge for live viz only.** Use `~/pause`, `~/resume`, `~/split_bagfile`, and consider snapshot mode for event-triggered capture. Do not rely on foxglove_bridge to produce the MCAP file; it is a WebSocket visualization bridge. Run the recorder and the bridge as separate nodes on the RPi5.
|
||||||
|
|
||||||
|
**Mission state machine — keep it small and explicit:**
|
||||||
|
4. **Model states as IDLE → LOADED → RUNNING → (PAUSED) → COMPLETED/ABORTED**, and keep *loaded* strictly separate from *running* (MOOS-IvP PARK-then-DRIVE; ArduPilot upload-then-AUTO). Loading should validate the YAML against a schema before it can be armed — directly borrowing MBARI's discipline of validating every Tethys mission against a schema with a *mandatory mission timeout* (the Meckler TethysL report states the schema "insures some safety in mission structure: the mission must have certain components (such as a mission timeout) to mitigate risks").
|
||||||
|
5. **Make STOP, ABORT, and RETURN-TO-HOME distinct commands with distinct recording side-effects:**
|
||||||
|
- *PAUSE/STOP* (operator takes manual control): keep recording, optionally split the bag; preserve mission state for resume (MOOS override / ArduSub mode drop).
|
||||||
|
- *RETURN-TO-HOME*: a normal terminal phase — keep recording through the transit, mark a bag split.
|
||||||
|
- *ABORT* (safety): trigger the ArduSub failsafe action (SURFACE for leak; DISARM/SURFACE for comms loss), and *finalize* the current bag cleanly (MCAP append-only guarantees recoverability even if power is lost).
|
||||||
|
6. **Provide an operator "override-and-resume" path that continues the mission**, mirroring ArduPilot's "Continue with Mission in Auto Mode" and MOOS `MOOS_MANUAL_OVERRIDE` clear — re-entering AUTO/autonomy resumes the loaded mission rather than reloading it.
|
||||||
|
|
||||||
|
**Selective recording — embed intent, execute at runtime:**
|
||||||
|
7. **Add a `recording:` block to your mission YAML** with named *record profiles* (e.g., `nav_only`, `inspection_full`) and per-stream toggles (forward camera, sonar, CTD, navigation), modeled on LRAUV's `Enabled<Instrument>` arguments and ArduPilot's in-mission camera DO-items. At execute-time, translate this into (a) the rosbag2 topic set/profile and (b) any MAVLink camera `DO_*` items — the same YAML→`MISSION_ITEM` translation you already do.
|
||||||
|
8. **Allow runtime override of the record selection from the console** (add/drop a stream, force start/stop/split), because every proven planner (QGC, MOOS, ArduPilot) supports both plan-time defaults and runtime control.
|
||||||
|
|
||||||
|
**Disk-safety and retention — engineer against the "filled the disk" failure mode:**
|
||||||
|
9. **Split MCAP files by size and duration** (`max-bag-size`/`max-bag-duration`), enable **zstd or lz4 compression** (use the Fastest level or the CRC-free "fastwrite" preset on the RPi5 if CPU-bound), and implement a **retention/circular policy** (delete-oldest when free space drops below a threshold), mirroring ArduPilot flash rotation and QGC's oldest-video deletion. Split bags on mission-phase boundaries for clean per-phase analysis.
|
||||||
|
10. **Record a low-rate "always-on" core** (vehicle state, mode, mission status, nav) plus a high-rate sensor set governed by the active profile — analogous to ArduPilot's separable `LOG_BITMASK` streams and QGC's 1 Hz CSV alongside the full tlog.
|
||||||
|
|
||||||
|
**Field-deployability:**
|
||||||
|
11. **Annotate every bag with mission metadata** (MCAP supports metadata records/attachments): the mission YAML, schema version, vehicle/firmware IDs, and the record profile in force — so a recording is self-describing and reproducible (LRAUV's reproducible-mission discipline).
|
||||||
|
12. **Surface recorder health in the operator UI and in telemetry**, treating a recorder fault like a failsafe condition (warn, and block mission start), consistent with making "no recording" a no-go.
|
||||||
|
|
||||||
|
**Benchmarks / thresholds that would change these recommendations:**
|
||||||
|
- If sustained write bandwidth (multi-camera + sonar) exceeds the RPi5's reliable disk throughput, move to hardware-accelerated video encode (as in the ZED ROS 2 / NVENC workflow) and record compressed video topics rather than raw frames.
|
||||||
|
- If missions become long-endurance/untethered for hours, shift from "record everything continuously" toward snapshot/event-triggered capture for the highest-rate streams while keeping the low-rate core continuous.
|
||||||
|
- If ArduSub's native AUTO mode proves usable with your DVL/USBL position source, some capture triggers could move into the MAVLink mission stream; until then, keep capture control in the RPi5 recording manager.
|
||||||
|
|
||||||
|
## Caveats
|
||||||
|
- **Missions in ArduSub AUTO mode are not officially supported** (Blue Robotics staff statement) and commonly fail without a valid position estimate; this report assumes — consistent with your design — that autonomy and mission execution live on the RPi5 companion computer with ArduSub used primarily for low-level control and failsafes.
|
||||||
|
- **foxglove_bridge does not itself record MCAP.** Several teams conflate the bridge (live WebSocket visualization) with recording (rosbag2 MCAP plugin / Foxglove SDK sink / Foxglove app). The recommendation treats them as separate components.
|
||||||
|
- **Some commercial details (Greensea OPENSEA, Kongsberg/Hydroid REMUS, Teledyne Gavia internal mission/recording formats) are not publicly documented** at the source-code level; findings on those systems rely on vendor/operator descriptions and secondary literature, and should be treated as indicative rather than authoritative.
|
||||||
|
- **MBARI LRAUV "continuous background logging" is partially inferred.** The primary design paper (Godin et al. 2010, "Scripting language for state configured layered control of the Tethys long-range autonomous underwater vehicle," OCEANS 2010 MTS/IEEE Seattle, DOI 10.1109/OCEANS.2010.5664515) is paywalled; the continuous-logging-plus-script-controlled-sampling characterization is corroborated by the MBARI mission documentation and the Meckler TethysL report but the exact logging-daemon wording could not be quoted verbatim. The mandatory **mission timeout** requirement *is* directly quoted from the Meckler report. No component named "LogVars" was found in the LRAUV documentation; sensor capture is controlled via the `Enabled<Instrument>` arguments and Sample/ESP behaviors.
|
||||||
|
- **The AUV mission-state academic literature** (e.g., MahmoudZadeh et al.; Albiez et al., "Adaptive AUV mission management in under-informed situations," OCEANS 2010) focuses on planning/replanning under uncertainty rather than recording lifecycle; it supports the "plan database with dynamic insertion/removal" concept (also seen in Bluefin) but does not prescribe recording architecture.
|
||||||
150
docs/simulation_ml_plan.md
Normal file
150
docs/simulation_ml_plan.md
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
# Argonaut 3 — Simulation & ML Tooling: Hardware, Cloud & Training-Asset Plan
|
||||||
|
**Version:** 1.0
|
||||||
|
**Date:** July 2026
|
||||||
|
**Status:** Scoping reference. Companion to the underwater-simulation research report. Registers the compute/platform decisions that report deferred to hardware, plus training-asset sourcing and the PCIe Gen 3 option. Nothing here is purchased or committed — this is the options board for scoping, not an implementation order.
|
||||||
|
|
||||||
|
> **Scope note.** KISS applies to *implementing a chosen path*, not to scoping. This document deliberately lays out options and trade-offs so a decision can be made on facts, not narrowed prematurely.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. The two tracks and where they converge
|
||||||
|
|
||||||
|
- **Track 1 — Control / navigation / mission (simulation-in-the-loop).** Bench-test the ROS2 autonomy stack (`mission_executor`, failsafe, nav, recording gate) against a simulated vehicle + world, running the *same* ArduSub firmware (as SITL) and MAVROS the real vehicle uses. **CPU-bound. Modest GPU.**
|
||||||
|
- **Track 2 — Perception / machine learning.** Generate photorealistic, auto-labelled synthetic imagery of structures and train feature-recognition models (VDMs, horizontals, K/X nodes, anodes). **GPU-bound. Needs a real RTX GPU.**
|
||||||
|
- **Convergence (the real bottleneck).** Track 2's trained detections become landmarks for **structure-relative visual localization** against the known CAD model — the actual unsolved foundational problem. A simulator lets this convergence be developed and validated before pool/field time.
|
||||||
|
|
||||||
|
The two tracks have different hardware centres of gravity. That is why hardware, not software, is the gating decision.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Simulator → hardware → stage → intent
|
||||||
|
|
||||||
|
| Simulator | Track | ROS2 Jazzy | Runs on | Real intent |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| **Gazebo Harmonic + `ardupilot_gazebo` (ArduSub SITL)** | 1 | Native | Laptop / dev PC (CPU-led, modest GPU) | Nav/mission/failsafe bench with real firmware + MAVROS, no hardware |
|
||||||
|
| **Stonefish** | 1 (higher-fidelity hydro) | Wrapper | Dev PC | Better hydrodynamics/sensor sim when Gazebo is too coarse |
|
||||||
|
| **HoloOcean / UNav-Sim (Unreal 5)** | 2 | Python / bridge | Dev PC **with RTX** | Photorealistic labelled synthetic imagery for perception training |
|
||||||
|
| **Isaac Sim + OceanSim** | 2 (+ RL) | Native (Isaac ROS) | Dev PC **with RTX 4080+** | Large-scale synthetic data, domain randomization, RL if pursued |
|
||||||
|
|
||||||
|
Track 1 runs on hardware already owned (a laptop, once it isn't fighting a VM). Track 2 is what forces a GPU purchase-or-rent decision.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Why the past SITL failures do NOT block this
|
||||||
|
|
||||||
|
Recorded facts, not memory:
|
||||||
|
|
||||||
|
- SITL failed in **two under-resourced hosts**: the server BlueOS VM (66–70% CPU on 2 vCPUs, May 2026) and the **RPi4**, where on 22 Jun 2026 a *fresh SD card* still showed `connected: false` — proving the SD was **not** the root cause. The real cause was RPi4 resource strain + a half-connected Pixhawk making BlueOS thrash between real board and SITL.
|
||||||
|
- The laptop's own pain was separate: Windows `VirtualMachinePlatform`/`vmmem` consuming ~4 GB RAM / 55% CPU.
|
||||||
|
|
||||||
|
None of that is intrinsic to SITL. On an adequate dev PC with nothing competing for the FCU, **SITL + Gazebo is the standard, well-supported bench** (`bluerov2_gz` / `orca4` reference stacks). SITL is *software in place of* the Pixhawk — in simulation the physical Pixhawk is not used at all; that is the point.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Dev PC — minimum / recommended / high-end
|
||||||
|
|
||||||
|
VRAM is the gating spec. 16 GB is the practical sweet spot; 24 GB+ buys headroom and RL capacity. All three tiers run Track 1 (Gazebo) comfortably — the tiers are set by the Track 2 GPU load.
|
||||||
|
|
||||||
|
| Tier | CPU / RAM | GPU (VRAM) | ~USD (box) | What it unlocks |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| **Minimum** | 6–8 core (Ryzen 7 / i5-13600K), 32 GB | RTX 4060 Ti **16 GB** or RTX 4070 12 GB | $1,200–1,600 | Gazebo comfortably; HoloOcean/UNav-Sim OK; YOLO/SAM fine-tuning fine. Isaac Sim runs but is *below its stated RTX 4080 minimum* (unsupported, may glitch). |
|
||||||
|
| **Recommended** | 8+ core (Ryzen 9 / i7-14700K), 64 GB | RTX 4080 Super 16 GB or RTX 5070 Ti / 5080 **16 GB** | $2,000–2,800 | Meets Isaac Sim's stated minimum. Comfortable synthetic-data generation + training across all Track 2 sims. The sensible target if buying once. |
|
||||||
|
| **High-end** | 12+ core (Ryzen 9 / i9 / Threadripper), 64–128 GB | RTX 4090 24 GB / RTX 5090 **32 GB** (or RTX 6000 Ada 48 GB pro) | $3,500–6,000+ | Fast iteration, large Replicator/synthetic batches, RL training, big scenes. Overkill until Track 2 is a daily activity. |
|
||||||
|
|
||||||
|
Hard facts behind the tiers:
|
||||||
|
- **Unreal Engine 5** (HoloOcean/UNav-Sim): playable from ~6 GB VRAM; Epic/Puget recommend a strong RTX card with ample VRAM for Nanite/Lumen work.
|
||||||
|
- **Isaac Sim (current docs):** minimum **RTX 4080, 16 GB VRAM**, 32–64 GB RAM; **RT cores required — A100/H100 are NOT supported**; 8 GB VRAM is insufficient for complex scenes.
|
||||||
|
- **ML fine-tuning** (YOLO / SAM2 / Grounding DINO): 12–16 GB VRAM comfortable; 24 GB generous.
|
||||||
|
|
||||||
|
**OS:** Ubuntu (native ROS2 Jazzy + Isaac/Gazebo) is the least-friction path; dual-boot Windows if the laptop's other duties need it. Do **not** run the GPU sim inside a Windows VM — that is a repeat of the `vmmem` problem.
|
||||||
|
|
||||||
|
**The R210 server cannot host this.** 1U, no slot/power/thermal budget for a modern GPU. It stays a services box. A GPU dev PC is a *separate* machine.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Cloud GPU — alternative / complement to a dev PC
|
||||||
|
|
||||||
|
**Critical caveat first:** cloud GPU is a **headless notebook/batch** resource — excellent for *training* and for *batch synthetic-data generation* runs, but a poor fit for **interactive** Unreal/Gazebo GUI work (needs a display server / pixel-streaming and is fiddly). So cloud **complements** a local dev PC (which you still want for interactive Track 1 + Track 2 dev); it does not fully replace one.
|
||||||
|
|
||||||
|
### Free facilities (no/low cost, good for training + learning)
|
||||||
|
| Platform | Free GPU | Allowance | Credit card | Best for |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| **Kaggle Notebooks** | P100 16 GB or 2×T4 | ~30 GPU-hr/week, 9-hr sessions, background exec | No | Most reliable free tier; fine-tuning small–mid models |
|
||||||
|
| **Google Colab** | T4 16 GB | ~15–30 GPU-hr/week (dynamic), ≤12-hr sessions | No | Quick experiments, LoRA/QLoRA, SD |
|
||||||
|
| **Lightning AI** | T4/L4 up to A100 (interruptible) | ~80 GPU-hr/mo credits, persistent IDE | No | Structured PyTorch dev, checkpoint-friendly |
|
||||||
|
| **Paperspace Gradient** | M4000 8 GB | 6-hr sessions, unlimited restarts | No | Light notebooks |
|
||||||
|
| **SageMaker Studio Lab** | T4 | 4-hr sessions | No | ⚠ AWS closing new signups **30 Jul 2026** |
|
||||||
|
| **Hyperscaler trial credits** | varies | GCP $300/90d, Azure $200/30d, AWS Activate (startups) | Yes | Short benchmarking bursts; GPU quota often refused on new accounts |
|
||||||
|
|
||||||
|
### Paid on-demand ($/GPU-hr, mid-2026 snapshot — verify live at deploy)
|
||||||
|
| GPU (VRAM) | Cheapest (Vast.ai / RunPod community) | Mid (Lambda / RunPod secure) | Use |
|
||||||
|
|---|---|---|---|
|
||||||
|
| RTX 4090 (24 GB) | ~$0.31–0.34 | ~$0.59 | Perception training, synthetic-data batch, ≤13B inference |
|
||||||
|
| A100 (40/80 GB) | ~$0.60–1.07 | ~$1.29–1.99 | Larger training / fine-tuning |
|
||||||
|
| H100 (80 GB) | ~$1.03–2.25 | ~$2.49–3.29 | Only if scaling large / RL |
|
||||||
|
|
||||||
|
Cheapest providers: **Vast.ai, RunPod, TensorDock, Lambda**. Hyperscalers (AWS/GCP/Azure) run 2–5× more and price whole VMs, not naked GPUs.
|
||||||
|
|
||||||
|
**Practical read:** a solo developer can do the entire perception-training track on **Kaggle/Colab free + occasional RunPod/Vast RTX 4090 at ~$0.33/hr** with *zero* capex, and defer or skip the dev-PC GPU. The dev PC earns its cost only when interactive sim work becomes frequent. This is a genuine fork worth deciding on cost, not reflex.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Models & data for training (paid + free)
|
||||||
|
|
||||||
|
Three distinct things get called "models." All three are needed.
|
||||||
|
|
||||||
|
### A. 3D assets to build simulated scenes (drop into the sim)
|
||||||
|
- **Free:** **Fab** (Epic's marketplace; **Quixel Megascans now free**), **Poly Haven** (CC0), **Sketchfab** (CC-filtered), **BlenderKit** (free tier), **NVIDIA SimReady / Omniverse** assets.
|
||||||
|
- **Paid:** **TurboSquid**, **CGTrader**, Fab premium.
|
||||||
|
- **Key point:** the actual structure CAD (EBM/NWD → IFC → glTF/GLB) is already owned — that is the irreplaceable asset. These libraries only fill the *surroundings* (seabed, marine growth, debris, props) for domain randomization.
|
||||||
|
|
||||||
|
### B. Datasets to train/transfer-learn on (free, real underwater)
|
||||||
|
| Dataset | Content | Relevance |
|
||||||
|
|---|---|---|
|
||||||
|
| **SubPipe** | Submarine pipeline inspection — RGB + side-scan sonar + DVL + INS, annotated | Closest real structure-inspection set; matches sensor suite direction |
|
||||||
|
| **COU (Common Objects Underwater)** | ~10k instance-segmented man-made objects | Man-made object seg, AUV-oriented |
|
||||||
|
| **TrashCan / Seaclear / Brackish / SUIM** | Debris, marine life, segmentation | General underwater vision pre-training / augmentation |
|
||||||
|
| **MIMIR-UW** | *Simulated* pipe inspection with labels | Sim-to-real reference |
|
||||||
|
|
||||||
|
> **Honest finding:** **no public dataset covers offshore jacket features (VDMs, K/X nodes, anodes).** That gap is exactly why the CAD-model + synthetic-data pipeline is the path — synthesize labelled jacket imagery from the owned CAD, pre-train/transfer from the sets above, then fine-tune on real field captures as they accumulate.
|
||||||
|
|
||||||
|
### C. Base ML models to fine-tune (free / open weights)
|
||||||
|
- **Ultralytics YOLO** (v8/v11) — detection + segmentation, light enough for on-vehicle ONNX/TFLite later.
|
||||||
|
- **SAM2** (Meta) — promptable segmentation; underwater-adapted variants exist (e.g. SAM-U).
|
||||||
|
- **Grounding DINO** — open-vocabulary detection, useful for bootstrapping labels.
|
||||||
|
|
||||||
|
These are the *starting weights*; you fine-tune them on synthetic (from A/CAD) + real (from B and field) data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. PCIe Gen 3 on the RPi5 (data drive) — option, logged
|
||||||
|
|
||||||
|
Kept brief per direction. This is future-proofing, not a current need.
|
||||||
|
|
||||||
|
| | Fact |
|
||||||
|
|---|---|
|
||||||
|
| How | `dtparam=pciex1_gen=3` in `/boot/firmware/config.txt`, reboot |
|
||||||
|
| Throughput | PCIe 2.0 x1 ≈ ~500 MB/s → PCIe 3.0 x1 ≈ ~900 MB/s (~2×) |
|
||||||
|
| Certification | RPi5 is **officially certified only at Gen 2**; Gen 3 is **enabled but not validated** by Raspberry Pi |
|
||||||
|
| Scope | Affects the **NVMe data drive** (`/data`) only — boot is on SD, so a bad link corrupts bags/logs, not boot |
|
||||||
|
|
||||||
|
**Pros:** ~2× NVMe throughput; one-line, reversible; free headroom for when camera streams are recorded to MCAP.
|
||||||
|
**Cons:** uncertified — some drives show link dropouts/corruption at Gen 3; **current benefit ≈ nil** (MCAP is nav/MAVLink only, a trickle Gen 2 already over-serves); the drive holds recorded dive data.
|
||||||
|
|
||||||
|
**Decision:** **Defer.** Revisit at **Phase 3 camera-to-MCAP integration**, and only enable after a stability soak on the specific drive (the official RPi SSD is the likeliest to be stable at Gen 3). Not worth touching for the current nav-only workload.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Effort read (solo developer)
|
||||||
|
|
||||||
|
- **Track 1 (Gazebo bench):** near-term, achievable, **needs no purchase to start** — the current laptop runs it once it isn't hosting a VM. Highest-value near-term win: bench-test mission/failsafe logic without hardware.
|
||||||
|
- **Track 2 (perception + training):** the harder, longer track. Start on **free cloud (Kaggle/Colab) + occasional rented RTX 4090**; buy the **Recommended dev PC** only when interactive sim/data-gen becomes frequent.
|
||||||
|
- **Convergence (structure-relative localization):** the genuine multi-year problem. Sim de-risks it; it is not a quick win.
|
||||||
|
- **RL:** research-grade. Defer until perception + localization are working.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
| Version | Date | Change |
|
||||||
|
|---|---|---|
|
||||||
|
| 1.0 | July 2026 | Initial. Simulator→hardware→stage mapping; dev-PC tiers (min/rec/high); cloud free + paid options with mid-2026 pricing; 3D-asset/dataset/base-model sourcing; PCIe Gen 3 decision (defer). Corrects the record on why past SITL failures do not block a dev-PC SITL+Gazebo bench. |
|
||||||
502
docs/ui_design.md
Normal file
502
docs/ui_design.md
Normal file
@ -0,0 +1,502 @@
|
|||||||
|
# Argonaut 3 — UI Design Specification
|
||||||
|
**Version:** 2.0
|
||||||
|
**Date:** 9 July 2026
|
||||||
|
**Status:** ⚠ **SAFETY-CRITICAL FINDING — W1 does not see the vehicle.** With the flight controller
|
||||||
|
unreachable, W1 displayed GREEN / "Systems nominal". Every widget reflects the **RPi5 autonomy
|
||||||
|
stack**, not the vehicle. In AUV mode W1 is the pre-dive GO/NO-GO gate (`health_role: gate`) — as
|
||||||
|
built it would pass a dive with a dead FC. See §W1 and DIR-7 / DIR-12. **OPEN.**
|
||||||
|
|
||||||
|
**This session:** `rov_api` `/mode` endpoints (GET/POST) built, deployed, tested (`9109439`).
|
||||||
|
**W8 Mode Indicator built and validated live — render + data** (`87000e6`) — the first widget
|
||||||
|
beyond W0/W1 confirmed with real data. Chronic "widgets show no data" fault root-caused:
|
||||||
|
`cockpit_bridge` had no `respawn`, so a boot-time race left the data-lake feed permanently dead
|
||||||
|
(`c099c00`); fixed and verified across a cold stack restart.
|
||||||
|
|
||||||
|
**Prior status (v1.9):** FastAPI backend (`rov_api`) core deployed and verified on RPi5:8081. W3 backend
|
||||||
|
(`/abort`) now LIVE and confirmed end-to-end (button path → failsafe_monitor FSM). Mission
|
||||||
|
start/stop endpoints available. W3 widget label decision "RETURN TO SAFE" stands; widget code
|
||||||
|
update + `FASTAPI_HOST` config still pending. W6 committed as `w6_altitude.json`. Stage 2b
|
||||||
|
endpoints (return budget, backup, mission upload) deferred. Production data path confirmed as
|
||||||
|
cockpit_bridge WebSocket.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
| Version | Date | Changes |
|
||||||
|
|---|---|---|
|
||||||
|
| 2.0 | 9 Jul 2026 | **⚠ SAFETY-CRITICAL: W1 does not see the vehicle.** With the FC unreachable, W1 showed GREEN / "Systems nominal". `failsafe_monitor` does not gate on FC connectivity; absence of data is being read as good data. In AUV mode W1 is the pre-dive GO/NO-GO gate — as built it would pass a dive with a dead flight controller. W1 spec rewritten with required fixes; status line promoted from debug affordance to operational element carrying cause text. See DIR-7, DIR-12. **OPEN.** **W8 Mode Indicator added** — read-only active-profile display, colour-coded by base mode; validated live (render + data), the first widget beyond W0/W1 so confirmed (`87000e6`). Mode *switching* explicitly excluded from W8 (belongs to the setup wizard, `POST /mode`); ArduSub arm/vehicle mode excluded (Cockpit shows it natively). **Widget testing status corrected** — "renders" and "works" are now explicitly distinguished throughout: validated with live data = W0, W1, W8; render-only = W2–W6; not built = W7. Prior wording conflated the two. **`rov_api` `/mode` endpoints** (GET/POST) built, deployed, tested incl. rejection path (`9109439`). **Chronic "widgets show no data" root-caused:** `cockpit_bridge` was launched but had no `respawn`; a boot-time race left it permanently dead, killing the whole data-lake feed. Fixed (`c099c00`), verified across a cold stack restart. This — not the widgets — was why W1 "sometimes" worked. |
|
||||||
|
| 1.9 | July 2026 | FastAPI backend (`rov_api`) core deployed on RPi5:8081 (commit 66a18ef). W3 backend `/abort` now LIVE — verified end-to-end (POST /abort → Bool /rov/mission/abort → failsafe_monitor flag_manual_abort → FSM NORMAL→HOLD_AND_RECOVER). Mission endpoints `/mission/start` and `/mission/stop` available and tested. `/health` returns live mission/recording/failsafe. W6 committed to Gitea as `w6_altitude.json` (underscore convention); probe widget removed. W3 widget code update (label + FASTAPI_HOST) still pending. Stage 2b deferred: `/return_budget` (W5), `/backup/*` (W7), `/mission/upload`. FastAPI runs as argonaut-api.service (manual-start). |
|
||||||
|
| 1.8 | July 2026 | W6 Altitude Widget built and deployed (`w6_altitude.json`). NO SONAR state confirmed — correct baseline with no Ping2. cockpit_bridge updated to 8 variables: `rov-recording` and `rov-altitude` added. W3 button label decided: "RETURN TO SAFE" (assessed vehicle return-to-safe per DIR-5, not emergency hard-stop; widget code update pending). Widget testing status clarified: W0 confirmed live, W1–W5 visual confirmed only, W7 not built. Production data path: cockpit_bridge WebSocket is confirmed production path — NAMED_VALUE bridge node not required. |
|
||||||
|
| 1.7 | June 2026 | W7 (Backup Due Notice) added to widget scope — operator-facing reminder that a device backup is due, with delay options. Depends on RPi5:8081 FastAPI backend. See W7 spec and DIR-11. |
|
||||||
|
| 1.6 | June 2026 | Cockpit v1.18.2 DIY widget API confirmed via probe widget. Full `window.cockpit` surface (17 keys) enumerated. Confirmed: `getDataLakeVariableData(id)` returns live value. W1–W5 can now be built. |
|
||||||
|
| 1.5 | June 2026 | Production data path confirmed as `cockpit_bridge` ROS2 node (port 9001, in rov_mission, commit 12b5161) — all 6 vars live in data lake as `external/rov-*`. W0 working with scroll fix. |
|
||||||
|
| 1.4 | May 2026 | Data injection architecture fully documented with confirmed working path (Generic WebSocket) and all failed approaches. Widget variable names corrected. |
|
||||||
|
| 1.3 | May 2026 | Cockpit native app confirmed required. JSON import format confirmed. CSS rules established. All 6 widgets complete and committed to Gitea. |
|
||||||
|
| 1.2 | May 2026 | Confirmed DIY widget approach, Data Lake Inspector as W0, build status added. |
|
||||||
|
| 1.1 | May 2026 | Added Mission Setup button widget. |
|
||||||
|
| 1.0 | May 2026 | Initial UI design specification. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Approach — Cockpit DIY Widgets (Confirmed)
|
||||||
|
|
||||||
|
Widgets are Cockpit **DIY widgets** — JSON files containing `html`, `css`, `js`, and
|
||||||
|
`inheritCockpitStyles` fields, imported via the gear icon on a placed DIY widget panel.
|
||||||
|
|
||||||
|
> **Cockpit native desktop app v1.18.2 is required.**
|
||||||
|
> The BlueOS browser extension version is "Lite" and does not support DIY widgets.
|
||||||
|
> DIY widget support was added in v1.9.0.
|
||||||
|
> Generic WebSocket data injection was added in v1.18.0.
|
||||||
|
> Download: https://github.com/bluerobotics/cockpit/releases
|
||||||
|
|
||||||
|
### Import Format
|
||||||
|
|
||||||
|
Each widget is a `.json` file with this exact structure:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"html": "...",
|
||||||
|
"css": "...",
|
||||||
|
"js": "...",
|
||||||
|
"inheritCockpitStyles": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### CSS Rules — Hard Constraints (Do Not Break)
|
||||||
|
|
||||||
|
These were discovered through testing. Violating any of them crashes or corrupts
|
||||||
|
the Cockpit renderer:
|
||||||
|
|
||||||
|
| Rule | Reason |
|
||||||
|
|---|---|
|
||||||
|
| No `body {}` or `:root {}` blocks | Bleed into Cockpit's own styles — causes renderer crash |
|
||||||
|
| No `@keyframes` animations | Cause renderer instability |
|
||||||
|
| No unicode characters in JS strings | Use plain ASCII only (-- not —, ... not …) |
|
||||||
|
| No `position: fixed` elements | Use `display: block/none` toggling instead |
|
||||||
|
| No `async/await` | Use `.then()/.catch()` for all fetch calls |
|
||||||
|
| No global tag selectors (e.g. `div {}`) | Use ID and class selectors only, scoped to widget root |
|
||||||
|
|
||||||
|
### What DIY Widgets Can Do
|
||||||
|
|
||||||
|
| Capability | Mechanism |
|
||||||
|
|---|---|
|
||||||
|
| Read all variable metadata | `window.cockpit.getAllDataLakeVariablesInfo()` — returns array of {id, name, type, description} ONLY, not values |
|
||||||
|
| **Read a single live value** | **`window.cockpit.getDataLakeVariableData(variableId)` — CONFIRMED working in v1.18.2. Returns live numeric/string value. Use this in all widgets.** |
|
||||||
|
| Read single variable metadata | `window.cockpit.getDataLakeVariableInfo(variableId)` |
|
||||||
|
| Set a variable | `window.cockpit.setDataLakeVariableData(variableId, value)` — available (not tested) |
|
||||||
|
| Call external REST endpoints | `fetch()` with `.then()/.catch()` |
|
||||||
|
| Open a new browser tab | `window.open('URL', '_blank')` |
|
||||||
|
|
||||||
|
**NOT available in DIY widget scope (confirmed absent by probe):**
|
||||||
|
- `getDataLakeValue()` — undefined in v1.18.2
|
||||||
|
- `listenToDatalakeVariable()` — undefined (postMessage iframe External API only)
|
||||||
|
|
||||||
|
**Full window.cockpit surface (17 keys, confirmed v1.18.2):**
|
||||||
|
`dataLakeVariableData [object]`, `getDataLakeVariableData [function]`, `listenDataLakeVariable [function]`, `unlistenDataLakeVariable [function]`, `getAllDataLakeVariablesInfo [function]`, `getDataLakeVariableInfo [function]`, `setDataLakeVariableData [function]`, `createDataLakeVariable [function]`, `updateDataLakeVariableInfo [function]`, `deleteDataLakeVariable [function]`, `getDataLakeVariableLastUpdateTimestamp [function]`, `availableCockpitActions [object]`, `registerNewAction [function]`, `deleteAction [function]`, `registerActionCallback [function]`, `unregisterActionCallback [function]`, `executeActionCallback [function]`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Data Injection Architecture — CONFIRMED FACTS
|
||||||
|
|
||||||
|
### Production Path (Confirmed) — cockpit_bridge WebSocket
|
||||||
|
|
||||||
|
```
|
||||||
|
cockpit_bridge (ROS2 node on RPi5, rov_mission package, port 9001)
|
||||||
|
WebSocket server :9001
|
||||||
|
<-- Cockpit connects as client (Settings -> Generic WebSocket Connections)
|
||||||
|
--> Sends 'variable-name=value' text messages at 2Hz
|
||||||
|
--> Data lake key: external/{variable-name}
|
||||||
|
--> DIY widget reads: getDataLakeVariableData('external/{variable-name}')
|
||||||
|
```
|
||||||
|
|
||||||
|
**Message format:** Plain text, one variable per message:
|
||||||
|
```
|
||||||
|
variable-name=value
|
||||||
|
```
|
||||||
|
|
||||||
|
**Data lake key format:** `external/{variable-name}`
|
||||||
|
|
||||||
|
> CRITICAL: Cockpit normalises variable names when creating data lake IDs.
|
||||||
|
> Underscores become hyphens in the data lake ID.
|
||||||
|
> ALWAYS USE HYPHENS in WebSocket variable names.
|
||||||
|
|
||||||
|
**Cockpit one-time configuration:**
|
||||||
|
```
|
||||||
|
Cockpit -> Menu -> Settings -> Generic WebSocket Connections
|
||||||
|
Enter: ws://192.168.1.101:9001
|
||||||
|
Click: ADD CONNECTION
|
||||||
|
```
|
||||||
|
|
||||||
|
This configuration persists between Cockpit restarts.
|
||||||
|
|
||||||
|
### Failed Injection Approaches (Do Not Retry)
|
||||||
|
|
||||||
|
| Approach | Why it failed |
|
||||||
|
|---|---|
|
||||||
|
| HTTP POST to mavlink2rest /mavlink | Stored in REST only — not broadcast to Cockpit |
|
||||||
|
| UDP to MAVLink ports from external scripts | Does not route to mavlink2rest WebSocket |
|
||||||
|
| NAMED_VALUE with sysid=1, compid=1 | ArduSub detects heartbeat conflict — do not impersonate |
|
||||||
|
|
||||||
|
### Data Lake Variables (8 total, published by cockpit_bridge)
|
||||||
|
|
||||||
|
| Variable | Source topic | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| `external/rov-failsafe` | `/rov/failsafe` | 0=GREEN, 1=AMBER, 2=RED; -1=no data |
|
||||||
|
| `external/rov-depth` | `/rov/depth` | Metres (3 dp) |
|
||||||
|
| `external/rov-voltage` | `/mavros/battery` | Battery volts (2 dp) |
|
||||||
|
| `external/rov-heading` | `/mavros/mavros/compass_hdg` | Compass degrees (1 dp) |
|
||||||
|
| `external/rov-ms` | `/rov/mission/status` | 0=IDLE…4=ABORTED; -1=no data |
|
||||||
|
| `external/rov-mp` | `/rov/mission/status` | Progress 0-100% |
|
||||||
|
| `external/rov-recording` | `/rov/recording/active` | 1=recording, 0=not recording |
|
||||||
|
| `external/rov-altitude` | `/ping2/altitude` | Ping2 metres; -1.0=NO SONAR |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Widget Palette — Overview
|
||||||
|
|
||||||
|
Argonaut 3 uses Cockpit as the operator hub. **No more than 8 widgets are in scope.**
|
||||||
|
Cockpit is NOT a container for everything — only the specific operator-facing information
|
||||||
|
and controls needed for field operations.
|
||||||
|
|
||||||
|
| # | Widget | Purpose | Status |
|
||||||
|
|---|---|---|---|
|
||||||
|
| W0 | Data Lake Inspector | Dev diagnostic — shows all 706 data lake variables live | **Confirmed working** |
|
||||||
|
| W1 | System Health Indicator | DP-style GREEN/AMBER/RED traffic light showing mission capability | **Visual confirmed** |
|
||||||
|
| W2 | Mission Status | Mission state, progress, current task | **Visual confirmed** |
|
||||||
|
| W3 | Return To Safe Button | Operator-initiated vehicle return-to-safe (DIR-5) | **UI confirmed; backend LIVE; label update pending** |
|
||||||
|
| W4 | Mission Setup Button | Opens mission configuration page in new tab | **Visual confirmed** |
|
||||||
|
| W5 | Return Budget | Battery % and dynamic return budget with headroom | **Visual confirmed** |
|
||||||
|
| W6 | Altitude | Ping2 downward sonar altitude with colour-banded bar | **NO SONAR confirmed** |
|
||||||
|
| W7 | Backup Due Notice | Operator-aware backup reminder with delay control | **Not built** |
|
||||||
|
| W8 | Mode Indicator | Read-only active mode profile name, colour-coded by base mode | **Validated live (render + data)** |
|
||||||
|
|
||||||
|
> **Widget testing status (corrected 9 Jul 2026).** There is a hard distinction between a widget
|
||||||
|
> that **renders** and a widget that **works**. "Renders" means it draws in Cockpit. "Works" means
|
||||||
|
> it has been validated end-to-end with live data. Do not conflate them.
|
||||||
|
>
|
||||||
|
> - **Validated with live data:** W0, W1, **W8**.
|
||||||
|
> - **Render-confirmed only, never validated with data:** W2, W3, W4, W5, W6.
|
||||||
|
> - **Not built:** W7.
|
||||||
|
>
|
||||||
|
> W6 shows a correct NO SONAR state, which is its *no-data* state — that is not data validation.
|
||||||
|
> Full testing requires a live mission, FastAPI backend, and/or Ping2 hardware.
|
||||||
|
|
||||||
|
> **All widgets read the RPi5 autonomy stack, not the vehicle.** For vehicle state (FC present,
|
||||||
|
> armed/disarmed, board running) the operator currently must use BlueOS/Cockpit-native surfaces.
|
||||||
|
> This is a defect, not a design — see W1 below and DIR-12. In AUV mode there is no operator and
|
||||||
|
> no second tool, so the gate must become self-sufficient.
|
||||||
|
|
||||||
|
> **Widget consolidation:** Grant may consolidate W0–W7 into a single unified widget
|
||||||
|
> as development progresses and the full operational picture is clearer. Not yet designed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Widget Specs
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### W0 — Data Lake Inspector
|
||||||
|
**File:** `w0_data_lake_inspector.json`
|
||||||
|
**Status:** **Confirmed working** — 706/706 vars live.
|
||||||
|
**Purpose:** Dev diagnostic tool. Renders a scrollable table of all Cockpit data lake
|
||||||
|
variables with live values. Used to confirm data is reaching the data lake from
|
||||||
|
cockpit_bridge, MAVROS, and other sources.
|
||||||
|
**Data source:** `window.cockpit.getAllDataLakeVariablesInfo()` for enumeration,
|
||||||
|
`getDataLakeVariableData(id)` for values.
|
||||||
|
**Notes:** Production use is optional — this is a dev/debug tool, not an operational widget.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### W1 — System Health Indicator
|
||||||
|
**File:** `w1_system_health_indicator.json`
|
||||||
|
**Status:** **Validated with live data** (GREEN/RED observed). ⚠ **But the assessment behind it is
|
||||||
|
incomplete and unsafe — see below.**
|
||||||
|
**Purpose:** Primary mission-capability indicator. DP-style traffic light showing
|
||||||
|
whether the vehicle is ready to conduct a mission (GREEN), degraded but proceeding
|
||||||
|
(AMBER), or in a critical state (RED).
|
||||||
|
**Data lake variable:** `external/rov-failsafe` (0=GREEN, 1=AMBER, 2=RED; -1=no data)
|
||||||
|
|
||||||
|
**Mode-dependent role (`external/rov-health-role`):**
|
||||||
|
- **ROV (`advisory`, 0)** — display only; the pilot decides.
|
||||||
|
- **AUV (`gate`, 1)** — **pre-dive GO/NO-GO gate.** RED blocks mission start. Last human-visible
|
||||||
|
decision point before the vehicle goes dark.
|
||||||
|
|
||||||
|
**Behaviour:**
|
||||||
|
- GREEN: "Systems nominal" — all parameters within limits
|
||||||
|
- AMBER: Named degraded parameter — mission continues with reduced confidence
|
||||||
|
- RED: Critical failure — return-to-safe active
|
||||||
|
- Initial / no data: grey / loading state
|
||||||
|
|
||||||
|
> ⚠ **OPEN, SAFETY-CRITICAL (9 Jul 2026).** On 9 July, with the Pixhawk unreachable and BlueOS
|
||||||
|
> reporting "No board running", **W1 displayed GREEN / "Systems nominal"**.
|
||||||
|
>
|
||||||
|
> **Cause:** `failsafe_monitor` does not gate on flight-controller connectivity. It assesses the
|
||||||
|
> autonomy stack's own view of health. With no FC data arriving it has no *bad* data, so it reports
|
||||||
|
> nominal. Absence of data is being treated as good data.
|
||||||
|
>
|
||||||
|
> **Consequence:** in AUV mode (`health_role: gate`) W1 would pass a pre-dive gate with a dead
|
||||||
|
> flight controller — on a vehicle that will have no operator once submerged.
|
||||||
|
>
|
||||||
|
> **Required (not built):**
|
||||||
|
> 1. `failsafe_monitor` MUST subscribe to `/mavros/state` and assess FC connection + heartbeat age,
|
||||||
|
> driving AMBER/RED on comms loss. (This was in the node's *original* design intent —
|
||||||
|
> `comms_timeout_sec`, `comms_lost` — but is not effective in the implemented node.)
|
||||||
|
> 2. A monitor with no input MUST NOT report GREEN. "No data" and "good data" must be distinct
|
||||||
|
> states, distinctly displayed.
|
||||||
|
> 3. W1's status line MUST carry cause text — `"Vehicle Disconnected"`, `"Heartbeat Lost"`,
|
||||||
|
> `"No Board Running"`, `"Systems nominal"` — not a bare colour. The status line was previously
|
||||||
|
> treated as a debug affordance; it is promoted to an operational element.
|
||||||
|
> 4. Consider a pre-dive FD-headroom check on BlueOS `ardupilot_manager` (handover §15a) as part of
|
||||||
|
> the same gate — same question, different failure mode.
|
||||||
|
>
|
||||||
|
> See **DIR-7** (health MUSTs) and **DIR-12** (vehicle health ≠ autonomy health).
|
||||||
|
|
||||||
|
**Test required:** Confirm GREEN/AMBER/RED transitions by triggering failsafe monitor
|
||||||
|
state changes with live data. **Additionally: confirm RED on FC disconnect** once (1) above is built.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### W2 — Mission Status
|
||||||
|
**File:** `w2_mission_status.json`
|
||||||
|
**Status:** **Visual confirmed** — IDLE state and progress bar display correctly. Untested with live mission.
|
||||||
|
**Purpose:** Show current mission state (IDLE/RUNNING/PAUSED/COMPLETE/ABORTED) and
|
||||||
|
progress as a percentage progress bar.
|
||||||
|
**Data lake variables:**
|
||||||
|
- `external/rov-ms` (0=IDLE, 1=RUNNING, 2=PAUSED, 3=COMPLETE, 4=ABORTED; -1=no data)
|
||||||
|
- `external/rov-mp` (0-100 percent)
|
||||||
|
**Test required:** Start a mission with waypoints, observe state transitions and progress bar advancing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### W3 — Return To Safe Button
|
||||||
|
**File:** `w3_abort_button.json`
|
||||||
|
**Status:** **UI confirmed** — confirm dialog, 10s auto-cancel countdown, clean network-error
|
||||||
|
handling. Label in widget code still reads "ABORT MISSION" — **update to "RETURN TO SAFE" pending.**
|
||||||
|
**Purpose:** Operator-initiated vehicle return-to-safe action (DIR-5). This is a
|
||||||
|
**vehicle-layer action** — it moves the vehicle. It is NOT the mission start/stop button.
|
||||||
|
**Button label:** "RETURN TO SAFE" (decided July 2026 per DIR-5)
|
||||||
|
- Not "ABORT MISSION" — that implies emergency hard-stop
|
||||||
|
- Not "STOP MISSION" — that is a mission-layer action (DIR-4), not vehicle layer
|
||||||
|
- "RETURN TO SAFE" correctly states what the vehicle does: follow breadcrumb path
|
||||||
|
back to the closest designated safe place
|
||||||
|
**Mechanism:** Confirm dialog (10s auto-dismiss) → `fetch()` POST to FastAPI `/abort`
|
||||||
|
**Config:** `FASTAPI_HOST` constant in JS must be set to `http://192.168.1.101:8081`
|
||||||
|
**Dependency:** FastAPI backend at RPi5:8081 — **LIVE** (commit 66a18ef). `/abort` verified
|
||||||
|
end-to-end server-side: POST → Bool `/rov/mission/abort` → failsafe_monitor FSM
|
||||||
|
NORMAL→HOLD_AND_RECOVER.
|
||||||
|
**Remaining:** update widget code label ("ABORT MISSION" → "RETURN TO SAFE") and set
|
||||||
|
`FASTAPI_HOST`, then test the full button→backend path from Cockpit.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### W4 — Mission Setup Button
|
||||||
|
**File:** `w4_mission_setup_button.json`
|
||||||
|
**Status:** **Visual confirmed** — cyan button renders, "Configure before diving" status shown.
|
||||||
|
Opens 404 until setup page exists.
|
||||||
|
**Purpose:** Open mission setup page in a new browser tab without leaving Cockpit.
|
||||||
|
**Behaviour:**
|
||||||
|
- IDLE: Full brightness, "Configure before diving" label — button enabled
|
||||||
|
- RUNNING/PAUSED: Dimmed — button disabled (mission already in progress)
|
||||||
|
**Config:** `SETUP_URL` constant in JS must be set to the FastAPI setup page URL
|
||||||
|
**Dependency:** FastAPI backend serving the setup page — not yet built.
|
||||||
|
**Test required:** Open setup page successfully with live FastAPI backend.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### W5 — Return Budget
|
||||||
|
**File:** `w5_battery_return_budget.json`
|
||||||
|
**Status:** **Visual confirmed** — battery % shows pending. Return budget pending
|
||||||
|
until `external/rov-return-budget` is published.
|
||||||
|
**Purpose:** Show battery percentage alongside dynamic return budget and headroom warning.
|
||||||
|
**Data lake variables:**
|
||||||
|
- `external/rov-voltage` — battery voltage from cockpit_bridge
|
||||||
|
- `external/rov-return-budget` — return budget % (not yet published by cockpit_bridge)
|
||||||
|
**Headroom thresholds:**
|
||||||
|
- Headroom < 15%: AMBER warning
|
||||||
|
- Headroom < 5%: RED critical
|
||||||
|
**Dependency:** FastAPI backend for return budget calculation; cockpit_bridge must be
|
||||||
|
updated to publish `external/rov-return-budget`.
|
||||||
|
**Test required:** Full test with live voltage and return budget data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### W6 — Altitude
|
||||||
|
**File:** `w6_altitude.json`
|
||||||
|
**Status:** **Built and deployed** (July 2026). **NO SONAR** state confirmed — correct
|
||||||
|
baseline with no Ping2 connected. Full test hardware-gated on Ping2 sonar purchase.
|
||||||
|
**Purpose:** Display downward Ping2 sonar altitude above hull or seafloor with
|
||||||
|
colour-coded status and vertical bar gauge.
|
||||||
|
**Data lake variable:** `external/rov-altitude` (metres float; -1.0 = NO SONAR sentinel)
|
||||||
|
**Source topic:** `/ping2/altitude` (sensor_msgs/Range) — published by Ping2 driver node
|
||||||
|
(hardware-gated; driver not yet built)
|
||||||
|
**Colour thresholds:**
|
||||||
|
- GREEN (GOOD): > 1.5m
|
||||||
|
- AMBER (CAUTION): 0.5–1.5m
|
||||||
|
- RED (CRITICAL): < 0.5m
|
||||||
|
- GREY (NO SONAR): sentinel value -1.0 (no Ping2 connected or no echo)
|
||||||
|
**Bar scale:** Linear, 0m (bottom) to 5m (top). Values above 5m fill bar to 100%.
|
||||||
|
**No hardware default:** Shows "NO SONAR" when altitude value is ≤ -1.0 (sentinel).
|
||||||
|
The sentinel is published by `cockpit_bridge` when no `/ping2/altitude` messages have
|
||||||
|
been received — meaning no Ping2 driver is running.
|
||||||
|
**Notes:**
|
||||||
|
- `sensor_msgs/Range` field `range_max` (100m for Ping2) is treated as "no return"
|
||||||
|
and also maps to the -1.0 sentinel in cockpit_bridge.
|
||||||
|
- Widget layout uses a vertical bar to the left of the numeric readout. The bar background
|
||||||
|
shows faint colour zone tints at the threshold boundaries even when reading is live.
|
||||||
|
**Pending:** Ping2 ROS2 driver node (wraps ping-python library). Interface defined:
|
||||||
|
topic `/ping2/altitude`, type `sensor_msgs/Range`, field `range` in metres.
|
||||||
|
Write driver when Ping2 hardware arrives — cockpit_bridge and widget are ready.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### W7 — Backup Due Notice (ops-facing)
|
||||||
|
**File:** `w7_backup_due_notice.json` — **NOT YET BUILT**
|
||||||
|
**Status:** Scoped, not built. Depends on RPi5:8081 FastAPI backend (shared with W3/W4).
|
||||||
|
**Purpose:** Surface to the operator that a device backup is due, and let the operator
|
||||||
|
delay it, rather than allowing an unattended backup to start during operations.
|
||||||
|
See DIR-11 for intent.
|
||||||
|
|
||||||
|
**Why this exists:** The automated Pi backup system (built 30 Jun 2026) runs each Pi's
|
||||||
|
self-backup via root cron — RPi4 at 00:00, RPi5 at 00:30 — unattended, which is correct
|
||||||
|
for dev. In the field this is the wrong default: an SD image is a multi-GB `dd` stream
|
||||||
|
that must NOT begin mid-dive. The ops model inverts the dev model — the operator is made
|
||||||
|
aware a backup is due and chooses when it runs, with the option to delay.
|
||||||
|
|
||||||
|
**Behaviour:**
|
||||||
|
- Hidden/dormant when no backup is due.
|
||||||
|
- When a backup is due, show a small unobtrusive notice: which device(s), how overdue.
|
||||||
|
- Offer delay options (e.g. delay 1h / 4h / until end of session). Delay is operator-chosen, never automatic.
|
||||||
|
- "Run now" option only enabled when vehicle is not in a mission (ties to `external/rov-ms`).
|
||||||
|
|
||||||
|
**Data sources (planned):**
|
||||||
|
- `FastAPI /backup/status` — backup-due state and overdue age per device
|
||||||
|
- `external/rov-ms` — mission state (to gate "run now")
|
||||||
|
|
||||||
|
**Delay mechanism (planned):** operator selection POSTs to `FastAPI /backup/delay`
|
||||||
|
|
||||||
|
**Dependency:** FastAPI backend on RPi5:8081 — not yet built.
|
||||||
|
**Open / TBC with Grant:** exact delay options; whether "run now" is exposed in field;
|
||||||
|
whether notice appears for all operators or pilot-only.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### W8 — Mode Indicator
|
||||||
|
**File:** `w8_mode_indicator.json`
|
||||||
|
**Status:** **Validated live** — render + data confirmed 9 Jul 2026 (commit `87000e6`).
|
||||||
|
**Purpose:** Read-only indicator of the **active operating-mode profile**. Answers one question:
|
||||||
|
"what mode am I in?" Nothing else.
|
||||||
|
**Data lake variables:**
|
||||||
|
- `external/rov-mode` (string) — active profile name: `ROV`, `AUV`, or a saved hybrid name
|
||||||
|
- `external/rov-mode-base` (number) — 0 = ROV, 1 = AUV; anything else treated as hybrid
|
||||||
|
|
||||||
|
**Behaviour:**
|
||||||
|
- Displays the profile name large and prominent, under a small `MODE` header
|
||||||
|
- Colour-coded by base mode: **cyan `#38bdf8`** = ROV, **green `#00e5a0`** = AUV,
|
||||||
|
**amber `#f59e0b`** = hybrid / unrecognised base
|
||||||
|
- No-data state: `--` in grey `#484f58` with a `NO DATA` / `NO API` status line
|
||||||
|
- 2 Hz poll, matching `cockpit_bridge` broadcast rate
|
||||||
|
|
||||||
|
**Explicitly NOT in scope:**
|
||||||
|
- **Read-only.** W8 never writes to the data lake and never calls `rov_api`. **Mode switching lives
|
||||||
|
in the setup wizard** (`POST /mode`), not here. An indicator indicates.
|
||||||
|
- **Does not show ArduSub arm state or vehicle mode.** Cockpit's native UI already surfaces
|
||||||
|
ARMED/DISARMED and flight mode; duplicating it here would add clutter, not information.
|
||||||
|
|
||||||
|
> **Note (DIR-12):** W8 reads a resolved profile that originates from `mode_profiles.yaml` on the
|
||||||
|
> RPi5. It reflects **configuration**, not vehicle state — it would display `AUV` with the Pixhawk
|
||||||
|
> disconnected. This is correct for what W8 is, but it is another reason W1 must carry the vehicle
|
||||||
|
> health picture.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Widget File Locations
|
||||||
|
|
||||||
|
**Gitea repo:** `rov-autonomy/widgets/`
|
||||||
|
**Laptop local:** `C:\Users\grant\Desktop\Autonomous ROV System\rov-autonomy\widgets\`
|
||||||
|
|
||||||
|
> **Widget JSON → Gitea:** W6 widget JSON (`w6_altitude.json`) and any updated W0–W5
|
||||||
|
> files need to be re-imported from the laptop and pushed to Gitea. Do NOT overwrite
|
||||||
|
> from chat history — the laptop files are the source of truth.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Production Data Path — Confirmed
|
||||||
|
|
||||||
|
The production data path is **cockpit_bridge WebSocket** (confirmed working, commit 12b5161
|
||||||
|
onwards). The NAMED_VALUE bridge node previously considered is not required — the WebSocket
|
||||||
|
path has no 10-char name limit, simpler implementation, and is already deployed.
|
||||||
|
|
||||||
|
**NAMED_VALUE is not needed for production.** Do not build a NAMED_VALUE bridge node.
|
||||||
|
|
||||||
|
The one case where native ArduSub NAMED_VALUE messages matter is data that ArduSub
|
||||||
|
itself publishes (e.g. attitude data) — these flow automatically through mavlink2rest to
|
||||||
|
Cockpit's data lake without any bridge. Custom ROS2 data goes exclusively through
|
||||||
|
cockpit_bridge.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. FastAPI Backend (`rov_api`)
|
||||||
|
|
||||||
|
Runs as `argonaut-api.service` on RPi5:8081 (manual-start in dev). **Core deployed and
|
||||||
|
verified end-to-end (commit 66a18ef).** Architecture: uvicorn/FastAPI in the main thread,
|
||||||
|
embedded rclpy node (`RovApiNode`) in a daemon background thread. CORS enabled for Cockpit.
|
||||||
|
|
||||||
|
### Endpoints
|
||||||
|
|
||||||
|
| Endpoint | Method | Action | Status |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `/health` | GET | Returns mission state, recording active, failsafe state | DONE — verified |
|
||||||
|
| `/abort` | POST | W3 RETURN TO SAFE — publishes Bool to `/rov/mission/abort` (vehicle-layer, DIR-5) | DONE — verified end-to-end |
|
||||||
|
| `/mission/start` | POST | MissionCommand START (recording no-go gate enforced by mission_executor) | DONE — verified |
|
||||||
|
| `/mission/stop` | POST | MissionCommand ABORT | DONE — verified |
|
||||||
|
| `/mission/upload` | POST | Accepts YAML file, saves to RPi5 | DEFERRED — Stage 2b |
|
||||||
|
| `/return_budget` | GET | Returns current calculated return budget % (feeds W5) | DEFERRED — Stage 2b |
|
||||||
|
| `/backup/status` | GET | Per-device backup-due state and overdue age (feeds W7) | DEFERRED — Stage 2b (Option A) |
|
||||||
|
| `/backup/delay` | POST | Records operator-chosen backup delay window (feeds W7) | DEFERRED — Stage 2b (Option A) |
|
||||||
|
|
||||||
|
**W3 abort path detail:** `/abort` publishes `Bool(true)` to `/rov/mission/abort`. The
|
||||||
|
failsafe_monitor latches this as `flag_manual_abort` → FSM transitions NORMAL → HOLD_AND_RECOVER.
|
||||||
|
This is the DIR-5 vehicle-layer return-to-safe, NOT the mission ABORT service. Verified live.
|
||||||
|
|
||||||
|
**W3 widget wiring (pending):** update `w3_abort_button.json` label to "RETURN TO SAFE" and
|
||||||
|
set the `FASTAPI_HOST` constant to `http://192.168.1.101:8081`.
|
||||||
|
|
||||||
|
**Stage 2b design notes:**
|
||||||
|
- `/return_budget`: return budget calculation approach under discussion ("shortest path to
|
||||||
|
safety" concept). Without a DVL, position is unreliable — approach TBD with Grant.
|
||||||
|
- `/backup/*`: Option A chosen — server exposes read-only JSON backup status, RPi5 FastAPI
|
||||||
|
polls outbound (most secure: no credentials on the field-exposed RPi5, read-only, outbound only).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Development Sequence
|
||||||
|
|
||||||
|
| Order | Task | Status |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | Confirm Cockpit native app required | DONE |
|
||||||
|
| 2 | Confirm JSON import format | DONE |
|
||||||
|
| 3 | Establish CSS rules for DIY widgets | DONE |
|
||||||
|
| 4 | W0 Data Lake Inspector | DONE — confirmed working (706 vars live) |
|
||||||
|
| 5 | W1 System Health Indicator | DONE — visual confirmed; full test pending |
|
||||||
|
| 6 | W2 Mission Status | DONE — visual confirmed; full test pending |
|
||||||
|
| 7 | W3 Return To Safe Button | DONE (UI); backend LIVE; label + FASTAPI_HOST update pending |
|
||||||
|
| 8 | W4 Mission Setup Button | DONE — visual confirmed; needs setup page (Stage 2b/Phase 6) |
|
||||||
|
| 9 | W5 Return Budget | DONE — visual confirmed; needs /return_budget (Stage 2b) |
|
||||||
|
| 10 | Commit W0–W5 to Gitea | DONE — `rov-autonomy/widgets/` |
|
||||||
|
| 11 | cockpit_bridge node (WebSocket production path) | DONE — 8 variables, commits 12b5161, 5d75a9d, 0640353 |
|
||||||
|
| 12 | Probe widget (API discovery tool) | DONE and DELETED — dev tool only, not part of W0–W7 |
|
||||||
|
| 13 | W6 Altitude Widget | DONE — `w6_altitude.json`, committed (commit 785ec56) |
|
||||||
|
| 14 | FastAPI backend core (RPi5:8081) | DONE — commit 66a18ef, verified end-to-end |
|
||||||
|
| 15 | W3 label update to "RETURN TO SAFE" + FASTAPI_HOST | PENDING — widget code update required |
|
||||||
|
| 16 | W3 backend `/abort` POST confirmed end-to-end | DONE — FSM NORMAL→HOLD_AND_RECOVER verified |
|
||||||
|
| 17 | W5 `/return_budget` + `rov-return-budget` publishing | DEFERRED — Stage 2b (approach TBD) |
|
||||||
|
| 18 | W7 Backup Due Notice + FastAPI `/backup/*` | DEFERRED — Stage 2b (Option A) |
|
||||||
|
| 19 | W6 full end-to-end test | HARDWARE-GATED — requires Ping2 sonar (Phase 3 purchase) |
|
||||||
|
| 20 | W2 full test with live mission | READY — backend available; needs loaded plan + `POST /mission/start` |
|
||||||
|
| 21 | Mission Setup View (full page, W4 target) | Phase 6 / Stage 2b |
|
||||||
|
| 22 | Viewer Page | Phase 6 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*End of document — Argonaut 3 UI Design v1.9 — July 2026*
|
||||||
|
*Next revision triggered by: FastAPI backend built, W3 label updated, W7 built, Ping2 purchased.*
|
||||||
Loading…
Reference in New Issue
Block a user