Initialization & Resets
A VIO estimator is only as trustworthy as its ability to start well and to recover well. voxl-open-vins-server initializes through a structure-from-motion (SfM) pipeline that runs entirely on-board with deterministic, real-time-friendly numerics — no Ceres, no dynamic thread pools — and recovers through a two-tier reset system designed so that a healthy filter is never torn down for a transient hiccup.
Table of contents
- Lifecycle
- Getting started: gravity and jerk gating
- Static initialization
- Dynamic initialization — Ceres-free SfM
- Resets
- Related configuration
Lifecycle
Getting started: gravity and jerk gating
Before any visual initialization, the server’s IMU front-end establishes two facts:
- Gravity direction — a plausible gravity sample orients the initial frame. By default the server accepts any attitude (the estimator’s SfM initializer is attitude-agnostic); platforms that want a level-start gate can opt in with
imu_init_max_gravity_angle_deg. - Motion state — a jerk detector watching accelerometer/gyroscope windows distinguishes “sitting on the bench” from “picked up / taking off”, which selects between the static and dynamic initializers and arms zero-velocity updates appropriately.
Static initialization
If the platform is stationary, the filter initializes directly: gravity from the averaged accelerometer, zero velocity, and bias priors from the quiet window. A deliberately tight tilt gate applies here — a pitched platform held in hand must fall through to dynamic initialization rather than receive a confidently wrong level-start seed.
Dynamic initialization — Ceres-free SfM
If the platform is moving (or tilted), the estimator solves a small structure-from-motion problem over a short window (~2 s) of feature tracks and preintegrated IMU segments: recover attitude (including gravity), velocity, biases, and feature depths, jointly, from scratch — at any attitude, mid-motion.
The refinement is a full maximum-likelihood solve, executed by a purpose-built solver — ov_init::zbft_sfm, authored by Joao Leonardo Silva Cotta, PhD — that replaces Ceres on the flight target:
- Deterministic and RT-friendly — fixed memory, bounded iterations, lock-free bounded parallelism; identical inputs produce identical outputs, run after run.
- Analytic Jacobians everywhere — closed-form derivatives for the IMU preintegration and reprojection factors (finite differences exist only as test oracles).
- Well-conditioned gravity handling — gravity is optimized on the unit sphere (S²) with a weak direction prior and a post-solve sanity gate, which is what makes reliable any-attitude initialization possible without flip ambiguities.
- Fast — full dynamic initialization completes in ~17–20 ms end-to-end on a QRB5165 (the legacy Ceres path took ~65 ms), including aggressive-motion cases with over 170° of rotation across the window.
Warm-start injection. The initializer does not just hand over a single state: the window’s poses and their joint covariance are injected as filter clones, so the filter performs useful updates immediately instead of cold-starting a fresh 2-second window after init.
Resets
Two tiers, one contract: the filter must come back honestly — no stale confidence, no leftover evidence from the previous life.
Soft reset (warm)
Triggered by RESET_VIO_SOFT on the control pipe or autonomously by health monitoring. The EKF state and covariance are rebuilt while everything expensive survives:
- feature tracks and the IMU history buffer — re-initialization is warm, not a cold restart;
- calibration values — per-camera time offsets and readout estimates carry over;
- live bias estimates — the running gyro/accel biases (with age-inflated uncertainty) are handed to the dynamic initializer as priors, dramatically conditioning the re-solve. The priors are gated: implausible magnitudes or uncertainties are rejected, and a divergence-suspected reset inflates them further before use.
What is deliberately purged: per-update bookkeeping keyed to the old timeline (the published feature list, quality smoothing, zero-velocity latches) — sensor time is continuous across a reset, so anything time-keyed would otherwise present pre-reset evidence as current.
Hard reset (cold)
Triggered by RESET_VIO_HARD or by estimator-poisoning error codes. The entire estimator object is destroyed and recreated: queued camera frames are released back to their owners exactly once, error codes are cleared (including those raised by the teardown itself — housekeeping is not a health event), and the system returns to the gravity/jerk gate.
Auto-reset policy
The health monitor (30 Hz) applies a strict severity split (full code table on the Pipe Interface page):
Estimator-health codes (corrupt covariance, sustained low quality, feature starvation, velocity-uncertainty breaches) recommend a reset — the filter itself is suspect. Transport warnings (dropped frame, dropped IMU sample, timestamp glitch) never do: the asynchronous ingest drops frames by design for ordering and staleness, and a single dropped frame once caused a mid-flight reset loop. Warning codes self-clear after 10 quiet seconds.
Additional guards prevent reset loops: quality checks arm only at the initialized rising edge (a re-initializing filter gets its full grace window), quality is capped during initialization so a freshly rebuilt covariance cannot report false confidence, and a reset that begins while a publish is in flight forces that last packet to report INITIALIZING.
Related configuration
Server-side knobs (/etc/modalai/voxl-open-vins-server.conf — see the main page): en_auto_reset, auto_reset_max_velocity, auto_reset_max_v_cov_timeout_s, auto_reset_min_features, auto_reset_min_feature_timeout_s, ok_state_grace_timeout_s, and the quality-hysteresis thresholds.
Estimator-side knobs (estimator_config.yaml, prefix init_): window length and feature budget (init_window_time, init_max_features), dynamic-init MLE controls (init_dyn_mle_max_iter, init_dyn_mle_max_threads, LM damping schedule), gravity gates (init_gravity_max_angle, init_static_gravity_max_angle, init_dyn_grav_prior_sigma, init_dyn_grav_gate_deg), warm start (init_warmstart_inject), and the reset bias priors (init_dyn_reset_prior_use and its acceptance gates).
The shipped platform configurations already encode sensible values for all of these; the knobs exist for bring-up and research. If you are tuning, change one group at a time and validate with voxl-benchmark-vio replays before flying.