rov-autonomy/docs/mission_recording_lifecycle.md

28 KiB

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 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 Above this, missions use Hierarchical Mode Declarations: the Charlie example mission defines INACTIVE, LOITERING, RETURNING, and STATION-KEEPING, with RETURNING ⊂ ACTIVE, Mit 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 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 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 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 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 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 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 Critically, the pre-arm check fails arming if logging fails ("PreArm: Logging failed" when the SD card isn't writable) Loghat — 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 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 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 an optional 1 Hz CSV is written alongside, QGroundControl 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 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 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 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 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 --regex/--exclude-regex, and --exclude patterns; auto-discovery picks up topics appearing later. ROS 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 — 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 MBARI 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 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 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).
  1. 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.