AP_NavEKF3: don't square the wind-state variance limit when seeding

WIND_VEL_VARIANCE_MAX is already a variance: the airspeed-present
path constrains tasDataDelayed.tasVariance with it, and
ConstrainVariances() clamps P[22][22] and P[23][23] to it directly.
Three seeding paths nevertheless applied sq() to it, asking for a
160,000 m^2/s^2 (400m/s 1-sigma) covariance on the wind states:

 - the no-usable-airspeed branch of setWindMagStateLearningMode()
 - the no-valid-heading branch below it
 - the "allow EKF to relearn wind states rapidly" path, taken when
   the wind states stop being treated as truth

This changes no estimator behaviour.  All three seeds are clamped
back to WIND_VEL_VARIANCE_MAX by ConstrainVariances() before any
fusion step sees them: within one UpdateFilter() cycle
controlFilterModes() seeds, CovariancePrediction() (which contains
both the third site and the ConstrainVariances() call) runs next,
and the fusion selectors run after it.  A four-run alternating A/B
on Plane.DeadreckoningNoAirSpeed shows the two arms indistinguishable
in wind estimate, convergence time (283-287 simulated seconds in all
four runs) and dead-reckoning divergence.  The only observable
difference is in logging: a seed landing on a frame where
runUpdates is false is not clamped until the next prediction, so
XKV2 can record 160,000 for that one sample where it now records 400.

The value of the change is that it removes a units error that is
invisible only because a later clamp masks it, and that would bite
the moment the clamp moved, was relaxed, or a fourth site copied the
pattern.

The "use 2-sigma for faster initial convergence" comment on the
first site is replaced: the seed is the constant itself (20m/s
1-sigma), identical to the bound the airspeed-present branch clamps
to, and ConstrainVariances() would flatten any inflation regardless.

The first two sites arrived together in 59d31cc7d5 ("Rework
non-airspeed wind estimation"), which introduced the constant and
substituted it for a "typical wind speed" of 5.0f without dropping
that speed's sq(); the third repeated the pattern in ffde7f815c.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Peter Barker
2026-09-26 07:09:08 +10:00
committed by Andrew Tridgell
co-authored by Claude Fable 5.1
parent c9ba77a861
commit f9b1bacb39
2 changed files with 3 additions and 3 deletions
+2 -2
View File
@@ -92,7 +92,7 @@ void NavEKF3_core::setWindMagStateLearningMode()
stateStruct.wind_vel.x = windSpeed * cosF(tempEuler.z);
stateStruct.wind_vel.y = windSpeed * sinF(tempEuler.z);
} else {
trueAirspeedVariance = sq(WIND_VEL_VARIANCE_MAX); // use 2-sigma for faster initial convergence
trueAirspeedVariance = WIND_VEL_VARIANCE_MAX; // no airspeed: seed at the wind-state variance limit
}
// set the wind state variances to the measurement uncertainty
@@ -105,7 +105,7 @@ void NavEKF3_core::setWindMagStateLearningMode()
// set the variances using a typical max wind speed for small UAV operation
zeroStatesVarCov(22, 23);
for (uint8_t index=22; index<=23; index++) {
Pmut[index][index] = sq(WIND_VEL_VARIANCE_MAX);
Pmut[index][index] = WIND_VEL_VARIANCE_MAX;
}
}
}
+1 -1
View File
@@ -1122,7 +1122,7 @@ void NavEKF3_core::CovariancePrediction(Vector3F *rotVarVecPtr)
treatWindStatesAsTruth = false;
if (windStateIsObservable) {
// allow EKF to relearn wind states rapidly
Pmut[23][23] = Pmut[22][22] = sq(WIND_VEL_VARIANCE_MAX);
Pmut[23][23] = Pmut[22][22] = WIND_VEL_VARIANCE_MAX;
}
}
ftype windVelVar = sq(dt * constrain_ftype(frontend->_windVelProcessNoise, 0.0f, 1.0f) * (1.0f + constrain_ftype(frontend->_wndVarHgtRateScale, 0.0f, 1.0f) * fabsF(hgtRate)));