flightopsDocumentation
Guides

Executable sensor input recipes

4 min readdocs/DEVICE-INTEGRATION-RECIPES.md (source repository)

The existing depth recipe is joined by non-actuating sensor_msgs/msg/LaserScan and sensor_msgs/msg/Imu observers in sensor_recipes (source repository). These publish observations only; they do not integrate motion, steer a robot, or establish physical calibration.

This page needs a source checkout: the recipes are in demo/sensor_recipes/, which is not in the install. An installed flightops-tui recipes list shows the recipes but cannot run their checks. Run the commands below from the repository root.

Offline use

The integrations bootstrap installs the small local recipe package without ROS. For an existing environment, install it explicitly:

sh
python -m pip install --no-deps --no-build-isolation ./demo/sensor_recipes
flightops-tui recipes list
flightops-tui recipes check lidar --output /tmp/lidar-recipe.json
flightops-tui recipes check imu --output /tmp/imu-recipe.json
flightops-results /tmp/lidar-recipe.json /tmp/imu-recipe.json -o /tmp/sensor-results.html

These execute six deterministic decoder cases per input. Results identify code and fixture hashes and the synthetic_decoder mode. Missing ROS is not required for these checks and is not presented as live transport acceptance.

Input contracts

InputChecked behavior
LaserScanFinite angle geometry, consistent ray count/end angle, range bounds, valid/invalid returns, optional intensity count, frame, source and receive freshness.
ImuFinite supplied measurements, normalized supplied quaternion, covariance shape/symmetry/positive-semidefinite checks, unavailable channels, frame, source and receive freshness.
DepthExisting encoding/row/endianness/invalid-pixel rules and near/clear/unavailable semantics remain unchanged.

A scan containing invalid returns is partial or unavailable; missing returns never mean free space. An IMU covariance of all zeros remains unknown. A leading -1 marks that channel unavailable and its measurement is not used. ROS covariance array scalar types are accepted and serialized into ordinary JSON numbers.

Units follow the selected message contract. Unannotated numeric values cannot establish physical units, axis alignment, gravity convention or calibration. See the official LaserScan and Imu definitions for their message conventions.

Installed ROS nodes

Inside a sourced ROS environment:

sh
colcon build --base-paths demo/sensor_recipes \
  --build-base /tmp/sensor-build --install-base /tmp/sensor-install
source /tmp/sensor-install/setup.bash
ros2 launch sensor_recipes sensor_observer.launch.py \
  kind:=lidar input_topic:=/scan output_topic:=/scan_observation \
  expected_frame:=laser input_mode:=simulation use_sim_time:=true

For IMU, choose kind:=imu and the corresponding input/frame. max_age_s defaults to 0.5 seconds. A steady-clock timer makes dropped input stale even when ROS time stops. target_frame optionally requires an exact-message-stamp TF lookup; availability of that transform is not calibration proof. The subscription requests the standard sensor-data QoS. Actual delivered observations are distinct from discovery; a topic merely appearing cannot pass a check.

input_mode is an operator declaration (synthetic, recorded, simulation, device or unspecified), retained as such. Selecting device is not device acceptance or authorization for actuation. Configuration parameters are read-only.

The installed-launch synthetic transport check exercises missing, valid, invalid, stale, future, wrong-frame, before-dropout and dropout observations, then verifies owned-process cleanup:

sh
python3 demo/sensor_recipes/live_check.py --kind lidar \
  --ros-domain-id 176 --output /tmp/lidar-live.json
python3 demo/sensor_recipes/live_check.py --kind imu \
  --ros-domain-id 176 --output /tmp/imu-live.json

Use an isolated development ROS domain/environment. The helper reuses Flightops process supervision, so source this checkout's installed Python components as well as the ROS recipe install. It does not overwrite existing results.

Recorded input example

The recipe can produce a small deterministic synthetic MCAP containing valid input, invalid input, recovery and dropout, then replay it into the installed observer:

sh
python3 -m sensor_recipes.recording create --kind lidar --output /tmp/lidar-input
python3 -m sensor_recipes.recording check /tmp/lidar-input \
  --ros-domain-id 176 --output /tmp/lidar-replay.json

Repeat with --kind imu and new paths. Creation needs the installed ROS MCAP writer. Checking verifies the selected recording hash, starts the installed node with simulation time, plays with /clock, observes the three expected transitions, and records process cleanup. The input MCAP is bounded to 2 MiB; normal examples are much smaller. Outputs retain recorded_input, synthetic recording origin and recording identity. This is fixed-input replay, not a closed-loop simulator trial.

Saved check records are accepted by flightops-results and can be attached to task experiments or explicitly included in a reproduction package.

These recipes observe synthetic or recorded input. Check clock and frame identities before using your own recording; a replay does not establish physical sensor calibration or hardware behavior.