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: ImuNoiseIMU noise densities.
gates: GatesInnovation gate thresholds.
timeouts: TimeoutsFusion timeouts feeding Status.
recovery: RecoveryAutomatic recovery from gate lockout, per source.
correlation: CorrelationHow long each source’s measurement error persists, equation (24′).
init: InitializationStatic initialization.
accuracy: AccuracyHow good an estimate must be to count as valid.
gravity: f32Local 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: SecondsLargest 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: f32Random 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: boolWhether 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: boolWhether 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
impl Config
Sourcepub fn validate(&self) -> Result<(), ConfigError>
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§
impl Copy for Config
impl StructuralPartialEq for Config
Auto Trait Implementations§
impl Freeze for Config
impl RefUnwindSafe for Config
impl Send for Config
impl Sync for Config
impl Unpin for Config
impl UnsafeUnpin for Config
impl UnwindSafe for Config
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> Scalar for T
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.