Repository navigation
A field-model error the plant sees and the filter does not - #236
Open
patrickmckeen wants to merge 5 commits into
Open
patrickmckeen wants to merge 5 commits into
patrickmckeen wants to merge 5 commits into
Conversation
Owner
|
I would prefer different field models for the estimator and the environment rather than introducing a new class that is hyperspecific for this problem |
patrickmckeen
force-pushed
the
feat/sensor-bias-instability
branch
from
October 22, 2026 14:54
a82a3e9 to
e1bb83d
Compare
patrickmckeen
force-pushed
the
feat/magnetometer-field-model-error
branch
from
October 22, 2026 14:54
1100f6a to
930a134
Compare
… sample rate Noise gains an optional density, in reading units per root hertz (the angle random walk of a gyro, the noise density of a magnetometer data sheet). A reading averaged over the sensor's sample_time then has standard deviation sqrt(std_noise^2 + density^2 / sample_time), so the noise no longer silently changes when the simulation step changes. The sensor passes its sample time both when it draws and when the estimator asks for the measurement covariance, so both sides agree; the warm start uses the same standard deviation. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Every sensor takes a scale_factor (its reading is that factor times a perfect device's) and a misalignment (a rotation vector from the nominal sensor frame to the actual one). A single-axis sensor reads along its sensitive axis as mounted; a vector sensor reports in its frame as mounted; the quaternion star tracker reports the attitude of its own frame. The sensors' Jacobians follow the device as built. The estimator's clone of a satellite is reset to the nominal device, since these errors are exactly what the filter does not know. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Bias keeps three parts and their sum: the constant offset the device was built with, a random walk of rate std_bias that now starts at zero rather than at the offset, and a bias instability modelled as a first-order Gauss-Markov process (instability_std, instability_time), the slow wander an Allan-variance plot shows as its floor. The estimator's random-walk rate covers the instability with the usual 2 sigma^2 / tau term. A bias built the old way produces the same sequence as before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…the filter does not FieldModelError is a slowly varying error field in the inertial frame (one first-order Gauss-Markov process per axis, std in tesla, correlation time in seconds), shared by a spacecraft's magnetometers, standing in for the difference between the real geomagnetic field and the IGRF model that the simulation's truth and the estimator otherwise share exactly. A magnetometer given one reads the model field plus the error, its Jacobian follows, and the estimator's clone of the satellite drops it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…the torquers act on it too The satellite owns the FieldModelError (Satellite(field_error=...)): it is stepped once per plant step with the other error models, every magnetometer reads it, and the magnetorquers and magnetic disturbances act on the model field plus the error (a copy of the orbital state with the offset field is used inside act_torque and dist_torques, so the caller's state is untouched). The estimator's clone has none of it. Until now only the readings saw the error while the torque used the model field, which understated the effect of model error on magnetic control. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
patrickmckeen
force-pushed
the
feat/magnetometer-field-model-error
branch
2 times, most recently
from
October 26, 2026 20:08
930a134 to
cd2ab4d
Compare
patrickmckeen
force-pushed
the
feat/sensor-bias-instability
branch
2 times, most recently
from
October 27, 2026 17:18
2be7599 to
683cab8
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
The simulation's "true" magnetic field and the estimator's field model are the same IGRF evaluation, so a magnetometer in a simulation is only ever wrong by its own noise and bias, and a torquer always produces exactly the torque the model predicts. On a real spacecraft the model itself is wrong: IGRF leaves out the crustal and external fields, and the difference at low Earth orbit is typically 100 to 300 nT, changing slowly as the spacecraft moves. That error is in the environment, so it is the same for every magnetometer on board and the torquers act on it too; what is off is the filter's prediction of both. Any study of magnetometer-based attitude accuracy or magnetic control without it is optimistic.
What this adds
FieldModelError(std, correlation_time): an error field in the inertial frame, one independent first-order Gauss-Markov process per axis, with the given standard deviation in tesla and correlation time in seconds. It starts in its stationary distribution, decays towards zero over the correlation time and is topped up each step so its standard deviation stays where configured, for any step length. Reasonable values for low Earth orbit are a few hundred nanotesla and a few hundred seconds (the time to move a few hundred kilometres), and the docstring says so. This is a stand-in for a model error, not a model of it: it has the right size and the right slowness, nothing more.The satellite owns it:
Satellite(field_error=FieldModelError(...)). It is stepped once per plant step with the other error models (update_errors), and the whole plant sees the same realisation:act_torque,dist_torques, and so the dynamics) act on the model field plus the error. The plant's orbital state is replaced by a copy with the offset field inside those two methods, so the caller's orbital state is never touched.The estimator's clone of the satellite has no error field, on the satellite or its sensors, because the model error is by definition what the model does not have. A magnetometer can still be given a
FieldModelErrordirectly when used on its own.This stacks on #235.
Tests
testing/test_sensors/test_field_model_error.py, 6 tests: the error field is stationary with the configured standard deviation and lag-one correlation and its axes are independent (20 000 steps); three magnetometers sharing one instance read one consistent error at one epoch and it steps once when the epoch changes; the analytic Jacobian matches a central-difference Jacobian with the error and a misalignment in place; the estimator's clone has no error field; the validation; and, at satellite level, that the readings, the torquer torque, the magnetic disturbance torque and the dynamics all use the same true field while the estimator's clone uses the model field, with the caller's orbital state untouched and one realisation per step. The full suite passes.🤖 Generated with Claude Code