Integrating OpenVINS with Ardupilot

I am working on integrating OpenVINS with an ArduPilot-based system using external vision as the primary navigation source.

Setup

  • Platform: ArduPilot (SITL / real vehicle)

  • VIO: OpenVINS publishing /ov_msckf/odomimu (Odometry)

  • Camera: mounted at ~45° tilt

  • ROS 2 environment

  • Previously used a custom MAVLink bridge to send:

    • VISION_POSITION_ESTIMATE

    • VISION_SPEED_ESTIMATE

What I’ve done

  • Converted OpenVINS output (Z-up frame) to NED and sent via MAVLink

  • Verified that VIO trajectory matches ground truth when GPS is enabled

  • Disabled GPS and configured EKF3 for external navigation:

    • EK3_SRC1_POSXY = 6

    • EK3_SRC1_POSZ = 6 (also tested with baro)

    • EK3_SRC1_VELXY = 6

    • EK3_SRC1_VELZ = 6

    • EK3_SRC1_YAW = 6

  • Set EKF origin at startup

  • Disabled compass (to avoid yaw conflicts)

  • Tuned EKF gates and noise parameters for VIO

Observed behavior

  • Vehicle takes off correctly and reaches target altitude

  • After reaching altitude, it starts rotating unexpectedly

  • Shortly after, EKF reports variance errors

  • System becomes unstable

Key observation

  • With GPS enabled → system is stable and trajectory matches ground truth

  • With GPS disabled (VIO only) → instability appears after takeoff

  • VIO itself appears accurate

Questions

  1. Is sending VISION_POSITION_ESTIMATE + VISION_SPEED_ESTIMATE directly sufficient, or is MAVROS /odometry/in recommended?

  2. Could this issue be caused by:

    • Frame mismatch (ENU vs NED, FLU vs FRD)?

    • Incorrect covariance values?

    • Improper yaw alignment or initialization?

    • Camera tilt (45°) not properly accounted for?

  3. Is there a recommended workflow for initializing EKF origin and yaw when using VIO-only (no GPS)?

  4. Are there specific EKF3 parameters that are critical for stable VIO-only operation beyond the standard EK3_SRC1_* settings?

Any guidance or debugging suggestions would be greatly appreciated.