SuperAPI AI Directory

matlab-fuse-inertial-sensors

General & other

View repository

Updated 10-16 00:21

About this skill

When to Use

Select, implement, or tune an inertial sensor fusion filter in MATLAB Navigation Toolbox.

When NOT to Use

  • Vision-only or LiDAR-only SLAM (no inertial sensor): use nav SLAM functions instead
  • Sensor simulation or data generation only: no filter needed
  • Simulink model-based sensor fusion

Choose Your Filter

Step 1 — Configured or Flexible?

Configured filters cover these sensor combinations exactly:

  • Accel + Mag only (no gyro)
  • Accel + Gyro (no mag, no GPS)
  • Accel + Gyro + Mag
  • Accel + Gyro + Mag + Altimeter
  • Accel + Gyro + GPS (ground vehicle, no mag)
  • Accel + Gyro + GPS + monocular visual odometry (ground vehicle)
  • Accel + Gyro + Mag + GPS (synchronous rates)
  • Accel + Gyro + Mag + GPS (mixed rates or dropped samples)
Is your sensor set one of the above?
        │ No ─────────────────────────────────────────────────────► Step 2B (Flexible)
        │ Yes
        ▼
Need a custom motion model (constant-velocity, bicycle, etc.)?
        │ Yes ────────────────────────────────────────────────────► Step 2B (Flexible)
        │ No
        ▼
Need RTS smoothing, StateCovariance output, or optimizer-based tune()?
        │ Yes ────────────────────────────────────────────────────► Step 2B (Flexible)
        │ No
        ▼
Batch-only processing (no real-time predict/fuse loop needed)?
        │ Yes ────────────────────────────────────────────────────► Step 2B (Flexible)
        │ No
        ▼
        Step 2A (Configured)

Step 2A — Pick a configured filter

What output do you need?
│
├─ Orientation only (no position)
│   │
│   ├─ Accel + Mag only (no gyro)
│   │   └──► ecompass   [function, not object; single-shot; useful for filter init]
│   │
│   ├─ Accel + Gyro (no mag)
│   │   └──► imufilter   [heading drifts over time without mag]
│   │
│   ├─ Accel + Gyro + Mag
│   │   ├──► ahrsfilter   [default; KF-based, statistically tunable]
│   │   └── lowest compute cost needed?
│   │       └──► complementaryFilter   [gain-based only; no KF, no tune()]
│   │
│   └─ Accel + Gyro + Mag + Altimeter
│       └──► ahrs10filter   [adds altitude + vertical velocity output]
│
└─ Full pose (orientation + position + velocity)
    │
    ├─ No motion constraints (aerial, or ground vehicle without side-slip constraint)
    │   ├─ Accel + Gyro + Mag + GPS, all sensors sync
    │   │   └──► insfilterMARG
    │   └─ Accel + Gyro + Mag + GPS, mixed rates or dropped samples
    │       └──► insfilterAsync
    │
    └─ Ground vehicle with zero side-slip constraint
        ├─ Accel + Gyro + GPS
        │   └──► insfilterNonholonomic
        └─ Accel + Gyro + GPS + monocular visual odometry
            └──► insfilterErrorState

Step 2B — Pick a flexible filter

Need real-time fusion, StateCovariance output, or optimizer-based tune()?
        │ Yes ──► insEKF   [production use; full KF; real-time and batch APIs]
        │ No
        ▼
Batch-only processing, no covariance needed, quick setup (R2026a+)?
        └──► insCF   [complementary filter; gain-based; not for production]

Data Preparation

Sample Rate

SampleRate (Group A filters) and IMUSampleRate (Group B filters) must match the actual data rate — a mismatch directly scales the noise model and corrupts all filter outputs. insfilterAsync, insEKF, and insCF are timestamp-driven and have no rate property.

Convert timestamps to seconds first, then infer the rate:

tSec = rawTimestamp / 1e6;   % µs → s; divide by 1e3 for ms, or skip if already seconds
fs = 1 / median(diff(tSec));

If timestamps are unusable (all identical, or non-finite result):

if numel(unique(tSec)) < 2 || ~isfinite(fs)
    fs = 100;   % use the stated or datasheet rate
end

Time Gaps

All stateful filters accumulate error across a recording gap (a large jump in timestamps). Split the data at each gap and reset the filter at the start of each segment.

gapThreshold = 5 / fs;                   % 5× the expected sample interval
gapIdx = find(diff(tSec) > gapThreshold);
segments = [1; gapIdx+1];                % start index of each segment

Group A System object filters (imufilter, ahrsfilter, complementaryFilter): call release(filt) between segments — resets state while preserving all tuned noise properties.

Group B and other filters: re-construct the filter object at the start of each segment.


Implementation

Consult the reference file for the filter you chose:

Filter group Reference
Attitude filters references/attitude-filters.md
Navigation filters references/navigation-filters.md
insEKF references/insekf.md
insCF references/inscf.md

Tuning

Two tune signatures exist — mixing them up is a common error:

  • Signature A (imufilter, ahrsfilter) — modifies filter in-place: tune(filt, sensorData, groundTruth)
  • Signature B (ahrs10filter, insfilterMARG, insfilterAsync, insfilterNonholonomic, insfilterErrorState, insEKF) — returns noise struct: tunedNoise = tune(filt, tunernoise(filt), sensorData, groundTruth)

Not tunable via tune:

  • ecompass, complementaryFilter — no tune method; adjust properties manually
  • insCF — use gainparts(filt, sensorName, value) instead

insEKF only: tunerconfig requires a filter instance — tunerconfig(filt), not tunerconfig('insEKF')

tunerconfig

Property Default Description
MaxIterations 20 Stop after this many iterations
ObjectiveLimit 0.1 Stop when cost drops below this value
Display "iter" "iter" prints progress each iteration; "none" suppresses output
Cost "RMS" "RMS" minimizes RMS error; "Custom" uses CustomCostFcn
CustomCostFcn [] Function handle; active only when Cost = "Custom"

tunernoise Field Names by Filter

Signature B filters require tunernoise(filt) before calling tune. Signature A filters (imufilter, ahrsfilter) do not use tunernoise — tune modifies their noise properties directly in-place.

Filter Fields returned by tunernoise
ahrs10filter MagnetometerNoise, AltimeterNoise
insfilterMARG MagnetometerNoise, GPSPositionNoise, GPSVelocityNoise
insfilterAsync AccelerometerNoise, GyroscopeNoise, MagnetometerNoise, GPSPositionNoise, GPSVelocityNoise
insfilterNonholonomic GPSPositionNoise, GPSVelocityNoise
insfilterErrorState MVOOrientationNoise, MVOPositionNoise, GPSPositionNoise, GPSVelocityNoise
insEKF Named by filt.SensorNames + Noise suffix (e.g. AccelerometerNoise, GPSNoise)

Fields use the ...Noise suffix — omitting it is a common mistake.

Visualizing Results

tunerPlotPose is an OutputFcn callback — pass it to tunerconfig to plot pose estimates live during each tuning iteration. It is not a standalone post-hoc plotting function; calling it directly with filter and data arguments will error ("Too many input arguments").

config = tunerconfig(filt);
config.OutputFcn = @tunerPlotPose;
tunedNoise = tune(filt, tunernoise(filt), sensorData, groundTruth, config);

For post-tuning visualization, extract the filter state and plot against your ground truth manually. Configured filters (ahrs10filter, insfilterMARG, insfilterAsync, insfilterNonholonomic, insfilterErrorState) have a pose() method — use it. insEKF and insCF have no pose() method — use stateparts(filt, 'Orientation'), stateparts(filt, 'Position'), etc. instead. For batch workflows, plot from the estimateStates output timetable.

For advanced patterns (controlling TunableParameters, custom cost functions, OutputFcn for logging/early stopping), see references/insekf.md and references/navigation-filters.md.


Conventions

  • Reference frame: default is "NED". To use ENU:

    • Configured filter objects (imufilter, ahrsfilter, complementaryFilter, ahrs10filter, insfilterMARG, insfilterAsync, insfilterNonholonomic, insfilterErrorState): filt.ReferenceFrame = "ENU"
    • ecompass (a function, not an object): the orientationFormat positional arg is required before any name-value pairs — ecompass(a, m, 'quaternion', 'ReferenceFrame', 'ENU'). Omitting it errors: "Expected input number 3, FORMAT..."
    • insEKF/insCF: ReferenceFrame is read-only after construction; set via insOptions/insCFOptions at construction time
  • Orientation output: quaternion object by default.

  • GPS position input: LLA vector in degrees / degrees / meters.

  • Sample rate property:

    • imufilter, ahrsfilter, complementaryFilter: SampleRate
    • ahrs10filter, insfilterMARG, insfilterNonholonomic, insfilterErrorState: IMUSampleRate
    • insfilterAsync, insEKF, insCF: no sample rate property (sensors carry their own timestamps)
  • ReferenceLocation: must be set on insfilterMARG, insfilterAsync, insfilterNonholonomic, and insfilterErrorState before the fusion loop. For insEKF/insCF, set it on the insGPS/insCFGPS sensor object instead.

Skill Feedback

If this skill's guidance was wrong, incomplete, or didn't work, emit a plain-text feedback marker in your response, then continue with the task.

Format: NAV-SKILL-FEEDBACK [matlab-fuse-inertial-sensors] [category]: what failed; what you did instead Categories: wrong-command (incorrect/outdated), missing-guidance (omitted step), discovery-failure (suggested approach didn't work), platform-issue (environment-specific)

Copyright 2026 The MathWorks, Inc.