Susi Server

Vehicular Fusion (Odometry-IMU)

Node ID: vehicularFusion · Role: Filter · Realtime config: no

Description

Vehicle dead-reckoning filter fusing wheel odometry (CAN bus speed) with IMU. Uses bicycle or differential drive model. Outputs 2D vehicle pose with heading, velocity, and UTM coordinates.

Algorithm notes

How it works

A planar dead-reckoning filter for wheeled vehicles. Wheel speed and the gyroscope turn rate propagate position and heading; GNSS position fixes (or, alternatively, an optical tracking system) correct the accumulated drift. One fused pose is published per wheel-speed sample. Because the motion comes from the wheels and the gyro, the filter coasts cleanly through GNSS outages. While driving it also learns the gyroscope yaw bias and a wheel-speed scale factor (tire wear and pressure).

What you must configure

How it starts

The filter waits for an RTK Fixed GNSS position (turn on Use GNSS Without RTK Fix to accept lower quality) and a heading. The heading comes from a dual-antenna receiver (Initialize from GNSS Orientation) or, with a single antenna, from the direction of travel once you drive straight faster than Init Velocity Threshold.

Tuning

Troubleshooting

Inputs / Outputs

Config aliases

OdometryImuFilter, odometryImuFilter, vehicularFusion

Required feature

vehicular_fusion

Properties

Each row shows the setting’s label in the node’s Properties panel and, in code, its key in the node’s settings object in the config file. A setting omitted from the config file uses the default shown here.

Core Tuning

PropertyTypeDefaultDescription
Velocity Error fuser.velErrornumber0.278Control noise std-dev on the wheel-speed input in m/s. Higher values trust the odometry speed less; lower values smooth speed but lag real accelerations.
Angular Velocity Error fuser.omegaErrornumber0.5Control noise std-dev on the turn-rate input in rad/s. Controls how quickly the heading may change between updates. Lower values produce smoother heading but slower response to real turns.
Measurement Error fuser.measurementErrornumber0.1Measurement noise std-dev for the GNSS / optical position update, in meters. Lower values trust the position fixes more; higher values let odometry dead-reckoning carry more weight.
Smoothing fuser.smoothFitbooleantrueSmooth the output by interpolating between the lagged and current filter states across measurement epochs. Removes the visible position step at each fix at the cost of a small effective latency.

Outlier Handling

PropertyTypeDefaultDescription
Use Position Outlier De-weighting fuser.usePositionOutlierDeweightingbooleantrueWhen a position update exceeds the innovation gate, inflate its measurement noise and apply it softly instead of applying it at full weight.
Innovation Gate (sigma) fuser.innovationGatenumber5.0Chi-square threshold for enabled outlier handling: a fix whose squared Mahalanobis distance exceeds dof + gate x sqrt(2 x dof) is treated as an outlier. 0 disables position outlier de-weighting.

Wheel Odometry & Turn Rate

PropertyTypeDefaultDescription
Use IMU Turn Rate fuser.useImuTurnRatebooleantrueTake the vehicle yaw rate from the IMU gyroscope (about the IMU Turn Rate Axis) instead of deriving it from differential wheel speeds. Recommended when a gyro is available, as it is far less sensitive to wheel slip and track-width error.
Wheel Turn-Rate Fallback fuser.useWheelAngularFallbackbooleanfalseFall back to the wheel-derived turn rate when the IMU stream is lost. Off by default: with no measured wheel angular rate a dead IMU leaves no heading reference, so output is stopped rather than dead-reckoned on a stale turn rate. Enable only when a trustworthy wheel angular rate is available.
IMU Timeout (s) fuser.imuTimeoutLimitnumber0.5Time in seconds without an IMU sample before the IMU stream is declared lost. On loss the filter stops output - or switches to the wheel turn rate if Wheel Turn-Rate Fallback is on and a valid wheel rate is present. The IMU normally streams at 100 Hz; raise this only to tolerate longer dropouts.
IMU Turn Rate Axis fuser.imuTurnRateAxisvector3{"x":0,"y":0,"z":1}Unit vector selecting which IMU gyroscope axis (in the IMU frame) supplies the vehicle yaw rate. Default (0,0,1) uses the IMU Z axis; set to match the axis pointing up through the vehicle.
Wheel Omega Scale fuser.wheelOmegaScalenumber1.0Multiplier applied to the wheel-derived angular velocity to correct for track-width or wheel-radius calibration error. Only relevant when Use IMU Turn Rate is off. 1.0 leaves the wheel turn rate unscaled.
Detect Wheel Slip fuser.detectSlipbooleantrueInflate the control noise on ticks where the IMU and wheel-derived turn rates disagree (wheel slip or wrong track width).
Estimate Wheel Scale fuser.estimateWheelScalebooleantrueEstimate a slow wheel-speed scale factor (tire wear/pressure) from the ratio of RTK-fixed travelled distance to integrated odometry distance.
Estimate Gyro Bias fuser.estimateGyroBiasbooleantrueEstimate the gyro yaw-rate bias as a Kalman filter state and subtract it from the IMU turn rate - the dominant yaw-drift source during GNSS outages. Verified multi-second GNSS-yaw windows and stopped gyro samples measure it directly.
Gyro Bias Random Walk fuser.gyroBiasRandomWalknumber0.0005Random-walk process noise density for the odometry-stage gyro yaw-bias state, in rad/s per sqrt(s). Lets the in-run bias track slow thermal changes.
Gyro Bias ZUPT Error fuser.gyroBiasZuptErrornumber0.0035Measurement noise for stopped zero-angular-rate gyro-bias updates, in rad/s.

Orientation & Heading

PropertyTypeDefaultDescription
Initialize from GNSS Orientation fuser.initializeFromGnssOrientationbooleanfalseSeed the filter’s initial heading from the GNSS dual-antenna orientation, avoiding the need to drive forward to resolve heading at startup. Only used when Use GNSS Without RTK Fix is off; when off without a valid reported orientation (single antenna), or when on, the heading is initialized from course over ground instead.
Use GNSS Yaw Measurement fuser.useGnssYawMeasurementbooleanfalseUse the GNSS dual-antenna heading as a 1D yaw measurement while moving, instead of relying on position cross-covariance alone. Also keeps the heading corrected through a position-only outage and feeds the moving gyro-bias estimator.
GNSS Yaw Error fuser.gnssYawErrornumber0.05GNSS heading measurement noise std-dev in radians.

Stopped Detection

PropertyTypeDefaultDescription
Retain State When Stopped fuser.retainStateWhenStoppedbooleantrueFreeze the filter state while the vehicle is detected as stationary, preventing position and heading drift from sensor noise when not moving.
Velocity Threshold fuser.velocityThresholdnumber0.01Vehicle speed in m/s below which the vehicle is considered stopped (with hysteresis at half this value). Used for stopped-state detection and optional state freezing.

Initialization & Convergence

PropertyTypeDefaultDescription
Init Velocity Threshold fuser.initVelocityThresholdnumber3.0Minimum speed in m/s for the course-over-ground heading initialization. Heading is locked in after several consecutive GNSS samples above this speed.
Init Velocity Tolerance fuser.initVelocityTolerancenumber0.4Maximum allowed difference in m/s between the GNSS-derived speed and the wheel speed for a sample to count toward the course-over-ground heading initialization.

RTK & GNSS Quality

PropertyTypeDefaultDescription
Use GNSS Without RTK Fix fuser.useGpsOnRtkFloatbooleanfalseAccept GNSS position fixes of any quality instead of only RTK Fixed (quality 4). When off, the filter stays uninitialized until the first RTK Fixed sample; position accuracy degrades to that of the raw fixes when on.
Weight GNSS by Fix Quality fuser.useGnssQualityWeightingbooleanfalseWeight GNSS position fixes by their NMEA fix quality instead of the all-or-nothing RTK gate. DGPS and RTK-float fixes are admitted as a loose drift leash (using the sigmas below) that bounds dead-reckoning drift during an RTK outage without being chased like an RTK Fixed fix. Single-point fixes stay rejected. Supersedes Use GNSS Without RTK Fix when on.
DGPS Position Error fuser.gnssDgpsErrornumber4.0Position measurement noise std-dev in meters for DGPS / SBAS fixes (quality 2). Only used when Weight GNSS by Fix Quality is on.
RTK Float Position Error fuser.gnssFloatErrornumber12.0Position measurement noise std-dev in meters for RTK Float fixes (quality 5). Only used when Weight GNSS by Fix Quality is on.
Use RTCM Age De-weighting fuser.useRtcmAgeDeweightingbooleantrueInflate the GNSS position and heading measurement noise with the receiver’s RTCM correction age, so an RTK-fixed fix on stale corrections is smoothly de-weighted toward dead reckoning. When off, RTK-fixed fixes are trusted regardless of correction age.
RTCM Age Sigma Per Second (m/s) fuser.rtcmAgeSigmaPerSecnumber0.05Per-second growth in meters of the GNSS position sigma when RTK FIXED with a non-zero RTCM correction age. Effective measurement error becomes measurementError + diffAge * this. Only used when Use RTCM Age De-weighting is on.

IMU Mounting & Frames

PropertyTypeDefaultDescription
IMU-to-Car Rotation fuser.imuToCarRotationquaternion{"w":1,"x":0,"y":0,"z":0}Quaternion (w,x,y,z) describing the rotation from the IMU sensor frame to the vehicle body frame. Accounts for the physical mounting orientation of the IMU.

Optical

PropertyTypeDefaultDescription
Use Optical Data fuser.useOpticalDatabooleanfalseIncorporate an attached optical pose stream as an additional position measurement, useful for indoor or GNSS-denied segments.
Optical Scaling Factor fuser.opticalScalingFactornumber10.0Scale factor applied to incoming optical position to convert it into meters / correct for tracking-volume scaling before fusion. Only used when Use Optical Data is enabled.
Align Optical to GNSS fuser.alignOpticalToGnssbooleantrueRotate the optical coordinate frame to match the GNSS/world frame before fusing, so optical and GNSS positions share a common reference. Only used when Use Optical Data is enabled.

Example node definition

A minimal entry in config.json looks like the block below. Paste it under the sinks key, keyed by an instance name of your choice. All settings are optional: anything not listed under settings uses the default from the Properties table above. See The configuration file for how the sections and endpoint wiring work.

{
  "sinks": {
    "vehicularFusion": {
      "dataEndpoint": "inproc://vehicularFusion_data",
      "inputEndpoints": [
        "inproc://gnss_data",
        "inproc://imu_data",
        "inproc://vehicleSpeed_data",
        "inproc://optical_data"
      ],
      "inputDataFilter": [
        "Gnss",
        "Imu",
        "VehicleSpeed",
        "Optical"
      ],
      "settings": {
        "fuser": {
          "imuToCarRotation": {
            "w": 1,
            "x": 0,
            "y": 0,
            "z": 0
          },
          "imuTurnRateAxis": {
            "x": 0,
            "y": 0,
            "z": 1
          }
        }
      }
    }
  }
}
Loading documentation…