Skip to main content

Composable stochastic and parametric error models for synthetic IMU increments

Project description

IMU Error Model

Composable stochastic and parametric error models for synthetic IMU increments. The package receives truth orientation and truth velocity excluding gravity; it returns imperfect accelerometer delta_v and gyroscope delta_theta over each sample interval.

Install the initial release with:

python -m pip install imu-error-model

The canonical measurement frame is the body frame at the beginning of the interval. The orientation input is R_world_from_body, a proper 3x3 rotation matrix. The model does not calculate gravity, dynamics, reconstruction, or state estimation.

Extension contract

Alternate implementations satisfy ImuModelProtocol through structural typing. They implement reset() and:

measure(timestamp, velocity_without_gravity, orientation_world_from_body,
temperature_celsius=None) -> ImuOutput

ImuModelProtocol is also runtime-checkable with isinstance; that check only confirms that reset and measure attributes exist. Use Pyright or another static type checker to validate their signatures.

The first call establishes the interval baseline and returns zero increments. Subsequent calls require strictly increasing timestamps in seconds. Truth velocity is a 3-vector in m/s, orientation is a proper R_world_from_body rotation matrix, and temperature_celsius is an optional temperature in degrees Celsius (°C). None means temperature is unavailable and disables thermal corrections for that sample. The default thermal reference is 25 °C, exposed as DEFAULT_REFERENCE_TEMPERATURE_C. AxisConfig noise densities are continuous-time rate densities: accelerometer values use m/s²/sqrt(Hz), gyroscope values use rad/s/sqrt(Hz). If supplied, noise_covariance is the 3x3 covariance of the corresponding continuous-time rate noise and must be positive semidefinite.

ImuOutput.delta_v is a body-frame velocity increment in m/s, and ImuOutput.delta_theta is a body-frame rotation vector in radians. Both use the body axes at start_time; neither is expressed in world axes. To convert an increment to world axes, multiply it by the supplied R_world_from_body orientation at the interval start. Each output carries its sampled interval as start_time and end_time; dt is derived from those timestamps. The derived acceleration and angular_rate properties are also body-frame quantities.

JSON profiles can be loaded with load_profile() or created with save_profile(). The same load_profile() helper also loads JSONC, YAML, and YML profiles based on their extension, returning a validated ImuConfig in all cases. Use load_profile_document() when profile provenance metadata is needed. Test-only JSON profile fixtures are in tests/profiles/.

Hardware-oriented best-effort estimates are kept separately under examples/imu_profiles/hardware-estimates. See the profile examples guide for loading and interpreting those notional estimates. They are documentation/examples only and are not package defaults or runtime configuration. Every numeric value there is a notional, approximate estimate derived from public datasheets or other cited source material; none is authoritative. These profiles are not vendor specifications, certification results, or performance guarantees. The load_profile_document() adapter maps their profile documents into the current model while retaining metadata, sample period, source path, and structured source metadata. Each hardware profile also stores structured provenance in metadata.sources, with a public URL and the date on which that source was reviewed. load_profile_document() returns this metadata as typed ProfileMetadata and ProfileSource models. These dates are provenance review dates, not asserted publication dates. The links identify the evidence used to choose approximate parameters; they do not turn the profiles into vendor specifications or certified models.

The built-in model supports independent white noise, correlated 3-axis noise covariance, turn-on bias, random-walk/Gauss–Markov bias, a finite-band flicker-bias approximation, fixed run-level misalignment, scale factor, nonlinearity, thermal bias/noise coefficients, clipping, and quantization. Scale factor, turn-on bias standard deviation, in-run bias standard deviation, and quantization step may be scalar values broadcast to all three axes or explicit three-axis values. Use misalignment_covariance for anisotropic reset misalignment; misalignment_std remains the isotropic shortcut. More elaborate thermal behavior can be supplied by implementing ThermalModel. Thermal models bind their AxisConfig once when constructed and expose evaluate(temperature_celsius: float) -> ThermalState; the per-sample interface receives only the current temperature in °C. The built-in LinearThermalModel(config) follows this contract. At model construction, immutable configuration vectors and covariance matrices are compiled into cached read-only NumPy arrays for reuse on the sampling hot path; temperature-dependent states and timestep-dependent transitions remain dynamic.

Configuration loading accepts plain mappings, streams, and extension-selected files:

config = config_from_mapping({"accelerometer": {"white_noise_density": 0.1}})
config = load_profile_stream(stream, format="yaml")  # text or binary stream
config = load_profile("profile.jsonc")  # .json, .jsonc, .yaml, .yml

JSON is the default stream format; YAML and JSONC require the explicit format when a stream has no filename extension.

For estimator covariance work, characterize_imu_error_process() provides a pure, deterministic description without sampling or mutating an ImuModel:

noise = characterize_imu_error_process(
    imu_config,
    dt=imu_output.dt,
    process_age_at_interval_start_s=process_age_s,
    temperature_celsius=imu_output.temperature_celsius,
)

The result exposes continuous white-noise covariance, direct increment covariance, exact persistent-state transitions and process covariance, reset, start, and end priors, and conditional versus marginal increment covariance. It also exposes the increment–state-process cross covariance needed by an exact augmented filter. Use the direct covariance for fresh sensor noise, propagate the full persistent state when modeling all configured blocks, and preserve the returned cross covariance when injecting state process noise separately. The marginal covariance includes the persistent prior and is a correlated-in-time uncertainty envelope, not fresh independent noise for every sample. The six-dimensional ordering is [delta_v_x, delta_v_y, delta_v_z, delta_theta_x, delta_theta_y, delta_theta_z].

Development and CI

Install the development tools and run the same task runner used by GitHub:

python3 -m pip install -e ".[dev]"
python3 scripts/ci.py test
python3 scripts/ci.py coverage
python3 scripts/ci.py lint
python3 scripts/ci.py format
python3 scripts/ci.py typecheck
python3 scripts/ci.py build
python3 scripts/ci.py docs --build

The documentation task builds both docs/imu_error_model.tex and the visual companion docs/imu_profile_showcase.tex. To regenerate the showcase figures and then build both PDFs in one step, run:

python3 scripts/build_docs.py

The helper assumes the development dependencies and a local LaTeX installation (latexmk or pdflatex) are available. Outputs are written under docs/artifacts/.

scripts/ci.py all runs Ruff linting, Pyright type checking, Markdown checks, regression tests, coverage, and package distribution builds. scripts/ci.py format is the canonical auto-fix command: it runs Ruff's autofixes and formatter, then inserts the repository's required #### scope markers. Ruff has no native extension point for project-specific marker rules, so the marker fixer is intentionally chained after Ruff. It also normalizes Markdown trailing whitespace, final newlines, heading/list spacing, and blank lines without rewriting fenced code contents. scripts/ci.py markdown checks that Markdown links are relative and resolve, that nested Markdown files are linked, and that source-document references do not enter the public documentation. Markdown and source comments must never encode links or paths to gitignored source material; use committed, relative references only. The check rejects local source-material paths and provenance strings that could disclose excluded documents. The GitHub workflow calls this runner on Python 3.11 and 3.12 and uploads the wheel, source distribution, and generated documentation artifacts.

The Publish to PyPI workflow publishes a GitHub Release through PyPI Trusted Publishing. Configure the repository's pypi environment and trusted publisher on PyPI before publishing the first release; no long-lived PyPI token is stored in the repository.

To regenerate the standard Allan-deviation analysis set, install the development dependencies and run:

python3 -m pip install -e ".[dev]"
python3 scripts/ci.py allan

This regenerates artifacts/allan/ for all hardware profiles plus the two short-correlation test fixtures. Use --duration and --points to change the record length or plot density.

For polished storefront-oriented charts, run:

python3 scripts/ci.py showcase

This writes paired PNG/SVG assets under artifacts/showcase/:

  • hero-allan-ladder: representative navigation, tactical, and consumer Allan-deviation curves.

  • error-anatomy-allan: white noise, Gauss–Markov bias, finite-band flicker, and combined Allan signatures.

  • reconstruction-drift: long-term position and attitude drift from identical truth motion.

  • measurement-time-series: truth rates compared with sampled imperfect rates and their residual errors.

  • thermal-trends: thermal-only accelerometer and gyroscope error trends.

Use --allan-duration, --reconstruction-duration, and --temperature-points to control generation time and density. The assets are notional showcase renderings, not vendor performance claims.

To regenerate the complete analysis bundle in one step:

python3 scripts/ci.py analysis

This combines the standard Allan-deviation plots and all showcase plots. Normal pytest intentionally does not generate files or require Matplotlib; the explicit analysis task owns generated artifacts.

Analysis example

To compare dead-reckoning drift across all hardware-estimate profiles:

python3 examples/dead_reckoning.py --duration 10

This reconstructs position, velocity, and attitude from the model's delta_v and delta_theta outputs and writes a CSV summary. It is deliberately kept outside the core package; filters such as an MEKF belong in a separate estimation project.

For datasheet-oriented noise characterization, generate Allan-deviation data and charts across the same estimate profiles:

python3 -m pip install -e ".[dev]"
python3 scripts/ci.py allan

This writes artifacts/allan/allan_deviation.csv plus accelerometer and gyroscope PNG charts. The τ range can be controlled explicitly:

python3 examples/allan_variance.py --duration 1000 \
  --tau-min-s 0.01 --tau-max-s 250 --points 32

To visualize the finite-band flicker fixture as well:

python3 examples/allan_variance.py --duration 100 \
  --profile tests/profiles/test/flicker-band.json

The generated charts are analysis artifacts, not package runtime outputs. A record should be several times longer than the largest τ of interest; a 120-second run cannot establish the long-term behavior of profiles with 250–3600-second bias correlation times. For the first-order Gauss–Markov model, expect a short-time positive slope, a turnover near the correlation time, and eventual averaging-down at long τ. A true bias-instability plateau would require a different stochastic process. The model also supports a finite-band flicker-bias approximation through flicker_bias_std, flicker_min_correlation_time, flicker_max_correlation_time, and flicker_components. It is calibrated to the requested Allan-deviation level near the geometric-center τ of that band.

Allan-variance meaning of the stochastic terms

The stochastic parameters are rate-domain parameters. With sample period dt, white_noise_density = N produces independent rate samples with standard deviation N / sqrt(dt). Under the estimator used by the analysis scripts, its short-time Allan deviation is therefore approximately

sigma_A(tau) = N / sqrt(tau)

For a stationary Gauss–Markov bias with stationary standard deviation bias_std = sigma_b and correlation time T, the continuous-time reference curve below is the scalar per-axis case. A three-axis bias_std applies the same process independently with its corresponding per-axis variance.

sigma_A²(tau) = sigma_b² [
    2 T / tau
    - (T / tau)² (3 - 4 exp(-tau/T) + exp(-2 tau/T))
]

It rises with an approximately +1/2 log-log slope at short averaging times, turns over near T, and falls with an approximately -1/2 slope at long averaging times. If bias_correlation_time is omitted, bias_std instead specifies the random-walk driving scale; that process has the expected longer-term +1/2 Allan slope rather than a stationary knee.

The finite-band flicker model is a practical datasheet-matching approximation, not an infinite-band 1/f generator. It sums flicker_components independent Gauss–Markov processes whose correlation times are logarithmically spaced from flicker_min_correlation_time to flicker_max_correlation_time. The flicker_bias_std value is the target Allan deviation at the geometric-center averaging time

tau_center = sqrt(flicker_min_correlation_time
                  * flicker_max_correlation_time)

The result is a finite plateau-like region with roll-off outside the selected band. More components make the approximation smoother; they do not extend the physical validity range beyond the configured correlation-time bounds. Independent process contributions add in Allan variance, while finite records and random seeds introduce normal Monte Carlo variation around these reference curves. Use python3 scripts/ci.py analysis to regenerate the parity fixtures, Allan charts, and showcase plots together.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

imu_error_model-0.1.2.tar.gz (97.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

imu_error_model-0.1.2-py3-none-any.whl (27.4 kB view details)

Uploaded Python 3

File details

Details for the file imu_error_model-0.1.2.tar.gz.

File metadata

  • Download URL: imu_error_model-0.1.2.tar.gz
  • Upload date:
  • Size: 97.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for imu_error_model-0.1.2.tar.gz
Algorithm Hash digest
SHA256 f30effe3f69fe79f4ecc1b025c03917fc85cd2ca75e78dc247517d9056ed5bd8
MD5 038e6e839b814a68972d523a5782e3ba
BLAKE2b-256 b6440b5ed077dce3cedf1dd76c8e17b54d164abc0893a2b981cd4429a27176f5

See more details on using hashes here.

File details

Details for the file imu_error_model-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for imu_error_model-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 416c70e9324c8205013f1bde89f70d48129309b735243eba1728f7ba40208630
MD5 cfdf7d98e803d88739e61e986c1f5659
BLAKE2b-256 31a226dc89918faf70b5cb81b5713365a2e41c85e9585af2035b4e9afb11e8d1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page