Pipe Interface
voxl-open-vins-server communicates exclusively through ModalAI’s MPA pipe architecture: sensors stream in through client pipes, estimates stream out through server pipes, and a control pipe accepts runtime commands. This page is the complete communications reference — what each pipe carries, when it publishes, and how to interpret states, quality, and error codes.
Table of contents
- The pipe map
- Input pipes
- Output pipes
- Control pipe
- States
- Quality
- Error codes
- Frame conventions
- Client tools
The pipe map
Publishing happens on the VIO thread, immediately after every estimator update — there is no separate publish timer. The output rate therefore tracks the camera update rate (e.g. ~60 Hz on a rig whose reference camera runs 60 fps), with IMU-rate pose propagation available to consumers that extrapolate between updates.
Input pipes
| Pipe | Source | Contents |
|---|---|---|
imu_apps_body | voxl-imu-server | Calibrated IMU samples (~1 kHz), already rotated into the body frame. Used when imu_body_frame_mode is enabled (default) |
imu_apps | voxl-imu-server | Raw-orientation IMU pipe, used when imu_body_frame_mode is disabled |
| camera pipes | voxl-camera-server | One image pipe per camera enabled in vio_cams.conf (e.g. tracking_front, tracking_down, hires_misp_grey). Cameras may be synchronized or fully unsynchronized — see Asynchronous & Mixed-Shutter Cameras |
All cameras must reference the same IMU in
vio_cams.conf. The IMU clock is the common timebase against which every camera’s time offset is estimated.
Camera frames are pushed into the estimator’s lock-free per-camera ingest directly from the camera pipe callbacks — nothing blocks a camera thread, and a stalled camera cannot stall the pipeline. The IMU callback drives estimation: each IMU batch advances the filter and releases any camera frames whose global time order is now decided.
Output pipes
/run/mpa/ov — primary VIO output
Publishes vio_data_t (defined in modal_pipe_interfaces.h), the packet consumed by voxl-px4 for position control:
| Field group | Meaning |
|---|---|
timestamp_ns | Timestamp of the state estimate (monotonic clock, nanoseconds) |
T_imu_wrt_vio, R_imu_to_vio | Position and rotation of the IMU/body frame with respect to VIO local frame (FRD conventions) |
vel_imu_wrt_vio | Velocity in the VIO frame |
imu_angular_vel | Body-rate estimate (from quaternion differentiation of consecutive states) |
quality | Estimation quality score — see Quality below |
state | VIO_STATE_INITIALIZING, VIO_STATE_OK, or VIO_STATE_FAILED |
error_code | Bitmask of active error codes — see Error codes |
n_feature_points | Number of SLAM landmarks currently in the state vector |
/run/mpa/ov_extended — extended output
Publishes ext_vio_data_t: everything in vio_data_t plus pose/velocity covariance diagonals, gyro/accel bias estimates, camera extrinsics in use, and the list of currently tracked feature locations (consumed by voxl-portal for the VIO overlay). Only written when at least one client is connected, so it costs nothing in normal flight.
/run/mpa/ov_status — health and diagnostics
Publishes ov_status_t at the health-check cadence: hysteresis state, counters, and operational diagnostics for logging and fleet monitoring (voxl-log-vins consumes this alongside ov).
Control pipe
The server listens on /run/mpa/ov/control (also reachable through the ov_extended channel):
| Command | Effect |
|---|---|
RESET_VIO_SOFT | Warm reset. Rebuilds the filter state while preserving feature tracks, IMU history, calibration values, and the live gyro/accel bias estimates (carried into re-initialization as priors). Recovers in seconds, in motion, at any attitude |
RESET_VIO_HARD | Cold reset. Destroys and recreates the whole estimator; all queued frames are released safely and every error code is cleared. Re-initializes from scratch |
voxl-reset-vins # hard reset
voxl-reset-vins -s # soft reset
See Initialization & Resets for what each reset preserves and when the server triggers them autonomously.
States
| State | Meaning |
|---|---|
VIO_STATE_INITIALIZING | Waiting for motion/gravity gates, or actively initializing (static or dynamic). Also reported during a reset and re-initialization. Quality is capped low in this state so a freshly built covariance cannot masquerade as confidence |
VIO_STATE_OK | Estimator initialized and updating normally |
VIO_STATE_FAILED | Auto-reset conditions met — the estimate should not be trusted; a reset is in progress or imminent |
Quality
quality is a 0–100 score computed from the tracked-feature population: per-camera image coverage (a 5×5 occupancy grid), SLAM landmark uncertainty, and MSCKF track quality, pooled over a short window so that asynchronous per-camera updates score exactly like synchronized batch updates. The published value is smoothed with a short time constant; -1 means failed, 0 means resetting.
A hysteresis state machine (INITIAL → GOOD/BAD) sits on top of the raw score to prevent rapid quality flapping; its thresholds are configurable in voxl-open-vins-server.conf (see the main page). The full design and the exact formulas — the windowed feature union, per-camera grid scoring, and the published-value EMA — are documented on the async page: Estimation quality on asynchronous rigs and Appendix A.5.
Error codes
error_code is a bitmask; multiple codes can be active simultaneously. The health monitor (30 Hz) classifies codes into two severities:
Only estimator-health codes can trigger an automatic reset. Transport hiccups — a dropped frame or IMU sample — are expected events on a busy system (the asynchronous ingest disposes frames deliberately for ordering and staleness) and must never take down a healthy filter. Warning-class codes self-clear after 10 s without a new occurrence.
| Code | Class | Meaning |
|---|---|---|
ERROR_CODE_COVARIANCE | auto-reset | Covariance no longer positive definite — filter is corrupt |
ERROR_CODE_IMU_OOB | auto-reset | IMU measurement exceeded sensor range |
ERROR_CODE_IMU_BW | auto-reset | IMU bandwidth/rate too low to propagate safely |
ERROR_CODE_NOT_STATIONARY | auto-reset | Estimation quality below threshold for too long (bit name is historical) |
ERROR_CODE_NO_FEATURES | auto-reset | No usable features for an extended period |
ERROR_CODE_CONSTRAINT | auto-reset | Insufficient geometric constraints from current features |
ERROR_CODE_FEATURE_ADD | auto-reset | Failed to add new features to the map |
ERROR_CODE_VEL_INST_CERT / ERROR_CODE_VEL_WINDOW_CERT | auto-reset | Velocity estimate uncertainty exceeded bounds |
ERROR_CODE_DROPPED_CAM | warning | A camera frame was dropped (ring full, stale, or out of order) |
ERROR_CODE_DROPPED_IMU | warning | IMU samples dropped |
ERROR_CODE_BAD_TIMESTAMP | warning | A sensor timestamp regressed or jumped |
ERROR_CODE_LOW_FEATURES | warning | Not enough good features to (re)initialize yet |
ERROR_CODE_BAD_CAM_CAL | warning | Camera intrinsic calibration looks questionable |
ERROR_CODE_CAM_MISSING / ERROR_CODE_IMU_MISSING | reconnect | Sensor pipe not delivering — handled by reconnect logic, not by reset |
ERROR_CODE_CAM_BAD_RES / ERROR_CODE_CAM_BAD_FORMAT | config | Camera stream resolution/format unsupported — fix the camera configuration |
Frame conventions
Internally the estimator runs in the OpenVINS world frame (gravity-aligned, z-up). The publisher transforms every output into ModalAI’s FRD (front-right-down) local frame convention before writing to the pipes, using the body → imu extrinsic from /etc/modalai/extrinsics.conf when imu_body_frame_mode is enabled. Heading is re-zeroed at every (re)initialization: the local frame’s x-axis is the vehicle’s heading at init, which is what position controllers expect.
Client tools
| Tool | Purpose |
|---|---|
voxl-inspect-vins | Live terminal view of the ov stream (-v velocity, -b body frame, -z everything) |
voxl-log-vins | CSV logging of VIO output and features for offline analysis |
voxl-reset-vins | Sends RESET_VIO_HARD / RESET_VIO_SOFT (-s) on the control pipe |
voxl-benchmark-vio | Replay-based benchmarking harness — see voxl-benchmark-vio |