Skip to content Link Search Menu Expand Document
ModalAI DOCS
Store

Initialization & Resets

How VIO starts, how it recovers — structure-from-motion initialization without Ceres, any-attitude resets, and the auto-reset policy.

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

  1. Lifecycle
  2. Getting started: gravity and jerk gating
  3. Static initialization
  4. Dynamic initialization — Ceres-free SfM
  5. Choosing static or dynamic initialization
  6. Resets
    1. Soft reset (warm)
    2. Hard reset (cold)
    3. Auto-reset policy
  7. Troubleshooting: VIO does not initialize from rest
  8. Related configuration

Lifecycle

Initialization and reset 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.

Choosing static or dynamic initialization

The choice between static and dynamic initialization depends on what the application can tolerate.

If the application needs VIO from takeoff and is not robust to structure-from-motion resets (a possible jump discontinuity in the estimate), do not rely on dynamic initialization or on the warm (soft) reset. Initialize statically on the ground, confirm the estimator is in the OKAY state, and take off from there.

“Convergence” applies to dynamic initialization, which solves a nonlinear optimization; static initialization does not optimize anything. A static initialization can still be poor as soon as the platform is in the air if the zero-velocity updates were noisy or the IMU calibration and noise (sigma / random-walk) values are wrong. This is common on custom or modified airframes, and a full IMU intrinsics recalibration is the first thing to try. The dynamic initializer is switched with init_dyn_use in estimator_config.yaml.


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 knowledge — per-camera time offsets and readouts are re-pinned (value and linearization point) to the configured calibration; online refinements from the previous episode are deliberately discarded, so the new episode restarts from the trusted prior rather than from estimates a divergence may have poisoned;
  • 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.


Troubleshooting: VIO does not initialize from rest

Symptoms: the estimator stays in INIT while the vehicle sits on the ground, no features are tracked after an automatic takeoff, or position hold only starts working once the vehicle has been picked up and moved by hand. Work through the list in order.

  1. IMU calibration. Recalibrate the IMU from voxl-portal or with voxl-calibrate-imu (Calibrate IMU), then confirm the gravity magnitude reads close to 9.81 m/s^2:

    voxl-inspect-imu imu_apps
    

    A wrong gravity magnitude or bad bias values give a static initialization that looks fine on the bench and falls apart once airborne.

  2. Camera calibration for the current resolution. Open the intrinsics file in /data/modalai/ for each VIO camera and check that the resolution stored in it matches the resolution the camera streams now; a calibration captured at a different resolution is not valid. Recalibrate if the stored reprojection_error is near the limit (Calibrate Cameras, Verifying Camera Calibration).
  3. Lighting and texture at the takeoff spot. Look at the tracking camera image in voxl-portal from the vehicle’s resting position. Static initialization does not need image features, but the filter needs tracked features immediately after it; a plain floor directly below a down-facing camera, low light, or a lens cover give it nothing to track. Move the vehicle or add scene texture rather than tuning the estimator.
  4. Static or dynamic initialization. Decide which initializer the application relies on (previous section). Static initialization requires the vehicle to be stationary and inside the tilt gate; dynamic initialization requires motion with enough parallax over its ~2 s window, which a vehicle sitting still never provides. If VIO must be initialized before takeoff, wait for voxl-inspect-vins to report OKAY on the ground before arming.
  5. Vibration and airframe changes. Added payloads, lights, or prop guards in the camera view and extra vibration both degrade initialization. Check voxl-inspect-vibration and the camera view for intrusions.
  6. Record a log. Capture voxl-logger -b -v ov around the failing takeoff so the run can be replayed offline with voxl-benchmark-vio. Include the log when asking for help.

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.