A field guide + glossary for ROS 2 data in underwater vehicles, and a worked read of a real bag

I’ve been working through how AUV/ROV mission data is actually structured in ROS 2, and ended up writing two things that might be useful to others coming at marine robotics from the data side rather than the controls side.

A field guide to the data patterns — five structural patterns in how untethered vehicles record sonar, navigation and telemetry, with each term explained three ways (technical, plain English, what it’s used for): ROS 2 Data Patterns in Underwater Robotics — An Animated Field Guide

A 34-term glossary, each entry with a diagram: Glossary — Underwater Robotics & ROS 2 Mission Data

A worked example on real data — I profiled the Girona Underwater Caves bag (661,327 messages, 12 topics, 32.6 min) with rosbags in pure Python, no ROS install, and plotted depth, track, DVL beams and IMU attitude: Reading a Real AUV Mission Recording — 661,327 Messages from a Cave Survey

Four things in that bag surprised me, and I’d be interested to know whether they’re typical:

  1. /odometry publishes pose but twist.twist.linear.x is zero for all 21,843 messages — the field is there and never populated. Speed has to come from the DVL.
  2. The Linkquest DVL reports altitude = -2.0 as a no-bottom-lock sentinel (6 of 5,564 pings). Averaging that column naively gives a quietly wrong answer.
  3. Per-beam dataGood flags show only 86.2% of pings have all four beams good, even though individual beams are 94.9–98.0%. Roughly one ping in seven is degraded.
  4. Quaternion sign flips in the IMU orientation look like the vehicle inverting; anything that interpolates or differentiates across them produces fictional rotation.

Also: the dual-topic pattern is real and it bit me. Every vendor sensor in that bag is published twice, once as a custom type and once as a sensor_msgs twin. My first IMU plot came back completely blank because I read the vendor type, whose fields are gx/ax/roll rather than the standard nested vectors.

Corrections welcome — particularly on the DVL conventions, where I could only find convergence across packages rather than an actual standard.

Data set citation is required and is on the article page: Mallios, Vidal, Campos, Carreras, “Underwater caves sonar data set,” IJRR 2017, doi:10.1177/0278364917732838.

Yeah it’s not really surprising that nobody can really agree on what to use when there’s no clear standard messages that would be a perfect fit. I don’t think those four things are typical, every dataset and vehicle always has its setup quirks. I’ve recently been involved in setting up the quadrotor MARS-LVIG dataset for VO and it very liberally mixes NED, ENU and FLU frames. It’s just something one has to deal with on per dataset basis.

I think part of the problem with message duplication is that gathering data is expensive and storage is relatively cheap, so nobody wants to throw away the raw part that might come in useful in the future. So for example, a MBES is conceptually an open and shut case for PC2, but what the sonar generates under the hood is a FLS-like image with maxima detection giving you the actual points. E.g.:

In this case if you want to do the usual hydrographic survey, you just need the points. For doing other kinds of more niche research or debugging the sensor, etc. you might also need the uncertainty for each beam, maybe cases where it failed to find a return, which is where the manufacturer specific custom messages tend to come in.

Conceptually a DVL produces two sources of data, the velocity (TwistStamped) or positional delta (Vector3Stamped), and the 3-6 beams which are less clearly defined. We’ve internally used anything from PC2 for the endpoints to Range lists, but if one is to stick to common_interfaces it’s not really possible to publish only one type.

Personally I’m still missing a message that’s essentially a tf registered CompressedImage for FLS/sidescan type imaging data. I’ve mainly been misusing the OccupancyGrid (realistically it’s the only option) for it since it also works in RViz with raw mode without any plugins and can be used to render sonar data overlaid over satelite imagery, lidar, stereo points, and other data. It’s not natively compressed though so sending it over a network isn’t ideal, and 8 bits per pixel throws away quite a bit of raw data (that’s already done at the Image stage I suppose). I’d really like to know what others are using in this regard, if they’re registering their imagery at all or just straight up only looking at Image data or SonarImageData with an adhoc renderer?

Hopefully one of the current options (perhaps marine_acoustic_msgs, or some other), becomes commonly accepted to cover most cases and gets actually added to common_interfaces, which would finally make it official and drive any sort of serious level of adoption.

P.S. Regarding the obviously completely autogenerated field guide, I suggest taking a peek at this topic.