Skip to main content

Config

Struct Config 

Source
pub struct Config {
Show 14 fields pub imu: ImuNoise, pub gates: Gates, pub timeouts: Timeouts, pub recovery: Recovery, pub correlation: Correlation, pub init: Initialization, pub accuracy: Accuracy, pub gravity: f32, pub max_predict_dt: Seconds, pub coast: Option<Coast>, pub hold: Option<Hold>, pub baro_offset_walk: f32, pub baro_reference_from_estimate: bool, pub yaw_estimator: bool,
}
Expand description

Everything the filter is tuned by.

Construct by updating the default:

use fusion_nav::{Config, Seconds, Timeouts};

let config = Config {
    timeouts: Timeouts {
        dead_reckoning_after: Seconds::from_secs(2.0),  // a multirotor in close quarters
    },
    ..Config::default()
};

Fields§

§imu: ImuNoise

IMU noise densities.

§gates: Gates

Innovation gate thresholds.

§timeouts: Timeouts

Fusion timeouts feeding Status.

§recovery: Recovery

Automatic recovery from gate lockout, per source.

§correlation: Correlation

How long each source’s measurement error persists, equation (24′).

§init: Initialization

Static initialization.

§accuracy: Accuracy

How good an estimate must be to count as valid.

§gravity: f32

Local gravity γ, m s⁻²: the g of equations (5)–(8), (11) and (22′).

Configured rather than read from the origin the filter places, because the origin arrives with the first GNSS fix, which can come after propagation has begun, and a propagation constant that changes mid-flight is the retuning differentiator 7 rules out (decision). The value for a site is Geodetic::normal_gravity at it, and the replay harness’s --derive prints it from a log’s origin. Bounded to Earth’s normal gravity, so a value in ft s⁻² or in units of g is refused.

§max_predict_dt: Seconds

Largest dt Eskf::predict will integrate one IMU sample over.

Beyond this the sample is not integrated, because the discretization of equations (9)–(22) is a first-order approximation over a short interval and one sample cannot describe a long one. The filter coasts across the gap instead, by Config::coast, or with that off refuses the step and leaves the state where it was.

Gaps come from logging dropouts, a scheduler overrun, or a sensor that genuinely stopped, and the filter cannot tell which. The default passes the longest ordinary interval on every corpus log, 90.5 ms, with 10 ms to spare (evidence); the replay harness’s --derive reads a log’s own, and prints it where it exceeds this.

The same bound holds a measurement timed after the state, which is carried forward to its time on the estimated velocity and the last sample’s rate: the longest the filter extrapolates on one sample either way. Later than this, a fuse_* refuses it as Fusion::OutOfHorizon.

§coast: Option<Coast>

Coasting across a step longer than max_predict_dt, equation (22′). None refuses the step instead, as Propagation::StepTooLong.

§hold: Option<Hold>

The position hold an unaided filter fuses to keep its tilt, equation (28″). None fuses nothing while unaided, and tilt is lost on the schedule ImuNoise sets.

§baro_offset_walk: f32

Random walk of the barometric offset, m s⁻¹ / √Hz: the q_b of equation (30′).

α₀, the reference a barometer’s altitude is measured against, is estimated rather than held constant, and this is how fast the filter lets it move. A barometer’s reference drifts with the weather and the sensor, which no window can measure, so it is configured as ImuNoise is.

The default is PX4’s baro_bias_nsd, src/modules/ekf2/EKF/common.h:347 at c4e4ef98e9, and the corpus’s one long drifting barometer agrees with it within what a log can say: the replay harness’s --derive reads 2c42096b’s walk as 0.21, an upper bound good to about half, and the log rejects no GNSS height at either value, where a constant reference, zero, rejects 14 (measured).

What it costs is height where the barometer does not drift. The simulator’s never does, and there mission scores 0.170 m of vertical RMSE here against 0.085 at zero: the offset walks away from what the barometer knew, and GNSS height takes over the low frequencies. A barometer characterized on the bench as more stable than this is the reason to lower it.

§baro_reference_from_estimate: bool

Whether a filter holding no barometric reference takes one from the estimate, at the first altitude once position is established. Equations (30) and (30′).

On by default, because every start that leaves no reference — one in motion, a window with no barometer, a seed — otherwise discards the barometer for the whole flight: cd7e0001, a coarse start, refuses all 3530 of its altitudes, as 2c42096b refused 35575 while it started coarse. PX4 does the same at baro_height_control.cpp:79 at c4e4ef98. See Eskf::fuse_baro_altitude for how the reference is seeded.

Off also keeps Recovery from reading a new one, for a barometer or a GNSS height locked out: the caller’s reference outlives both.

Off is for an application that names its own reference with Eskf::set_baro_reference — a surveyed pad, say — and would rather altitudes were refused with Fusion::NoReference until it does than referred to the estimate’s height at whatever moment the first reading arrived.

§yaw_estimator: bool

Whether the filter runs the yaw estimator of equations (45)–(52): a bank of yaw hypotheses weighed by each GNSS velocity, whose answer is adopted as the first heading where no heading source has established one.

It is how a multirotor with no magnetometer and no second antenna gets a heading, which Eskf::fuse_course cannot give a vehicle that flies in any direction facing any other. PX4 and ArduPilot run the same estimator (EKFGSF_yaw). It needs GNSS velocity and a horizontal acceleration: a hover separates no hypotheses.

On by default, per rejection handling. Off, it is never stepped and costs no cycles, for a vehicle that always has a heading source or a processor that cannot afford five small filters on every IMU sample (cost).

Implementations§

Source§

impl Config

Source

pub fn validate(&self) -> Result<(), ConfigError>

Check every value the filter would otherwise take on trust; Eskf::new calls it.

Config is plain data, built as Config { field, ..Config::default() }, so the bound on each value is checked once where it enters the filter rather than by a type per field. A value outside its bound is not a tuning the filter can honour: each reads as something else downstream, silently. A NaN Recovery timeout never compares true, so that source’s recovery is off; a zero one adopts on the first rejection, so its gate is off. A NaN density in ImuNoise, Coast or baro_offset_walk makes every step’s covariance non-finite, so predict refuses each one and the filter stops. A NaN Accuracy bound makes every quantity invalid for good. A negative density is squared into the same Q as its magnitude and is refused as a claim nothing means, the sign of a unit or a field confused. None is how an optional value says off, so an infinite one is refused rather than taken as a second spelling of it.

Trait Implementations§

Source§

impl Clone for Config

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for Config

Source§

impl Debug for Config

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Config

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl PartialEq for Config

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Config

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Scalar for T
where T: 'static + Clone + PartialEq + Debug,

Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.