mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-04-14 10:07:39 +08:00
ekf2: innovation sequence monitoring for all aid sources
- add new 'innovation_filtered' and 'test_ratio_filtered' fields to estimator_aid_source topics
This commit is contained in:
parent
1c657a59b1
commit
206488b844
@ -11,8 +11,12 @@ float32 observation
|
|||||||
float32 observation_variance
|
float32 observation_variance
|
||||||
|
|
||||||
float32 innovation
|
float32 innovation
|
||||||
|
float32 innovation_filtered
|
||||||
|
|
||||||
float32 innovation_variance
|
float32 innovation_variance
|
||||||
float32 test_ratio
|
|
||||||
|
float32 test_ratio # normalized innovation squared
|
||||||
|
float32 test_ratio_filtered # signed filtered test ratio
|
||||||
|
|
||||||
bool innovation_rejected # true if the observation has been rejected
|
bool innovation_rejected # true if the observation has been rejected
|
||||||
bool fused # true if the sample was successfully fused
|
bool fused # true if the sample was successfully fused
|
||||||
|
|||||||
@ -11,8 +11,12 @@ float32[2] observation
|
|||||||
float32[2] observation_variance
|
float32[2] observation_variance
|
||||||
|
|
||||||
float32[2] innovation
|
float32[2] innovation
|
||||||
|
float32[2] innovation_filtered
|
||||||
|
|
||||||
float32[2] innovation_variance
|
float32[2] innovation_variance
|
||||||
float32[2] test_ratio
|
|
||||||
|
float32[2] test_ratio # normalized innovation squared
|
||||||
|
float32[2] test_ratio_filtered # signed filtered test ratio
|
||||||
|
|
||||||
bool innovation_rejected # true if the observation has been rejected
|
bool innovation_rejected # true if the observation has been rejected
|
||||||
bool fused # true if the sample was successfully fused
|
bool fused # true if the sample was successfully fused
|
||||||
|
|||||||
@ -11,8 +11,12 @@ float32[3] observation
|
|||||||
float32[3] observation_variance
|
float32[3] observation_variance
|
||||||
|
|
||||||
float32[3] innovation
|
float32[3] innovation
|
||||||
|
float32[3] innovation_filtered
|
||||||
|
|
||||||
float32[3] innovation_variance
|
float32[3] innovation_variance
|
||||||
float32[3] test_ratio
|
|
||||||
|
float32[3] test_ratio # normalized innovation squared
|
||||||
|
float32[3] test_ratio_filtered # signed filtered test ratio
|
||||||
|
|
||||||
bool innovation_rejected # true if the observation has been rejected
|
bool innovation_rejected # true if the observation has been rejected
|
||||||
bool fused # true if the sample was successfully fused
|
bool fused # true if the sample was successfully fused
|
||||||
|
|||||||
@ -63,6 +63,7 @@ void Ekf::controlAirDataFusion(const imuSample &imu_delayed)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_EKF2_GNSS)
|
#if defined(CONFIG_EKF2_GNSS)
|
||||||
|
|
||||||
// clear yaw estimator airspeed (updated later with true airspeed if airspeed fusion is active)
|
// clear yaw estimator airspeed (updated later with true airspeed if airspeed fusion is active)
|
||||||
if (_control_status.flags.fixed_wing) {
|
if (_control_status.flags.fixed_wing) {
|
||||||
if (_control_status.flags.in_air && !_control_status.flags.vehicle_at_rest) {
|
if (_control_status.flags.in_air && !_control_status.flags.vehicle_at_rest) {
|
||||||
@ -74,6 +75,7 @@ void Ekf::controlAirDataFusion(const imuSample &imu_delayed)
|
|||||||
_yawEstimator.setTrueAirspeed(0.f);
|
_yawEstimator.setTrueAirspeed(0.f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CONFIG_EKF2_GNSS
|
#endif // CONFIG_EKF2_GNSS
|
||||||
|
|
||||||
if (_params.arsp_thr <= 0.f) {
|
if (_params.arsp_thr <= 0.f) {
|
||||||
@ -89,10 +91,14 @@ void Ekf::controlAirDataFusion(const imuSample &imu_delayed)
|
|||||||
|
|
||||||
_innov_check_fail_status.flags.reject_airspeed = _aid_src_airspeed.innovation_rejected; // TODO: remove this redundant flag
|
_innov_check_fail_status.flags.reject_airspeed = _aid_src_airspeed.innovation_rejected; // TODO: remove this redundant flag
|
||||||
|
|
||||||
const bool continuing_conditions_passing = _control_status.flags.in_air && _control_status.flags.fixed_wing && !_control_status.flags.fake_pos;
|
const bool continuing_conditions_passing = _control_status.flags.in_air
|
||||||
|
&& _control_status.flags.fixed_wing
|
||||||
|
&& !_control_status.flags.fake_pos;
|
||||||
|
|
||||||
const bool is_airspeed_significant = airspeed_sample.true_airspeed > _params.arsp_thr;
|
const bool is_airspeed_significant = airspeed_sample.true_airspeed > _params.arsp_thr;
|
||||||
const bool is_airspeed_consistent = (_aid_src_airspeed.test_ratio > 0.f && _aid_src_airspeed.test_ratio < 1.f);
|
const bool is_airspeed_consistent = (_aid_src_airspeed.test_ratio > 0.f && _aid_src_airspeed.test_ratio < 1.f);
|
||||||
const bool starting_conditions_passing = continuing_conditions_passing && is_airspeed_significant
|
const bool starting_conditions_passing = continuing_conditions_passing
|
||||||
|
&& is_airspeed_significant
|
||||||
&& (is_airspeed_consistent || !_control_status.flags.wind || _control_status.flags.inertial_dead_reckoning);
|
&& (is_airspeed_consistent || !_control_status.flags.wind || _control_status.flags.inertial_dead_reckoning);
|
||||||
|
|
||||||
if (_control_status.flags.fuse_aspd) {
|
if (_control_status.flags.fuse_aspd) {
|
||||||
@ -139,26 +145,22 @@ void Ekf::controlAirDataFusion(const imuSample &imu_delayed)
|
|||||||
|
|
||||||
void Ekf::updateAirspeed(const airspeedSample &airspeed_sample, estimator_aid_source1d_s &aid_src) const
|
void Ekf::updateAirspeed(const airspeedSample &airspeed_sample, estimator_aid_source1d_s &aid_src) const
|
||||||
{
|
{
|
||||||
// reset flags
|
|
||||||
resetEstimatorAidStatus(aid_src);
|
|
||||||
|
|
||||||
// Variance for true airspeed measurement - (m/sec)^2
|
// Variance for true airspeed measurement - (m/sec)^2
|
||||||
const float R = sq(math::constrain(_params.eas_noise, 0.5f, 5.0f) *
|
const float R = sq(math::constrain(_params.eas_noise, 0.5f, 5.0f) *
|
||||||
math::constrain(airspeed_sample.eas2tas, 0.9f, 10.0f));
|
math::constrain(airspeed_sample.eas2tas, 0.9f, 10.0f));
|
||||||
|
|
||||||
float innov = 0.f;
|
float innov = 0.f;
|
||||||
float innov_var = 0.f;
|
float innov_var = 0.f;
|
||||||
sym::ComputeAirspeedInnovAndInnovVar(_state.vector(), P, airspeed_sample.true_airspeed, R, FLT_EPSILON, &innov, &innov_var);
|
sym::ComputeAirspeedInnovAndInnovVar(_state.vector(), P, airspeed_sample.true_airspeed, R, FLT_EPSILON,
|
||||||
|
&innov, &innov_var);
|
||||||
|
|
||||||
aid_src.observation = airspeed_sample.true_airspeed;
|
updateAidSourceStatus(aid_src,
|
||||||
aid_src.observation_variance = R;
|
airspeed_sample.time_us, // sample timestamp
|
||||||
aid_src.innovation = innov;
|
airspeed_sample.true_airspeed, // observation
|
||||||
aid_src.innovation_variance = innov_var;
|
R, // observation variance
|
||||||
|
innov, // innovation
|
||||||
aid_src.timestamp_sample = airspeed_sample.time_us;
|
innov_var, // innovation variance
|
||||||
|
math::max(_params.tas_innov_gate, 1.f)); // innovation gate
|
||||||
const float innov_gate = fmaxf(_params.tas_innov_gate, 1.f);
|
|
||||||
setEstimatorAidStatusTestRatio(aid_src, innov_gate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::fuseAirspeed(const airspeedSample &airspeed_sample, estimator_aid_source1d_s &aid_src)
|
void Ekf::fuseAirspeed(const airspeedSample &airspeed_sample, estimator_aid_source1d_s &aid_src)
|
||||||
@ -221,7 +223,6 @@ void Ekf::stopAirspeedFusion()
|
|||||||
{
|
{
|
||||||
if (_control_status.flags.fuse_aspd) {
|
if (_control_status.flags.fuse_aspd) {
|
||||||
ECL_INFO("stopping airspeed fusion");
|
ECL_INFO("stopping airspeed fusion");
|
||||||
resetEstimatorAidStatus(_aid_src_airspeed);
|
|
||||||
_control_status.flags.fuse_aspd = false;
|
_control_status.flags.fuse_aspd = false;
|
||||||
|
|
||||||
#if defined(CONFIG_EKF2_GNSS)
|
#if defined(CONFIG_EKF2_GNSS)
|
||||||
@ -235,16 +236,18 @@ void Ekf::resetWindUsingAirspeed(const airspeedSample &airspeed_sample)
|
|||||||
constexpr float sideslip_var = sq(math::radians(15.0f));
|
constexpr float sideslip_var = sq(math::radians(15.0f));
|
||||||
|
|
||||||
const float euler_yaw = getEulerYaw(_R_to_earth);
|
const float euler_yaw = getEulerYaw(_R_to_earth);
|
||||||
const float airspeed_var = sq(math::constrain(_params.eas_noise, 0.5f, 5.0f) * math::constrain(airspeed_sample.eas2tas, 0.9f, 10.0f));
|
const float airspeed_var = sq(math::constrain(_params.eas_noise, 0.5f, 5.0f)
|
||||||
|
* math::constrain(airspeed_sample.eas2tas, 0.9f, 10.0f));
|
||||||
|
|
||||||
matrix::SquareMatrix<float, State::wind_vel.dof> P_wind;
|
matrix::SquareMatrix<float, State::wind_vel.dof> P_wind;
|
||||||
sym::ComputeWindInitAndCovFromAirspeed(_state.vel, euler_yaw, airspeed_sample.true_airspeed, getVelocityVariance(), getYawVar(), sideslip_var, airspeed_var, &_state.wind_vel, &P_wind);
|
sym::ComputeWindInitAndCovFromAirspeed(_state.vel, euler_yaw, airspeed_sample.true_airspeed, getVelocityVariance(),
|
||||||
|
getYawVar(), sideslip_var, airspeed_var, &_state.wind_vel, &P_wind);
|
||||||
|
|
||||||
resetStateCovariance<State::wind_vel>(P_wind);
|
resetStateCovariance<State::wind_vel>(P_wind);
|
||||||
|
|
||||||
ECL_INFO("reset wind using airspeed to (%.3f, %.3f)", (double)_state.wind_vel(0), (double)_state.wind_vel(1));
|
ECL_INFO("reset wind using airspeed to (%.3f, %.3f)", (double)_state.wind_vel(0), (double)_state.wind_vel(1));
|
||||||
|
|
||||||
_aid_src_airspeed.time_last_fuse = _time_delayed_us;
|
resetAidSourceStatusZeroInnovation(_aid_src_airspeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::resetVelUsingAirspeed(const airspeedSample &airspeed_sample)
|
void Ekf::resetVelUsingAirspeed(const airspeedSample &airspeed_sample)
|
||||||
|
|||||||
@ -80,14 +80,17 @@ void AuxGlobalPosition::update(Ekf &ekf, const estimator::imuSample &imu_delayed
|
|||||||
position = ekf.global_origin().project(sample.latitude, sample.longitude);
|
position = ekf.global_origin().project(sample.latitude, sample.longitude);
|
||||||
//const float hgt = ekf.getEkfGlobalOriginAltitude() - (float)sample.altitude;
|
//const float hgt = ekf.getEkfGlobalOriginAltitude() - (float)sample.altitude;
|
||||||
// relax the upper observation noise limit which prevents bad measurements perturbing the position estimate
|
// relax the upper observation noise limit which prevents bad measurements perturbing the position estimate
|
||||||
float pos_noise = math::max(sample.eph, _param_ekf2_agp_noise.get());
|
float pos_noise = math::max(sample.eph, _param_ekf2_agp_noise.get(), 0.01f);
|
||||||
const float pos_var = sq(pos_noise);
|
const float pos_var = sq(pos_noise);
|
||||||
const Vector2f pos_obs_var(pos_var, pos_var);
|
const Vector2f pos_obs_var(pos_var, pos_var);
|
||||||
ekf.updateHorizontalPositionAidSrcStatus(sample.time_us,
|
|
||||||
|
ekf.updateAidSourceStatus(aid_src,
|
||||||
|
sample.time_us, // sample timestamp
|
||||||
position, // observation
|
position, // observation
|
||||||
pos_obs_var, // observation variance
|
pos_obs_var, // observation variance
|
||||||
math::max(_param_ekf2_agp_gate.get(), 1.f), // innovation gate
|
Vector2f(ekf.state().pos) - position, // innovation
|
||||||
aid_src);
|
Vector2f(ekf.getPositionVariance()) + pos_obs_var, // innovation variance
|
||||||
|
math::max(_param_ekf2_agp_gate.get(), 1.f)); // innovation gate
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool starting_conditions = PX4_ISFINITE(sample.latitude) && PX4_ISFINITE(sample.longitude)
|
const bool starting_conditions = PX4_ISFINITE(sample.latitude) && PX4_ISFINITE(sample.longitude)
|
||||||
@ -97,6 +100,7 @@ void AuxGlobalPosition::update(Ekf &ekf, const estimator::imuSample &imu_delayed
|
|||||||
|
|
||||||
switch (_state) {
|
switch (_state) {
|
||||||
case State::stopped:
|
case State::stopped:
|
||||||
|
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
case State::starting:
|
case State::starting:
|
||||||
if (starting_conditions) {
|
if (starting_conditions) {
|
||||||
@ -116,6 +120,7 @@ void AuxGlobalPosition::update(Ekf &ekf, const estimator::imuSample &imu_delayed
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case State::active:
|
case State::active:
|
||||||
@ -124,8 +129,11 @@ void AuxGlobalPosition::update(Ekf &ekf, const estimator::imuSample &imu_delayed
|
|||||||
|
|
||||||
if (isTimedOut(aid_src.time_last_fuse, imu_delayed.time_us, ekf._params.no_aid_timeout_max)
|
if (isTimedOut(aid_src.time_last_fuse, imu_delayed.time_us, ekf._params.no_aid_timeout_max)
|
||||||
|| (_reset_counters.lat_lon != sample.lat_lon_reset_counter)) {
|
|| (_reset_counters.lat_lon != sample.lat_lon_reset_counter)) {
|
||||||
|
|
||||||
ekf.resetHorizontalPositionTo(Vector2f(aid_src.observation), Vector2f(aid_src.observation_variance));
|
ekf.resetHorizontalPositionTo(Vector2f(aid_src.observation), Vector2f(aid_src.observation_variance));
|
||||||
aid_src.time_last_fuse = imu_delayed.time_us;
|
|
||||||
|
ekf.resetAidSourceStatusZeroInnovation(aid_src);
|
||||||
|
|
||||||
_reset_counters.lat_lon = sample.lat_lon_reset_counter;
|
_reset_counters.lat_lon = sample.lat_lon_reset_counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,6 +141,7 @@ void AuxGlobalPosition::update(Ekf &ekf, const estimator::imuSample &imu_delayed
|
|||||||
ekf.disableControlStatusAuxGpos();
|
ekf.disableControlStatusAuxGpos();
|
||||||
_state = State::stopped;
|
_state = State::stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -143,6 +152,7 @@ void AuxGlobalPosition::update(Ekf &ekf, const estimator::imuSample &imu_delayed
|
|||||||
aid_src.timestamp = hrt_absolute_time();
|
aid_src.timestamp = hrt_absolute_time();
|
||||||
_estimator_aid_src_aux_global_position_pub.publish(aid_src);
|
_estimator_aid_src_aux_global_position_pub.publish(aid_src);
|
||||||
#endif // MODULE_NAME
|
#endif // MODULE_NAME
|
||||||
|
|
||||||
} else if ((_state != State::stopped) && isTimedOut(_time_last_buffer_push, imu_delayed.time_us, (uint64_t)5e6)) {
|
} else if ((_state != State::stopped) && isTimedOut(_time_last_buffer_push, imu_delayed.time_us, (uint64_t)5e6)) {
|
||||||
ekf.disableControlStatusAuxGpos();
|
ekf.disableControlStatusAuxGpos();
|
||||||
_state = State::stopped;
|
_state = State::stopped;
|
||||||
|
|||||||
@ -36,13 +36,17 @@
|
|||||||
void Ekf::controlAuxVelFusion()
|
void Ekf::controlAuxVelFusion()
|
||||||
{
|
{
|
||||||
if (_auxvel_buffer) {
|
if (_auxvel_buffer) {
|
||||||
auxVelSample auxvel_sample_delayed;
|
auxVelSample sample;
|
||||||
|
|
||||||
if (_auxvel_buffer->pop_first_older_than(_time_delayed_us, &auxvel_sample_delayed)) {
|
if (_auxvel_buffer->pop_first_older_than(_time_delayed_us, &sample)) {
|
||||||
|
|
||||||
resetEstimatorAidStatus(_aid_src_aux_vel);
|
updateAidSourceStatus(_aid_src_aux_vel,
|
||||||
|
sample.time_us, // sample timestamp
|
||||||
updateHorizontalVelocityAidSrcStatus(auxvel_sample_delayed.time_us, auxvel_sample_delayed.vel, auxvel_sample_delayed.velVar, fmaxf(_params.auxvel_gate, 1.f), _aid_src_aux_vel);
|
sample.vel, // observation
|
||||||
|
sample.velVar, // observation variance
|
||||||
|
Vector2f(_state.vel.xy()) - sample.vel, // innovation
|
||||||
|
Vector2f(getStateVariance<State::vel>()) + sample.velVar, // innovation variance
|
||||||
|
math::max(_params.auxvel_gate, 1.f)); // innovation gate
|
||||||
|
|
||||||
if (isHorizontalAidingActive()) {
|
if (isHorizontalAidingActive()) {
|
||||||
fuseHorizontalVelocity(_aid_src_aux_vel);
|
fuseHorizontalVelocity(_aid_src_aux_vel);
|
||||||
@ -55,5 +59,4 @@ void Ekf::stopAuxVelFusion()
|
|||||||
{
|
{
|
||||||
ECL_INFO("stopping aux vel fusion");
|
ECL_INFO("stopping aux vel fusion");
|
||||||
//_control_status.flags.aux_vel = false;
|
//_control_status.flags.aux_vel = false;
|
||||||
resetEstimatorAidStatus(_aid_src_aux_vel);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,8 +59,6 @@ void Ekf::controlBaroHeightFusion()
|
|||||||
|
|
||||||
const float measurement_var = sq(_params.baro_noise);
|
const float measurement_var = sq(_params.baro_noise);
|
||||||
|
|
||||||
const float innov_gate = fmaxf(_params.baro_innov_gate, 1.f);
|
|
||||||
|
|
||||||
const bool measurement_valid = PX4_ISFINITE(measurement) && PX4_ISFINITE(measurement_var);
|
const bool measurement_valid = PX4_ISFINITE(measurement) && PX4_ISFINITE(measurement_var);
|
||||||
|
|
||||||
if (measurement_valid) {
|
if (measurement_valid) {
|
||||||
@ -80,11 +78,11 @@ void Ekf::controlBaroHeightFusion()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// vertical position innovation - baro measurement has opposite sign to earth z axis
|
// vertical position innovation - baro measurement has opposite sign to earth z axis
|
||||||
updateVerticalPositionAidSrcStatus(baro_sample.time_us,
|
updateVerticalPositionAidStatus(aid_src,
|
||||||
-(measurement - bias_est.getBias()),
|
baro_sample.time_us,
|
||||||
measurement_var + bias_est.getBiasVar(),
|
-(measurement - bias_est.getBias()), // observation
|
||||||
innov_gate,
|
measurement_var + bias_est.getBiasVar(), // observation variance
|
||||||
aid_src);
|
math::max(_params.baro_innov_gate, 1.f)); // innovation gate
|
||||||
|
|
||||||
// Compensate for positive static pressure transients (negative vertical position innovations)
|
// Compensate for positive static pressure transients (negative vertical position innovations)
|
||||||
// caused by rotor wash ground interaction by applying a temporary deadzone to baro innovations.
|
// caused by rotor wash ground interaction by applying a temporary deadzone to baro innovations.
|
||||||
@ -191,7 +189,6 @@ void Ekf::stopBaroHgtFusion()
|
|||||||
}
|
}
|
||||||
|
|
||||||
_baro_b_est.setFusionInactive();
|
_baro_b_est.setFusionInactive();
|
||||||
resetEstimatorAidStatus(_aid_src_baro_hgt);
|
|
||||||
|
|
||||||
_control_status.flags.baro_hgt = false;
|
_control_status.flags.baro_hgt = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -105,11 +105,16 @@ void Ekf::fuseDrag(const dragSample &drag_sample)
|
|||||||
bcoef_inv(1) = bcoef_inv(0);
|
bcoef_inv(1) = bcoef_inv(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
_aid_src_drag.timestamp_sample = drag_sample.time_us;
|
|
||||||
_aid_src_drag.fused = false;
|
|
||||||
|
|
||||||
bool fused[] {false, false};
|
bool fused[] {false, false};
|
||||||
|
|
||||||
|
Vector2f observation{};
|
||||||
|
Vector2f observation_variance{R_ACC, R_ACC};
|
||||||
|
Vector2f innovation{};
|
||||||
|
Vector2f innovation_variance{};
|
||||||
|
|
||||||
|
// Apply an innovation consistency check with a 5 Sigma threshold
|
||||||
|
const float innov_gate = 5.f;
|
||||||
|
|
||||||
VectorState H;
|
VectorState H;
|
||||||
|
|
||||||
// perform sequential fusion of XY specific forces
|
// perform sequential fusion of XY specific forces
|
||||||
@ -120,16 +125,15 @@ void Ekf::fuseDrag(const dragSample &drag_sample)
|
|||||||
// Drag is modelled as an arbitrary combination of bluff body drag that proportional to
|
// Drag is modelled as an arbitrary combination of bluff body drag that proportional to
|
||||||
// equivalent airspeed squared, and rotor momentum drag that is proportional to true airspeed
|
// equivalent airspeed squared, and rotor momentum drag that is proportional to true airspeed
|
||||||
// parallel to the rotor disc and mass flow through the rotor disc.
|
// parallel to the rotor disc and mass flow through the rotor disc.
|
||||||
const float pred_acc = -0.5f * bcoef_inv(axis_index) * rho * rel_wind_body(axis_index) * rel_wind_speed - rel_wind_body(axis_index) * mcoef_corrrected;
|
const float pred_acc = -0.5f * bcoef_inv(axis_index) * rho * rel_wind_body(axis_index) * rel_wind_speed
|
||||||
|
- rel_wind_body(axis_index) * mcoef_corrrected;
|
||||||
|
|
||||||
_aid_src_drag.observation[axis_index] = mea_acc;
|
observation(axis_index) = mea_acc;
|
||||||
_aid_src_drag.observation_variance[axis_index] = R_ACC;
|
innovation(axis_index) = pred_acc - mea_acc;
|
||||||
_aid_src_drag.innovation[axis_index] = pred_acc - mea_acc;
|
|
||||||
_aid_src_drag.innovation_variance[axis_index] = NAN; // reset
|
|
||||||
|
|
||||||
if (axis_index == 0) {
|
if (axis_index == 0) {
|
||||||
sym::ComputeDragXInnovVarAndH(state_vector_prev, P, rho, bcoef_inv(axis_index), mcoef_corrrected, R_ACC, FLT_EPSILON,
|
sym::ComputeDragXInnovVarAndH(state_vector_prev, P, rho, bcoef_inv(axis_index), mcoef_corrrected, R_ACC, FLT_EPSILON,
|
||||||
&_aid_src_drag.innovation_variance[axis_index], &H);
|
&innovation_variance(axis_index), &H);
|
||||||
|
|
||||||
if (!using_bcoef_x && !using_mcoef) {
|
if (!using_bcoef_x && !using_mcoef) {
|
||||||
continue;
|
continue;
|
||||||
@ -137,35 +141,41 @@ void Ekf::fuseDrag(const dragSample &drag_sample)
|
|||||||
|
|
||||||
} else if (axis_index == 1) {
|
} else if (axis_index == 1) {
|
||||||
sym::ComputeDragYInnovVarAndH(state_vector_prev, P, rho, bcoef_inv(axis_index), mcoef_corrrected, R_ACC, FLT_EPSILON,
|
sym::ComputeDragYInnovVarAndH(state_vector_prev, P, rho, bcoef_inv(axis_index), mcoef_corrrected, R_ACC, FLT_EPSILON,
|
||||||
&_aid_src_drag.innovation_variance[axis_index], &H);
|
&innovation_variance(axis_index), &H);
|
||||||
|
|
||||||
if (!using_bcoef_y && !using_mcoef) {
|
if (!using_bcoef_y && !using_mcoef) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_aid_src_drag.innovation_variance[axis_index] < R_ACC) {
|
if (innovation_variance(axis_index) < R_ACC) {
|
||||||
// calculation is badly conditioned
|
// calculation is badly conditioned
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply an innovation consistency check with a 5 Sigma threshold
|
const float test_ratio = sq(innovation(axis_index)) / (sq(innov_gate) * innovation_variance(axis_index));
|
||||||
const float innov_gate = 5.f;
|
|
||||||
setEstimatorAidStatusTestRatio(_aid_src_drag, innov_gate);
|
|
||||||
|
|
||||||
if (_control_status.flags.in_air && _control_status.flags.wind && !_control_status.flags.fake_pos
|
if (_control_status.flags.in_air && _control_status.flags.wind && !_control_status.flags.fake_pos
|
||||||
&& PX4_ISFINITE(_aid_src_drag.innovation_variance[axis_index]) && PX4_ISFINITE(_aid_src_drag.innovation[axis_index])
|
&& PX4_ISFINITE(innovation_variance(axis_index)) && PX4_ISFINITE(innovation(axis_index))
|
||||||
&& (_aid_src_drag.test_ratio[axis_index] < 1.f)
|
&& (test_ratio < 1.f)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
VectorState K = P * H / _aid_src_drag.innovation_variance[axis_index];
|
VectorState K = P * H / innovation_variance(axis_index);
|
||||||
|
|
||||||
if (measurementUpdate(K, H, R_ACC, _aid_src_drag.innovation[axis_index])) {
|
if (measurementUpdate(K, H, R_ACC, innovation(axis_index))) {
|
||||||
fused[axis_index] = true;
|
fused[axis_index] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateAidSourceStatus(_aid_src_drag,
|
||||||
|
drag_sample.time_us, // sample timestamp
|
||||||
|
observation, // observation
|
||||||
|
observation_variance, // observation variance
|
||||||
|
innovation, // innovation
|
||||||
|
innovation_variance, // innovation variance
|
||||||
|
innov_gate); // innovation gate
|
||||||
|
|
||||||
if (fused[0] && fused[1]) {
|
if (fused[0] && fused[1]) {
|
||||||
_aid_src_drag.fused = true;
|
_aid_src_drag.fused = true;
|
||||||
_aid_src_drag.time_last_fuse = _time_delayed_us;
|
_aid_src_drag.time_last_fuse = _time_delayed_us;
|
||||||
|
|||||||
@ -69,9 +69,6 @@ void Ekf::controlExternalVisionFusion()
|
|||||||
_ev_sample_prev = ev_sample;
|
_ev_sample_prev = ev_sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
// record corresponding yaw state for future EV delta heading innovation (logging only)
|
|
||||||
_ev_yaw_pred_prev = getEulerYaw(_state.quat_nominal);
|
|
||||||
|
|
||||||
} else if ((_control_status.flags.ev_pos || _control_status.flags.ev_vel || _control_status.flags.ev_yaw
|
} else if ((_control_status.flags.ev_pos || _control_status.flags.ev_vel || _control_status.flags.ev_yaw
|
||||||
|| _control_status.flags.ev_hgt)
|
|| _control_status.flags.ev_hgt)
|
||||||
&& isTimedOut(_ev_sample_prev.time_us, 2 * EV_MAX_INTERVAL)) {
|
&& isTimedOut(_ev_sample_prev.time_us, 2 * EV_MAX_INTERVAL)) {
|
||||||
|
|||||||
@ -85,11 +85,11 @@ void Ekf::controlEvHeightFusion(const extVisionSample &ev_sample, const bool com
|
|||||||
|
|
||||||
const bool measurement_valid = PX4_ISFINITE(measurement) && PX4_ISFINITE(measurement_var);
|
const bool measurement_valid = PX4_ISFINITE(measurement) && PX4_ISFINITE(measurement_var);
|
||||||
|
|
||||||
updateVerticalPositionAidSrcStatus(ev_sample.time_us,
|
updateVerticalPositionAidStatus(aid_src,
|
||||||
measurement - bias_est.getBias(),
|
ev_sample.time_us,
|
||||||
measurement_var + bias_est.getBiasVar(),
|
measurement - bias_est.getBias(), // observation
|
||||||
math::max(_params.ev_pos_innov_gate, 1.f),
|
measurement_var + bias_est.getBiasVar(), // observation variance
|
||||||
aid_src);
|
math::max(_params.ev_pos_innov_gate, 1.f)); // innovation gate
|
||||||
|
|
||||||
// update the bias estimator before updating the main filter but after
|
// update the bias estimator before updating the main filter but after
|
||||||
// using its current state to compute the vertical position innovation
|
// using its current state to compute the vertical position innovation
|
||||||
@ -220,7 +220,6 @@ void Ekf::stopEvHgtFusion()
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ev_hgt_b_est.setFusionInactive();
|
_ev_hgt_b_est.setFusionInactive();
|
||||||
resetEstimatorAidStatus(_aid_src_ev_hgt);
|
|
||||||
|
|
||||||
_control_status.flags.ev_hgt = false;
|
_control_status.flags.ev_hgt = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -126,12 +126,14 @@ void Ekf::controlEvPosFusion(const extVisionSample &ev_sample, const bool common
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_EKF2_GNSS)
|
#if defined(CONFIG_EKF2_GNSS)
|
||||||
|
|
||||||
// increase minimum variance if GPS active (position reference)
|
// increase minimum variance if GPS active (position reference)
|
||||||
if (_control_status.flags.gps) {
|
if (_control_status.flags.gps) {
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
pos_cov(i, i) = math::max(pos_cov(i, i), sq(_params.gps_pos_noise));
|
pos_cov(i, i) = math::max(pos_cov(i, i), sq(_params.gps_pos_noise));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CONFIG_EKF2_GNSS
|
#endif // CONFIG_EKF2_GNSS
|
||||||
|
|
||||||
const Vector2f measurement{pos(0), pos(1)};
|
const Vector2f measurement{pos(0), pos(1)};
|
||||||
@ -155,18 +157,24 @@ void Ekf::controlEvPosFusion(const extVisionSample &ev_sample, const bool common
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateHorizontalPositionAidSrcStatus(ev_sample.time_us,
|
const Vector2f position = measurement - _ev_pos_b_est.getBias();
|
||||||
measurement - _ev_pos_b_est.getBias(), // observation
|
const Vector2f pos_obs_var = measurement_var + _ev_pos_b_est.getBiasVar();
|
||||||
measurement_var + _ev_pos_b_est.getBiasVar(), // observation variance
|
|
||||||
math::max(_params.ev_pos_innov_gate, 1.f), // innovation gate
|
updateAidSourceStatus(aid_src,
|
||||||
aid_src);
|
ev_sample.time_us, // sample timestamp
|
||||||
|
position, // observation
|
||||||
|
pos_obs_var, // observation variance
|
||||||
|
Vector2f(_state.pos) - position, // innovation
|
||||||
|
Vector2f(getStateVariance<State::pos>()) + pos_obs_var, // innovation variance
|
||||||
|
math::max(_params.ev_pos_innov_gate, 1.f)); // innovation gate
|
||||||
|
|
||||||
// update the bias estimator before updating the main filter but after
|
// update the bias estimator before updating the main filter but after
|
||||||
// using its current state to compute the vertical position innovation
|
// using its current state to compute the vertical position innovation
|
||||||
if (measurement_valid && quality_sufficient) {
|
if (measurement_valid && quality_sufficient) {
|
||||||
_ev_pos_b_est.setMaxStateNoise(Vector2f(sqrtf(measurement_var(0)), sqrtf(measurement_var(1))));
|
_ev_pos_b_est.setMaxStateNoise(Vector2f(sqrtf(measurement_var(0)), sqrtf(measurement_var(1))));
|
||||||
_ev_pos_b_est.setProcessNoiseSpectralDensity(_params.ev_hgt_bias_nsd); // TODO
|
_ev_pos_b_est.setProcessNoiseSpectralDensity(_params.ev_hgt_bias_nsd); // TODO
|
||||||
_ev_pos_b_est.fuseBias(measurement - Vector2f(_state.pos.xy()), measurement_var + Vector2f(getStateVariance<State::pos>()));
|
_ev_pos_b_est.fuseBias(measurement - Vector2f(_state.pos.xy()),
|
||||||
|
measurement_var + Vector2f(getStateVariance<State::pos>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!measurement_valid) {
|
if (!measurement_valid) {
|
||||||
@ -297,8 +305,6 @@ void Ekf::updateEvPosFusion(const Vector2f &measurement, const Vector2f &measure
|
|||||||
void Ekf::stopEvPosFusion()
|
void Ekf::stopEvPosFusion()
|
||||||
{
|
{
|
||||||
if (_control_status.flags.ev_pos) {
|
if (_control_status.flags.ev_pos) {
|
||||||
resetEstimatorAidStatus(_aid_src_ev_pos);
|
|
||||||
|
|
||||||
_control_status.flags.ev_pos = false;
|
_control_status.flags.ev_pos = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,12 +107,14 @@ void Ekf::controlEvVelFusion(const extVisionSample &ev_sample, const bool common
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_EKF2_GNSS)
|
#if defined(CONFIG_EKF2_GNSS)
|
||||||
|
|
||||||
// increase minimum variance if GPS active (position reference)
|
// increase minimum variance if GPS active (position reference)
|
||||||
if (_control_status.flags.gps) {
|
if (_control_status.flags.gps) {
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
vel_cov(i, i) = math::max(vel_cov(i, i), sq(_params.gps_vel_noise));
|
vel_cov(i, i) = math::max(vel_cov(i, i), sq(_params.gps_vel_noise));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CONFIG_EKF2_GNSS
|
#endif // CONFIG_EKF2_GNSS
|
||||||
|
|
||||||
const Vector3f measurement{vel};
|
const Vector3f measurement{vel};
|
||||||
@ -125,11 +127,13 @@ void Ekf::controlEvVelFusion(const extVisionSample &ev_sample, const bool common
|
|||||||
|
|
||||||
const bool measurement_valid = measurement.isAllFinite() && measurement_var.isAllFinite();
|
const bool measurement_valid = measurement.isAllFinite() && measurement_var.isAllFinite();
|
||||||
|
|
||||||
updateVelocityAidSrcStatus(ev_sample.time_us,
|
updateAidSourceStatus(aid_src,
|
||||||
|
ev_sample.time_us, // sample timestamp
|
||||||
measurement, // observation
|
measurement, // observation
|
||||||
measurement_var, // observation variance
|
measurement_var, // observation variance
|
||||||
math::max(_params.ev_vel_innov_gate, 1.f), // innovation gate
|
_state.vel - measurement, // innovation
|
||||||
aid_src);
|
getVelocityVariance() + measurement_var, // innovation variance
|
||||||
|
math::max(_params.ev_vel_innov_gate, 1.f)); // innovation gate
|
||||||
|
|
||||||
if (!measurement_valid) {
|
if (!measurement_valid) {
|
||||||
continuing_conditions_passing = false;
|
continuing_conditions_passing = false;
|
||||||
@ -149,7 +153,7 @@ void Ekf::controlEvVelFusion(const extVisionSample &ev_sample, const bool common
|
|||||||
ECL_INFO("reset to %s", AID_SRC_NAME);
|
ECL_INFO("reset to %s", AID_SRC_NAME);
|
||||||
_information_events.flags.reset_vel_to_vision = true;
|
_information_events.flags.reset_vel_to_vision = true;
|
||||||
resetVelocityTo(measurement, measurement_var);
|
resetVelocityTo(measurement, measurement_var);
|
||||||
aid_src.time_last_fuse = _time_delayed_us;
|
resetAidSourceStatusZeroInnovation(aid_src);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// EV has reset, but quality isn't sufficient
|
// EV has reset, but quality isn't sufficient
|
||||||
@ -174,7 +178,7 @@ void Ekf::controlEvVelFusion(const extVisionSample &ev_sample, const bool common
|
|||||||
_information_events.flags.reset_vel_to_vision = true;
|
_information_events.flags.reset_vel_to_vision = true;
|
||||||
ECL_WARN("%s fusion failing, resetting", AID_SRC_NAME);
|
ECL_WARN("%s fusion failing, resetting", AID_SRC_NAME);
|
||||||
resetVelocityTo(measurement, measurement_var);
|
resetVelocityTo(measurement, measurement_var);
|
||||||
aid_src.time_last_fuse = _time_delayed_us;
|
resetAidSourceStatusZeroInnovation(aid_src);
|
||||||
|
|
||||||
if (_control_status.flags.in_air) {
|
if (_control_status.flags.in_air) {
|
||||||
_nb_ev_vel_reset_available--;
|
_nb_ev_vel_reset_available--;
|
||||||
@ -205,19 +209,25 @@ void Ekf::controlEvVelFusion(const extVisionSample &ev_sample, const bool common
|
|||||||
if (starting_conditions_passing) {
|
if (starting_conditions_passing) {
|
||||||
// activate fusion, only reset if necessary
|
// activate fusion, only reset if necessary
|
||||||
if (!isHorizontalAidingActive() || yaw_alignment_changed) {
|
if (!isHorizontalAidingActive() || yaw_alignment_changed) {
|
||||||
ECL_INFO("starting %s fusion, resetting velocity to (%.3f, %.3f, %.3f)", AID_SRC_NAME, (double)measurement(0), (double)measurement(1), (double)measurement(2));
|
ECL_INFO("starting %s fusion, resetting velocity to (%.3f, %.3f, %.3f)", AID_SRC_NAME,
|
||||||
_information_events.flags.reset_vel_to_vision = true;
|
(double)measurement(0), (double)measurement(1), (double)measurement(2));
|
||||||
resetVelocityTo(measurement, measurement_var);
|
|
||||||
|
|
||||||
} else {
|
_information_events.flags.reset_vel_to_vision = true;
|
||||||
|
|
||||||
|
resetVelocityTo(measurement, measurement_var);
|
||||||
|
resetAidSourceStatusZeroInnovation(aid_src);
|
||||||
|
|
||||||
|
_control_status.flags.ev_vel = true;
|
||||||
|
|
||||||
|
} else if (fuseVelocity(aid_src)) {
|
||||||
ECL_INFO("starting %s fusion", AID_SRC_NAME);
|
ECL_INFO("starting %s fusion", AID_SRC_NAME);
|
||||||
|
_control_status.flags.ev_vel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
aid_src.time_last_fuse = _time_delayed_us;
|
if (_control_status.flags.ev_vel) {
|
||||||
|
|
||||||
_nb_ev_vel_reset_available = 5;
|
_nb_ev_vel_reset_available = 5;
|
||||||
_information_events.flags.starting_vision_vel_fusion = true;
|
_information_events.flags.starting_vision_vel_fusion = true;
|
||||||
_control_status.flags.ev_vel = true;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -225,7 +235,6 @@ void Ekf::controlEvVelFusion(const extVisionSample &ev_sample, const bool common
|
|||||||
void Ekf::stopEvVelFusion()
|
void Ekf::stopEvVelFusion()
|
||||||
{
|
{
|
||||||
if (_control_status.flags.ev_vel) {
|
if (_control_status.flags.ev_vel) {
|
||||||
resetEstimatorAidStatus(_aid_src_ev_vel);
|
|
||||||
|
|
||||||
_control_status.flags.ev_vel = false;
|
_control_status.flags.ev_vel = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,11 +43,22 @@ void Ekf::controlEvYawFusion(const extVisionSample &ev_sample, const bool common
|
|||||||
{
|
{
|
||||||
static constexpr const char *AID_SRC_NAME = "EV yaw";
|
static constexpr const char *AID_SRC_NAME = "EV yaw";
|
||||||
|
|
||||||
resetEstimatorAidStatus(aid_src);
|
float obs = getEulerYaw(ev_sample.quat);
|
||||||
aid_src.timestamp_sample = ev_sample.time_us;
|
float obs_var = math::max(ev_sample.orientation_var(2), _params.ev_att_noise, sq(0.01f));
|
||||||
aid_src.observation = getEulerYaw(ev_sample.quat);
|
|
||||||
aid_src.observation_variance = math::max(ev_sample.orientation_var(2), _params.ev_att_noise, sq(0.01f));
|
float innov = wrap_pi(getEulerYaw(_R_to_earth) - obs);
|
||||||
aid_src.innovation = wrap_pi(getEulerYaw(_R_to_earth) - aid_src.observation);
|
float innov_var = 0.f;
|
||||||
|
|
||||||
|
VectorState H_YAW;
|
||||||
|
computeYawInnovVarAndH(obs_var, innov_var, H_YAW);
|
||||||
|
|
||||||
|
updateAidSourceStatus(aid_src,
|
||||||
|
ev_sample.time_us, // sample timestamp
|
||||||
|
obs, // observation
|
||||||
|
obs_var, // observation variance
|
||||||
|
innov, // innovation
|
||||||
|
innov_var, // innovation variance
|
||||||
|
math::max(_params.heading_innov_gate, 1.f)); // innovation gate
|
||||||
|
|
||||||
if (ev_reset) {
|
if (ev_reset) {
|
||||||
_control_status.flags.ev_yaw_fault = false;
|
_control_status.flags.ev_yaw_fault = false;
|
||||||
@ -65,10 +76,6 @@ void Ekf::controlEvYawFusion(const extVisionSample &ev_sample, const bool common
|
|||||||
&& (ev_sample.pos_frame != PositionFrame::LOCAL_FRAME_NED)
|
&& (ev_sample.pos_frame != PositionFrame::LOCAL_FRAME_NED)
|
||||||
) {
|
) {
|
||||||
continuing_conditions_passing = false;
|
continuing_conditions_passing = false;
|
||||||
|
|
||||||
// use delta yaw for innovation logging
|
|
||||||
aid_src.innovation = wrap_pi(wrap_pi(getEulerYaw(_R_to_earth) - _ev_yaw_pred_prev)
|
|
||||||
- wrap_pi(getEulerYaw(ev_sample.quat) - getEulerYaw(_ev_sample_prev.quat)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool starting_conditions_passing = common_starting_conditions_passing
|
const bool starting_conditions_passing = common_starting_conditions_passing
|
||||||
@ -94,7 +101,7 @@ void Ekf::controlEvYawFusion(const extVisionSample &ev_sample, const bool common
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if (quality_sufficient) {
|
} else if (quality_sufficient) {
|
||||||
fuseYaw(aid_src);
|
fuseYaw(aid_src, H_YAW);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
aid_src.innovation_rejected = true;
|
aid_src.innovation_rejected = true;
|
||||||
@ -141,18 +148,25 @@ void Ekf::controlEvYawFusion(const extVisionSample &ev_sample, const bool common
|
|||||||
if (ev_sample.pos_frame == PositionFrame::LOCAL_FRAME_NED) {
|
if (ev_sample.pos_frame == PositionFrame::LOCAL_FRAME_NED) {
|
||||||
|
|
||||||
if (_control_status.flags.yaw_align) {
|
if (_control_status.flags.yaw_align) {
|
||||||
|
|
||||||
|
if (fuseYaw(aid_src, H_YAW)) {
|
||||||
ECL_INFO("starting %s fusion", AID_SRC_NAME);
|
ECL_INFO("starting %s fusion", AID_SRC_NAME);
|
||||||
|
_information_events.flags.starting_vision_yaw_fusion = true;
|
||||||
|
|
||||||
|
_control_status.flags.ev_yaw = true;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// reset yaw to EV and set yaw_align
|
// reset yaw to EV and set yaw_align
|
||||||
ECL_INFO("starting %s fusion, resetting state", AID_SRC_NAME);
|
ECL_INFO("starting %s fusion, resetting state", AID_SRC_NAME);
|
||||||
|
_information_events.flags.starting_vision_yaw_fusion = true;
|
||||||
|
|
||||||
resetQuatStateYaw(aid_src.observation, aid_src.observation_variance);
|
resetQuatStateYaw(aid_src.observation, aid_src.observation_variance);
|
||||||
_control_status.flags.yaw_align = true;
|
_control_status.flags.yaw_align = true;
|
||||||
}
|
_control_status.flags.ev_yaw = true;
|
||||||
|
|
||||||
aid_src.time_last_fuse = _time_delayed_us;
|
aid_src.time_last_fuse = _time_delayed_us;
|
||||||
_information_events.flags.starting_vision_yaw_fusion = true;
|
}
|
||||||
_control_status.flags.ev_yaw = true;
|
|
||||||
|
|
||||||
} else if (ev_sample.pos_frame == PositionFrame::LOCAL_FRAME_FRD) {
|
} else if (ev_sample.pos_frame == PositionFrame::LOCAL_FRAME_FRD) {
|
||||||
// turn on fusion of external vision yaw measurements
|
// turn on fusion of external vision yaw measurements
|
||||||
@ -178,7 +192,6 @@ void Ekf::stopEvYawFusion()
|
|||||||
{
|
{
|
||||||
#if defined(CONFIG_EKF2_EXTERNAL_VISION)
|
#if defined(CONFIG_EKF2_EXTERNAL_VISION)
|
||||||
if (_control_status.flags.ev_yaw) {
|
if (_control_status.flags.ev_yaw) {
|
||||||
resetEstimatorAidStatus(_aid_src_ev_yaw);
|
|
||||||
|
|
||||||
_control_status.flags.ev_yaw = false;
|
_control_status.flags.ev_yaw = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,8 +51,7 @@ void Ekf::controlFakeHgtFusion()
|
|||||||
const float obs_var = sq(_params.pos_noaid_noise);
|
const float obs_var = sq(_params.pos_noaid_noise);
|
||||||
const float innov_gate = 3.f;
|
const float innov_gate = 3.f;
|
||||||
|
|
||||||
updateVerticalPositionAidSrcStatus(_time_delayed_us, _last_known_pos(2), obs_var, innov_gate, aid_src);
|
updateVerticalPositionAidStatus(aid_src, _time_delayed_us, _last_known_pos(2), obs_var, innov_gate);
|
||||||
|
|
||||||
|
|
||||||
const bool continuing_conditions_passing = !isVerticalAidingActive();
|
const bool continuing_conditions_passing = !isVerticalAidingActive();
|
||||||
const bool starting_conditions_passing = continuing_conditions_passing
|
const bool starting_conditions_passing = continuing_conditions_passing
|
||||||
@ -113,7 +112,5 @@ void Ekf::stopFakeHgtFusion()
|
|||||||
if (_control_status.flags.fake_hgt) {
|
if (_control_status.flags.fake_hgt) {
|
||||||
ECL_INFO("stop fake height fusion");
|
ECL_INFO("stop fake height fusion");
|
||||||
_control_status.flags.fake_hgt = false;
|
_control_status.flags.fake_hgt = false;
|
||||||
|
|
||||||
resetEstimatorAidStatus(_aid_src_fake_hgt);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,10 +63,17 @@ void Ekf::controlFakePosFusion()
|
|||||||
obs_var(0) = obs_var(1) = sq(0.5f);
|
obs_var(0) = obs_var(1) = sq(0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Vector2f position(_last_known_pos);
|
||||||
|
|
||||||
const float innov_gate = 3.f;
|
const float innov_gate = 3.f;
|
||||||
|
|
||||||
updateHorizontalPositionAidSrcStatus(_time_delayed_us, Vector2f(_last_known_pos), obs_var, innov_gate, aid_src);
|
updateAidSourceStatus(aid_src,
|
||||||
|
_time_delayed_us,
|
||||||
|
position, // observation
|
||||||
|
obs_var, // observation variance
|
||||||
|
Vector2f(_state.pos) - position, // innovation
|
||||||
|
Vector2f(getStateVariance<State::pos>()) + obs_var, // innovation variance
|
||||||
|
innov_gate); // innovation gate
|
||||||
|
|
||||||
const bool continuing_conditions_passing = !isHorizontalAidingActive()
|
const bool continuing_conditions_passing = !isHorizontalAidingActive()
|
||||||
&& ((getTiltVariance() > sq(math::radians(3.f))) || _control_status.flags.vehicle_at_rest)
|
&& ((getTiltVariance() > sq(math::radians(3.f))) || _control_status.flags.vehicle_at_rest)
|
||||||
@ -131,7 +138,5 @@ void Ekf::stopFakePosFusion()
|
|||||||
if (_control_status.flags.fake_pos) {
|
if (_control_status.flags.fake_pos) {
|
||||||
ECL_INFO("stop fake position fusion");
|
ECL_INFO("stop fake position fusion");
|
||||||
_control_status.flags.fake_pos = false;
|
_control_status.flags.fake_pos = false;
|
||||||
|
|
||||||
resetEstimatorAidStatus(_aid_src_fake_pos);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,16 +67,14 @@ void Ekf::controlGnssHeightFusion(const gnssSample &gps_sample)
|
|||||||
const float measurement = gnss_alt - getEkfGlobalOriginAltitude();
|
const float measurement = gnss_alt - getEkfGlobalOriginAltitude();
|
||||||
const float measurement_var = sq(noise);
|
const float measurement_var = sq(noise);
|
||||||
|
|
||||||
const float innov_gate = math::max(_params.gps_pos_innov_gate, 1.f);
|
|
||||||
|
|
||||||
const bool measurement_valid = PX4_ISFINITE(measurement) && PX4_ISFINITE(measurement_var);
|
const bool measurement_valid = PX4_ISFINITE(measurement) && PX4_ISFINITE(measurement_var);
|
||||||
|
|
||||||
// GNSS position, vertical position GNSS measurement has opposite sign to earth z axis
|
// GNSS position, vertical position GNSS measurement has opposite sign to earth z axis
|
||||||
updateVerticalPositionAidSrcStatus(gps_sample.time_us,
|
updateVerticalPositionAidStatus(aid_src,
|
||||||
|
gps_sample.time_us,
|
||||||
-(measurement - bias_est.getBias()),
|
-(measurement - bias_est.getBias()),
|
||||||
measurement_var + bias_est.getBiasVar(),
|
measurement_var + bias_est.getBiasVar(),
|
||||||
innov_gate,
|
math::max(_params.gps_pos_innov_gate, 1.f));
|
||||||
aid_src);
|
|
||||||
|
|
||||||
// update the bias estimator before updating the main filter but after
|
// update the bias estimator before updating the main filter but after
|
||||||
// using its current state to compute the vertical position innovation
|
// using its current state to compute the vertical position innovation
|
||||||
@ -171,7 +169,6 @@ void Ekf::stopGpsHgtFusion()
|
|||||||
}
|
}
|
||||||
|
|
||||||
_gps_hgt_b_est.setFusionInactive();
|
_gps_hgt_b_est.setFusionInactive();
|
||||||
resetEstimatorAidStatus(_aid_src_gnss_hgt);
|
|
||||||
|
|
||||||
_control_status.flags.gps_hgt = false;
|
_control_status.flags.gps_hgt = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -183,13 +183,26 @@ void Ekf::updateGnssVel(const gnssSample &gnss_sample, estimator_aid_source3d_s
|
|||||||
const Vector3f vel_offset_earth = _R_to_earth * vel_offset_body;
|
const Vector3f vel_offset_earth = _R_to_earth * vel_offset_body;
|
||||||
const Vector3f velocity = gnss_sample.vel - vel_offset_earth;
|
const Vector3f velocity = gnss_sample.vel - vel_offset_earth;
|
||||||
|
|
||||||
const float vel_var = sq(math::max(gnss_sample.sacc, _params.gps_vel_noise));
|
const float vel_var = sq(math::max(gnss_sample.sacc, _params.gps_vel_noise, 0.01f));
|
||||||
const Vector3f vel_obs_var(vel_var, vel_var, vel_var * sq(1.5f));
|
const Vector3f vel_obs_var(vel_var, vel_var, vel_var * sq(1.5f));
|
||||||
updateVelocityAidSrcStatus(gnss_sample.time_us,
|
|
||||||
|
const float innovation_gate = math::max(_params.gps_vel_innov_gate, 1.f);
|
||||||
|
|
||||||
|
updateAidSourceStatus(aid_src,
|
||||||
|
gnss_sample.time_us, // sample timestamp
|
||||||
velocity, // observation
|
velocity, // observation
|
||||||
vel_obs_var, // observation variance
|
vel_obs_var, // observation variance
|
||||||
math::max(_params.gps_vel_innov_gate, 1.f), // innovation gate
|
_state.vel - velocity, // innovation
|
||||||
aid_src);
|
getVelocityVariance() + vel_obs_var, // innovation variance
|
||||||
|
innovation_gate); // innovation gate
|
||||||
|
|
||||||
|
// vz special case if there is bad vertical acceleration data, then don't reject measurement if GNSS reports velocity accuracy is acceptable,
|
||||||
|
// but limit innovation to prevent spikes that could destabilise the filter
|
||||||
|
if (aid_src.innovation_rejected && _fault_status.flags.bad_acc_vertical && (gnss_sample.sacc < _params.req_sacc)) {
|
||||||
|
const float innov_limit = innovation_gate * sqrtf(aid_src.innovation_variance[2]);
|
||||||
|
aid_src.innovation[2] = math::constrain(aid_src.innovation[2], -innov_limit, innov_limit);
|
||||||
|
aid_src.innovation_rejected = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::updateGnssPos(const gnssSample &gnss_sample, estimator_aid_source2d_s &aid_src)
|
void Ekf::updateGnssPos(const gnssSample &gnss_sample, estimator_aid_source2d_s &aid_src)
|
||||||
@ -210,13 +223,16 @@ void Ekf::updateGnssPos(const gnssSample &gnss_sample, estimator_aid_source2d_s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const float pos_var = sq(pos_noise);
|
const float pos_var = math::max(sq(pos_noise), sq(0.01f));
|
||||||
const Vector2f pos_obs_var(pos_var, pos_var);
|
const Vector2f pos_obs_var(pos_var, pos_var);
|
||||||
updateHorizontalPositionAidSrcStatus(gnss_sample.time_us,
|
|
||||||
|
updateAidSourceStatus(aid_src,
|
||||||
|
gnss_sample.time_us, // sample timestamp
|
||||||
position, // observation
|
position, // observation
|
||||||
pos_obs_var, // observation variance
|
pos_obs_var, // observation variance
|
||||||
math::max(_params.gps_pos_innov_gate, 1.f), // innovation gate
|
Vector2f(_state.pos) - position, // innovation
|
||||||
aid_src);
|
Vector2f(getStateVariance<State::pos>()) + pos_obs_var, // innovation variance
|
||||||
|
math::max(_params.gps_pos_innov_gate, 1.f)); // innovation gate
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::controlGnssYawEstimator(estimator_aid_source3d_s &aid_src_vel)
|
void Ekf::controlGnssYawEstimator(estimator_aid_source3d_s &aid_src_vel)
|
||||||
@ -289,7 +305,8 @@ void Ekf::resetVelocityToGnss(estimator_aid_source3d_s &aid_src)
|
|||||||
{
|
{
|
||||||
_information_events.flags.reset_vel_to_gps = true;
|
_information_events.flags.reset_vel_to_gps = true;
|
||||||
resetVelocityTo(Vector3f(aid_src.observation), Vector3f(aid_src.observation_variance));
|
resetVelocityTo(Vector3f(aid_src.observation), Vector3f(aid_src.observation_variance));
|
||||||
aid_src.time_last_fuse = _time_delayed_us;
|
|
||||||
|
resetAidSourceStatusZeroInnovation(aid_src);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::resetHorizontalPositionToGnss(estimator_aid_source2d_s &aid_src)
|
void Ekf::resetHorizontalPositionToGnss(estimator_aid_source2d_s &aid_src)
|
||||||
@ -297,7 +314,8 @@ void Ekf::resetHorizontalPositionToGnss(estimator_aid_source2d_s &aid_src)
|
|||||||
_information_events.flags.reset_pos_to_gps = true;
|
_information_events.flags.reset_pos_to_gps = true;
|
||||||
resetHorizontalPositionTo(Vector2f(aid_src.observation), Vector2f(aid_src.observation_variance));
|
resetHorizontalPositionTo(Vector2f(aid_src.observation), Vector2f(aid_src.observation_variance));
|
||||||
_gpos_origin_eph = 0.f; // The uncertainty of the global origin is now contained in the local position uncertainty
|
_gpos_origin_eph = 0.f; // The uncertainty of the global origin is now contained in the local position uncertainty
|
||||||
aid_src.time_last_fuse = _time_delayed_us;
|
|
||||||
|
resetAidSourceStatusZeroInnovation(aid_src);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Ekf::shouldResetGpsFusion() const
|
bool Ekf::shouldResetGpsFusion() const
|
||||||
@ -342,12 +360,12 @@ void Ekf::controlGpsYawFusion(const gnssSample &gps_sample)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGpsYaw(gps_sample);
|
|
||||||
|
|
||||||
const bool is_new_data_available = PX4_ISFINITE(gps_sample.yaw);
|
const bool is_new_data_available = PX4_ISFINITE(gps_sample.yaw);
|
||||||
|
|
||||||
if (is_new_data_available) {
|
if (is_new_data_available) {
|
||||||
|
|
||||||
|
updateGpsYaw(gps_sample);
|
||||||
|
|
||||||
const bool continuing_conditions_passing = _control_status.flags.tilt_align;
|
const bool continuing_conditions_passing = _control_status.flags.tilt_align;
|
||||||
|
|
||||||
const bool is_gps_yaw_data_intermittent = !isNewestSampleRecent(_time_last_gps_yaw_buffer_push,
|
const bool is_gps_yaw_data_intermittent = !isNewestSampleRecent(_time_last_gps_yaw_buffer_push,
|
||||||
@ -392,7 +410,9 @@ void Ekf::controlGpsYawFusion(const gnssSample &gps_sample)
|
|||||||
|
|
||||||
// Reset before starting the fusion
|
// Reset before starting the fusion
|
||||||
if (resetYawToGps(gps_sample.yaw, gps_sample.yaw_offset)) {
|
if (resetYawToGps(gps_sample.yaw, gps_sample.yaw_offset)) {
|
||||||
_aid_src_gnss_yaw.time_last_fuse = _time_delayed_us;
|
|
||||||
|
resetAidSourceStatusZeroInnovation(_aid_src_gnss_yaw);
|
||||||
|
|
||||||
_control_status.flags.gps_yaw = true;
|
_control_status.flags.gps_yaw = true;
|
||||||
_control_status.flags.yaw_align = true;
|
_control_status.flags.yaw_align = true;
|
||||||
}
|
}
|
||||||
@ -422,7 +442,6 @@ void Ekf::stopGpsYawFusion()
|
|||||||
if (_control_status.flags.gps_yaw) {
|
if (_control_status.flags.gps_yaw) {
|
||||||
|
|
||||||
_control_status.flags.gps_yaw = false;
|
_control_status.flags.gps_yaw = false;
|
||||||
resetEstimatorAidStatus(_aid_src_gnss_yaw);
|
|
||||||
|
|
||||||
ECL_INFO("stopping GPS yaw fusion");
|
ECL_INFO("stopping GPS yaw fusion");
|
||||||
}
|
}
|
||||||
@ -433,8 +452,7 @@ void Ekf::stopGpsFusion()
|
|||||||
{
|
{
|
||||||
if (_control_status.flags.gps) {
|
if (_control_status.flags.gps) {
|
||||||
ECL_INFO("stopping GPS position and velocity fusion");
|
ECL_INFO("stopping GPS position and velocity fusion");
|
||||||
resetEstimatorAidStatus(_aid_src_gnss_pos);
|
|
||||||
resetEstimatorAidStatus(_aid_src_gnss_vel);
|
|
||||||
_last_gps_fail_us = 0;
|
_last_gps_fail_us = 0;
|
||||||
_last_gps_pass_us = 0;
|
_last_gps_pass_us = 0;
|
||||||
|
|
||||||
|
|||||||
@ -48,13 +48,6 @@
|
|||||||
|
|
||||||
void Ekf::updateGpsYaw(const gnssSample &gps_sample)
|
void Ekf::updateGpsYaw(const gnssSample &gps_sample)
|
||||||
{
|
{
|
||||||
if (PX4_ISFINITE(gps_sample.yaw)) {
|
|
||||||
|
|
||||||
auto &gnss_yaw = _aid_src_gnss_yaw;
|
|
||||||
resetEstimatorAidStatus(gnss_yaw);
|
|
||||||
|
|
||||||
// initially populate for estimator_aid_src_gnss_yaw logging
|
|
||||||
|
|
||||||
// calculate the observed yaw angle of antenna array, converting a from body to antenna yaw measurement
|
// calculate the observed yaw angle of antenna array, converting a from body to antenna yaw measurement
|
||||||
const float measured_hdg = wrap_pi(gps_sample.yaw + gps_sample.yaw_offset);
|
const float measured_hdg = wrap_pi(gps_sample.yaw + gps_sample.yaw_offset);
|
||||||
|
|
||||||
@ -64,28 +57,24 @@ void Ekf::updateGpsYaw(const gnssSample &gps_sample)
|
|||||||
float heading_pred;
|
float heading_pred;
|
||||||
float heading_innov_var;
|
float heading_innov_var;
|
||||||
|
|
||||||
{
|
|
||||||
VectorState H;
|
VectorState H;
|
||||||
sym::ComputeGnssYawPredInnovVarAndH(_state.vector(), P, gps_sample.yaw_offset, R_YAW, FLT_EPSILON, &heading_pred, &heading_innov_var, &H);
|
sym::ComputeGnssYawPredInnovVarAndH(_state.vector(), P, gps_sample.yaw_offset, R_YAW, FLT_EPSILON,
|
||||||
}
|
&heading_pred, &heading_innov_var, &H);
|
||||||
|
|
||||||
gnss_yaw.observation = measured_hdg;
|
updateAidSourceStatus(_aid_src_gnss_yaw,
|
||||||
gnss_yaw.observation_variance = R_YAW;
|
gps_sample.time_us, // sample timestamp
|
||||||
gnss_yaw.innovation = wrap_pi(heading_pred - measured_hdg);
|
measured_hdg, // observation
|
||||||
gnss_yaw.innovation_variance = heading_innov_var;
|
R_YAW, // observation variance
|
||||||
|
wrap_pi(heading_pred - measured_hdg), // innovation
|
||||||
gnss_yaw.timestamp_sample = gps_sample.time_us;
|
heading_innov_var, // innovation variance
|
||||||
|
math::max(_params.heading_innov_gate, 1.f)); // innovation gate
|
||||||
const float innov_gate = math::max(_params.heading_innov_gate, 1.0f);
|
|
||||||
setEstimatorAidStatusTestRatio(gnss_yaw, innov_gate);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::fuseGpsYaw(float antenna_yaw_offset)
|
void Ekf::fuseGpsYaw(float antenna_yaw_offset)
|
||||||
{
|
{
|
||||||
auto &gnss_yaw = _aid_src_gnss_yaw;
|
auto &aid_src = _aid_src_gnss_yaw;
|
||||||
|
|
||||||
if (gnss_yaw.innovation_rejected) {
|
if (aid_src.innovation_rejected) {
|
||||||
_innov_check_fail_status.flags.reject_yaw = true;
|
_innov_check_fail_status.flags.reject_yaw = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -94,19 +83,17 @@ void Ekf::fuseGpsYaw(float antenna_yaw_offset)
|
|||||||
antenna_yaw_offset = 0.f;
|
antenna_yaw_offset = 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
VectorState H;
|
|
||||||
|
|
||||||
{
|
|
||||||
float heading_pred;
|
float heading_pred;
|
||||||
float heading_innov_var;
|
float heading_innov_var;
|
||||||
|
VectorState H;
|
||||||
|
|
||||||
// Note: we recompute innov and innov_var because it doesn't cost much more than just computing H
|
// Note: we recompute innov and innov_var because it doesn't cost much more than just computing H
|
||||||
// making a separate function just for H uses more flash space without reducing CPU load significantly
|
// making a separate function just for H uses more flash space without reducing CPU load significantly
|
||||||
sym::ComputeGnssYawPredInnovVarAndH(_state.vector(), P, antenna_yaw_offset, gnss_yaw.observation_variance, FLT_EPSILON, &heading_pred, &heading_innov_var, &H);
|
sym::ComputeGnssYawPredInnovVarAndH(_state.vector(), P, antenna_yaw_offset, aid_src.observation_variance, FLT_EPSILON,
|
||||||
}
|
&heading_pred, &heading_innov_var, &H);
|
||||||
|
|
||||||
// check if the innovation variance calculation is badly conditioned
|
// check if the innovation variance calculation is badly conditioned
|
||||||
if (gnss_yaw.innovation_variance < gnss_yaw.observation_variance) {
|
if (aid_src.innovation_variance < aid_src.observation_variance) {
|
||||||
// the innovation variance contribution from the state covariances is negative which means the covariance matrix is badly conditioned
|
// the innovation variance contribution from the state covariances is negative which means the covariance matrix is badly conditioned
|
||||||
_fault_status.flags.bad_hdg = true;
|
_fault_status.flags.bad_hdg = true;
|
||||||
|
|
||||||
@ -119,11 +106,9 @@ void Ekf::fuseGpsYaw(float antenna_yaw_offset)
|
|||||||
_fault_status.flags.bad_hdg = false;
|
_fault_status.flags.bad_hdg = false;
|
||||||
_innov_check_fail_status.flags.reject_yaw = false;
|
_innov_check_fail_status.flags.reject_yaw = false;
|
||||||
|
|
||||||
_gnss_yaw_signed_test_ratio_lpf.update(matrix::sign(gnss_yaw.innovation) * gnss_yaw.test_ratio);
|
if ((fabsf(aid_src.test_ratio_filtered) > 0.2f)
|
||||||
|
&& !_control_status.flags.in_air && isTimedOut(aid_src.time_last_fuse, (uint64_t)1e6)
|
||||||
if ((fabsf(_gnss_yaw_signed_test_ratio_lpf.getState()) > 0.2f)
|
) {
|
||||||
&& !_control_status.flags.in_air && isTimedOut(gnss_yaw.time_last_fuse, (uint64_t)1e6)) {
|
|
||||||
|
|
||||||
// A constant large signed test ratio is a sign of wrong gyro bias
|
// A constant large signed test ratio is a sign of wrong gyro bias
|
||||||
// Reset the yaw gyro variance to converge faster and avoid
|
// Reset the yaw gyro variance to converge faster and avoid
|
||||||
// being stuck on a previous bad estimate
|
// being stuck on a previous bad estimate
|
||||||
@ -132,15 +117,15 @@ void Ekf::fuseGpsYaw(float antenna_yaw_offset)
|
|||||||
|
|
||||||
// calculate the Kalman gains
|
// calculate the Kalman gains
|
||||||
// only calculate gains for states we are using
|
// only calculate gains for states we are using
|
||||||
VectorState Kfusion = P * H / gnss_yaw.innovation_variance;
|
VectorState Kfusion = P * H / aid_src.innovation_variance;
|
||||||
|
|
||||||
const bool is_fused = measurementUpdate(Kfusion, H, gnss_yaw.observation_variance, gnss_yaw.innovation);
|
const bool is_fused = measurementUpdate(Kfusion, H, aid_src.observation_variance, aid_src.innovation);
|
||||||
_fault_status.flags.bad_hdg = !is_fused;
|
_fault_status.flags.bad_hdg = !is_fused;
|
||||||
gnss_yaw.fused = is_fused;
|
aid_src.fused = is_fused;
|
||||||
|
|
||||||
if (is_fused) {
|
if (is_fused) {
|
||||||
_time_last_heading_fuse = _time_delayed_us;
|
_time_last_heading_fuse = _time_delayed_us;
|
||||||
gnss_yaw.time_last_fuse = _time_delayed_us;
|
aid_src.time_last_fuse = _time_delayed_us;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,8 +146,5 @@ bool Ekf::resetYawToGps(const float gnss_yaw, const float gnss_yaw_offset)
|
|||||||
const float yaw_variance = sq(fmaxf(_params.gps_heading_noise, 1.e-2f));
|
const float yaw_variance = sq(fmaxf(_params.gps_heading_noise, 1.e-2f));
|
||||||
resetQuatStateYaw(measured_yaw, yaw_variance);
|
resetQuatStateYaw(measured_yaw, yaw_variance);
|
||||||
|
|
||||||
_aid_src_gnss_yaw.time_last_fuse = _time_delayed_us;
|
|
||||||
_gnss_yaw_signed_test_ratio_lpf.reset(0.f);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,19 +69,13 @@ void Ekf::controlGravityFusion(const imuSample &imu)
|
|||||||
sym::ComputeGravityXyzInnovVarAndHx(state_vector, P, measurement_var, &innovation_variance, &H);
|
sym::ComputeGravityXyzInnovVarAndHx(state_vector, P, measurement_var, &innovation_variance, &H);
|
||||||
|
|
||||||
// fill estimator aid source status
|
// fill estimator aid source status
|
||||||
resetEstimatorAidStatus(_aid_src_gravity);
|
updateAidSourceStatus(_aid_src_gravity,
|
||||||
_aid_src_gravity.timestamp_sample = imu.time_us;
|
imu.time_us, // sample timestamp
|
||||||
measurement.copyTo(_aid_src_gravity.observation);
|
measurement, // observation
|
||||||
|
Vector3f{measurement_var, measurement_var, measurement_var}, // observation variance
|
||||||
for (auto &var : _aid_src_gravity.observation_variance) {
|
innovation, // innovation
|
||||||
var = measurement_var;
|
innovation_variance, // innovation variance
|
||||||
}
|
0.25f); // innovation gate
|
||||||
|
|
||||||
innovation.copyTo(_aid_src_gravity.innovation);
|
|
||||||
innovation_variance.copyTo(_aid_src_gravity.innovation_variance);
|
|
||||||
|
|
||||||
float innovation_gate = 0.25f;
|
|
||||||
setEstimatorAidStatusTestRatio(_aid_src_gravity, innovation_gate);
|
|
||||||
|
|
||||||
bool fused[3] {false, false, false};
|
bool fused[3] {false, false, false};
|
||||||
|
|
||||||
|
|||||||
@ -97,10 +97,6 @@ void Ekf::controlMagFusion()
|
|||||||
_fault_status.flags.bad_mag_y = false;
|
_fault_status.flags.bad_mag_y = false;
|
||||||
_fault_status.flags.bad_mag_z = false;
|
_fault_status.flags.bad_mag_z = false;
|
||||||
|
|
||||||
|
|
||||||
resetEstimatorAidStatus(aid_src);
|
|
||||||
aid_src.timestamp_sample = mag_sample.time_us;
|
|
||||||
|
|
||||||
// XYZ Measurement uncertainty. Need to consider timing errors for fast rotations
|
// XYZ Measurement uncertainty. Need to consider timing errors for fast rotations
|
||||||
const float R_MAG = math::max(sq(_params.mag_noise), sq(0.01f));
|
const float R_MAG = math::max(sq(_params.mag_noise), sq(0.01f));
|
||||||
|
|
||||||
@ -112,15 +108,13 @@ void Ekf::controlMagFusion()
|
|||||||
VectorState H;
|
VectorState H;
|
||||||
sym::ComputeMagInnovInnovVarAndHx(_state.vector(), P, mag_sample.mag, R_MAG, FLT_EPSILON, &mag_innov, &innov_var, &H);
|
sym::ComputeMagInnovInnovVarAndHx(_state.vector(), P, mag_sample.mag, R_MAG, FLT_EPSILON, &mag_innov, &innov_var, &H);
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
updateAidSourceStatus(aid_src,
|
||||||
aid_src.observation[i] = mag_sample.mag(i);
|
mag_sample.time_us, // sample timestamp
|
||||||
aid_src.observation_variance[i] = R_MAG;
|
mag_sample.mag, // observation
|
||||||
aid_src.innovation[i] = mag_innov(i);
|
Vector3f(R_MAG, R_MAG, R_MAG), // observation variance
|
||||||
aid_src.innovation_variance[i] = innov_var(i);
|
mag_innov, // innovation
|
||||||
}
|
innov_var, // innovation variance
|
||||||
|
math::max(_params.mag_innov_gate, 1.f)); // innovation gate
|
||||||
const float innov_gate = math::max(_params.mag_innov_gate, 1.f);
|
|
||||||
setEstimatorAidStatusTestRatio(aid_src, innov_gate);
|
|
||||||
|
|
||||||
// Perform an innovation consistency check and report the result
|
// Perform an innovation consistency check and report the result
|
||||||
_innov_check_fail_status.flags.reject_mag_x = (aid_src.test_ratio[0] > 1.f);
|
_innov_check_fail_status.flags.reject_mag_x = (aid_src.test_ratio[0] > 1.f);
|
||||||
|
|||||||
@ -164,8 +164,6 @@ void Ekf::stopFlowFusion()
|
|||||||
if (_control_status.flags.opt_flow) {
|
if (_control_status.flags.opt_flow) {
|
||||||
ECL_INFO("stopping optical flow fusion");
|
ECL_INFO("stopping optical flow fusion");
|
||||||
_control_status.flags.opt_flow = false;
|
_control_status.flags.opt_flow = false;
|
||||||
|
|
||||||
resetEstimatorAidStatus(_aid_src_optical_flow);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -50,9 +50,6 @@
|
|||||||
|
|
||||||
void Ekf::updateOptFlow(estimator_aid_source2d_s &aid_src)
|
void Ekf::updateOptFlow(estimator_aid_source2d_s &aid_src)
|
||||||
{
|
{
|
||||||
resetEstimatorAidStatus(aid_src);
|
|
||||||
aid_src.timestamp_sample = _flow_sample_delayed.time_us;
|
|
||||||
|
|
||||||
const Vector2f vel_body = predictFlowVelBody();
|
const Vector2f vel_body = predictFlowVelBody();
|
||||||
const float range = predictFlowRange();
|
const float range = predictFlowRange();
|
||||||
|
|
||||||
@ -66,24 +63,26 @@ void Ekf::updateOptFlow(estimator_aid_source2d_s &aid_src)
|
|||||||
_flow_vel_body(1) = opt_flow_rate(0) * range;
|
_flow_vel_body(1) = opt_flow_rate(0) * range;
|
||||||
_flow_vel_ne = Vector2f(_R_to_earth * Vector3f(_flow_vel_body(0), _flow_vel_body(1), 0.f));
|
_flow_vel_ne = Vector2f(_R_to_earth * Vector3f(_flow_vel_body(0), _flow_vel_body(1), 0.f));
|
||||||
|
|
||||||
aid_src.observation[0] = opt_flow_rate(0); // flow around the X axis
|
Vector2f innovation{
|
||||||
aid_src.observation[1] = opt_flow_rate(1); // flow around the Y axis
|
(vel_body(1) / range) - opt_flow_rate(0),
|
||||||
|
(-vel_body(0) / range) - opt_flow_rate(1)
|
||||||
aid_src.innovation[0] = (vel_body(1) / range) - aid_src.observation[0];
|
};
|
||||||
aid_src.innovation[1] = (-vel_body(0) / range) - aid_src.observation[1];
|
|
||||||
|
|
||||||
// calculate the optical flow observation variance
|
// calculate the optical flow observation variance
|
||||||
const float R_LOS = calcOptFlowMeasVar(_flow_sample_delayed);
|
const float R_LOS = calcOptFlowMeasVar(_flow_sample_delayed);
|
||||||
aid_src.observation_variance[0] = R_LOS;
|
|
||||||
aid_src.observation_variance[1] = R_LOS;
|
|
||||||
|
|
||||||
Vector2f innov_var;
|
Vector2f innov_var;
|
||||||
VectorState H;
|
VectorState H;
|
||||||
sym::ComputeFlowXyInnovVarAndHx(_state.vector(), P, range, R_LOS, FLT_EPSILON, &innov_var, &H);
|
sym::ComputeFlowXyInnovVarAndHx(_state.vector(), P, range, R_LOS, FLT_EPSILON, &innov_var, &H);
|
||||||
innov_var.copyTo(aid_src.innovation_variance);
|
|
||||||
|
|
||||||
// run the innovation consistency check and record result
|
// run the innovation consistency check and record result
|
||||||
setEstimatorAidStatusTestRatio(aid_src, math::max(_params.flow_innov_gate, 1.f));
|
updateAidSourceStatus(aid_src,
|
||||||
|
_flow_sample_delayed.time_us, // sample timestamp
|
||||||
|
opt_flow_rate, // observation
|
||||||
|
Vector2f{R_LOS, R_LOS}, // observation variance
|
||||||
|
innovation, // innovation
|
||||||
|
innov_var, // innovation variance
|
||||||
|
math::max(_params.flow_innov_gate, 1.f)); // innovation gate
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::fuseOptFlow()
|
void Ekf::fuseOptFlow()
|
||||||
@ -101,17 +100,13 @@ void Ekf::fuseOptFlow()
|
|||||||
sym::ComputeFlowXyInnovVarAndHx(state_vector, P, range, R_LOS, FLT_EPSILON, &innov_var, &H);
|
sym::ComputeFlowXyInnovVarAndHx(state_vector, P, range, R_LOS, FLT_EPSILON, &innov_var, &H);
|
||||||
innov_var.copyTo(_aid_src_optical_flow.innovation_variance);
|
innov_var.copyTo(_aid_src_optical_flow.innovation_variance);
|
||||||
|
|
||||||
if ((_aid_src_optical_flow.innovation_variance[0] < R_LOS)
|
if ((innov_var(0) < R_LOS) || (innov_var(1) < R_LOS)) {
|
||||||
|| (_aid_src_optical_flow.innovation_variance[1] < R_LOS)) {
|
|
||||||
// we need to reinitialise the covariance matrix and abort this fusion step
|
// we need to reinitialise the covariance matrix and abort this fusion step
|
||||||
ECL_ERR("Opt flow error - covariance reset");
|
ECL_ERR("Opt flow error - covariance reset");
|
||||||
initialiseCovariance();
|
initialiseCovariance();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// run the innovation consistency check and record result
|
|
||||||
setEstimatorAidStatusTestRatio(_aid_src_optical_flow, math::max(_params.flow_innov_gate, 1.f));
|
|
||||||
|
|
||||||
_innov_check_fail_status.flags.reject_optflow_X = (_aid_src_optical_flow.test_ratio[0] > 1.f);
|
_innov_check_fail_status.flags.reject_optflow_X = (_aid_src_optical_flow.test_ratio[0] > 1.f);
|
||||||
_innov_check_fail_status.flags.reject_optflow_Y = (_aid_src_optical_flow.test_ratio[1] > 1.f);
|
_innov_check_fail_status.flags.reject_optflow_Y = (_aid_src_optical_flow.test_ratio[1] > 1.f);
|
||||||
|
|
||||||
|
|||||||
@ -102,16 +102,14 @@ void Ekf::controlRangeHeightFusion()
|
|||||||
const float measurement = math::max(_range_sensor.getDistBottom(), _params.rng_gnd_clearance);
|
const float measurement = math::max(_range_sensor.getDistBottom(), _params.rng_gnd_clearance);
|
||||||
const float measurement_var = sq(_params.range_noise) + sq(_params.range_noise_scaler * _range_sensor.getDistBottom());
|
const float measurement_var = sq(_params.range_noise) + sq(_params.range_noise_scaler * _range_sensor.getDistBottom());
|
||||||
|
|
||||||
const float innov_gate = math::max(_params.range_innov_gate, 1.f);
|
|
||||||
|
|
||||||
const bool measurement_valid = PX4_ISFINITE(measurement) && PX4_ISFINITE(measurement_var);
|
const bool measurement_valid = PX4_ISFINITE(measurement) && PX4_ISFINITE(measurement_var);
|
||||||
|
|
||||||
// vertical position innovation - baro measurement has opposite sign to earth z axis
|
// vertical position innovation - baro measurement has opposite sign to earth z axis
|
||||||
updateVerticalPositionAidSrcStatus(_range_sensor.getSampleAddress()->time_us,
|
updateVerticalPositionAidStatus(aid_src,
|
||||||
-(measurement - bias_est.getBias()),
|
_range_sensor.getSampleAddress()->time_us, // sample timestamp
|
||||||
measurement_var + bias_est.getBiasVar(),
|
-(measurement - bias_est.getBias()), // observation
|
||||||
innov_gate,
|
measurement_var + bias_est.getBiasVar(), // observation variance
|
||||||
aid_src);
|
math::max(_params.range_innov_gate, 1.f)); // innovation gate
|
||||||
|
|
||||||
// update the bias estimator before updating the main filter but after
|
// update the bias estimator before updating the main filter but after
|
||||||
// using its current state to compute the vertical position innovation
|
// using its current state to compute the vertical position innovation
|
||||||
@ -259,7 +257,6 @@ void Ekf::stopRngHgtFusion()
|
|||||||
}
|
}
|
||||||
|
|
||||||
_rng_hgt_b_est.setFusionInactive();
|
_rng_hgt_b_est.setFusionInactive();
|
||||||
resetEstimatorAidStatus(_aid_src_rng_hgt);
|
|
||||||
|
|
||||||
_control_status.flags.rng_hgt = false;
|
_control_status.flags.rng_hgt = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,8 +49,10 @@
|
|||||||
|
|
||||||
void Ekf::controlBetaFusion(const imuSample &imu_delayed)
|
void Ekf::controlBetaFusion(const imuSample &imu_delayed)
|
||||||
{
|
{
|
||||||
_control_status.flags.fuse_beta = _params.beta_fusion_enabled && _control_status.flags.fixed_wing
|
_control_status.flags.fuse_beta = _params.beta_fusion_enabled
|
||||||
&& _control_status.flags.in_air && !_control_status.flags.fake_pos;
|
&& _control_status.flags.fixed_wing
|
||||||
|
&& _control_status.flags.in_air
|
||||||
|
&& !_control_status.flags.fake_pos;
|
||||||
|
|
||||||
if (_control_status.flags.fuse_beta) {
|
if (_control_status.flags.fuse_beta) {
|
||||||
|
|
||||||
@ -76,26 +78,22 @@ void Ekf::controlBetaFusion(const imuSample &imu_delayed)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::updateSideslip(estimator_aid_source1d_s &sideslip) const
|
void Ekf::updateSideslip(estimator_aid_source1d_s &aid_src) const
|
||||||
{
|
{
|
||||||
// reset flags
|
float observation = 0.f;
|
||||||
resetEstimatorAidStatus(sideslip);
|
const float R = math::max(sq(_params.beta_noise), sq(0.01f)); // observation noise variance
|
||||||
|
|
||||||
const float R = sq(_params.beta_noise); // observation noise variance
|
float innov;
|
||||||
|
float innov_var;
|
||||||
float innov = 0.f;
|
|
||||||
float innov_var = 0.f;
|
|
||||||
sym::ComputeSideslipInnovAndInnovVar(_state.vector(), P, R, FLT_EPSILON, &innov, &innov_var);
|
sym::ComputeSideslipInnovAndInnovVar(_state.vector(), P, R, FLT_EPSILON, &innov, &innov_var);
|
||||||
|
|
||||||
sideslip.observation = 0.f;
|
updateAidSourceStatus(aid_src,
|
||||||
sideslip.observation_variance = R;
|
_time_delayed_us, // sample timestamp
|
||||||
sideslip.innovation = innov;
|
observation, // observation
|
||||||
sideslip.innovation_variance = innov_var;
|
R, // observation variance
|
||||||
|
innov, // innovation
|
||||||
sideslip.timestamp_sample = _time_delayed_us;
|
innov_var, // innovation variance
|
||||||
|
math::max(_params.beta_innov_gate, 1.f)); // innovation gate
|
||||||
const float innov_gate = fmaxf(_params.beta_innov_gate, 1.f);
|
|
||||||
setEstimatorAidStatusTestRatio(sideslip, innov_gate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::fuseSideslip(estimator_aid_source1d_s &sideslip)
|
void Ekf::fuseSideslip(estimator_aid_source1d_s &sideslip)
|
||||||
@ -103,6 +101,7 @@ void Ekf::fuseSideslip(estimator_aid_source1d_s &sideslip)
|
|||||||
if (sideslip.innovation_rejected) {
|
if (sideslip.innovation_rejected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine if we need the sideslip fusion to correct states other than wind
|
// determine if we need the sideslip fusion to correct states other than wind
|
||||||
bool update_wind_only = !_control_status.flags.wind_dead_reckoning;
|
bool update_wind_only = !_control_status.flags.wind_dead_reckoning;
|
||||||
|
|
||||||
|
|||||||
@ -124,53 +124,6 @@ void Ekf::reset()
|
|||||||
_time_good_vert_accel = 0;
|
_time_good_vert_accel = 0;
|
||||||
_clip_counter = 0;
|
_clip_counter = 0;
|
||||||
|
|
||||||
#if defined(CONFIG_EKF2_BAROMETER)
|
|
||||||
resetEstimatorAidStatus(_aid_src_baro_hgt);
|
|
||||||
#endif // CONFIG_EKF2_BAROMETER
|
|
||||||
#if defined(CONFIG_EKF2_AIRSPEED)
|
|
||||||
resetEstimatorAidStatus(_aid_src_airspeed);
|
|
||||||
#endif // CONFIG_EKF2_AIRSPEED
|
|
||||||
#if defined(CONFIG_EKF2_SIDESLIP)
|
|
||||||
resetEstimatorAidStatus(_aid_src_sideslip);
|
|
||||||
#endif // CONFIG_EKF2_SIDESLIP
|
|
||||||
|
|
||||||
resetEstimatorAidStatus(_aid_src_fake_pos);
|
|
||||||
resetEstimatorAidStatus(_aid_src_fake_hgt);
|
|
||||||
|
|
||||||
#if defined(CONFIG_EKF2_EXTERNAL_VISION)
|
|
||||||
resetEstimatorAidStatus(_aid_src_ev_hgt);
|
|
||||||
resetEstimatorAidStatus(_aid_src_ev_pos);
|
|
||||||
resetEstimatorAidStatus(_aid_src_ev_vel);
|
|
||||||
resetEstimatorAidStatus(_aid_src_ev_yaw);
|
|
||||||
#endif // CONFIG_EKF2_EXTERNAL_VISION
|
|
||||||
|
|
||||||
#if defined(CONFIG_EKF2_GNSS)
|
|
||||||
resetEstimatorAidStatus(_aid_src_gnss_hgt);
|
|
||||||
resetEstimatorAidStatus(_aid_src_gnss_pos);
|
|
||||||
resetEstimatorAidStatus(_aid_src_gnss_vel);
|
|
||||||
|
|
||||||
# if defined(CONFIG_EKF2_GNSS_YAW)
|
|
||||||
resetEstimatorAidStatus(_aid_src_gnss_yaw);
|
|
||||||
# endif // CONFIG_EKF2_GNSS_YAW
|
|
||||||
#endif // CONFIG_EKF2_GNSS
|
|
||||||
|
|
||||||
#if defined(CONFIG_EKF2_MAGNETOMETER)
|
|
||||||
resetEstimatorAidStatus(_aid_src_mag);
|
|
||||||
#endif // CONFIG_EKF2_MAGNETOMETER
|
|
||||||
|
|
||||||
#if defined(CONFIG_EKF2_AUXVEL)
|
|
||||||
resetEstimatorAidStatus(_aid_src_aux_vel);
|
|
||||||
#endif // CONFIG_EKF2_AUXVEL
|
|
||||||
|
|
||||||
#if defined(CONFIG_EKF2_OPTICAL_FLOW)
|
|
||||||
resetEstimatorAidStatus(_aid_src_optical_flow);
|
|
||||||
resetEstimatorAidStatus(_aid_src_terrain_optical_flow);
|
|
||||||
#endif // CONFIG_EKF2_OPTICAL_FLOW
|
|
||||||
|
|
||||||
#if defined(CONFIG_EKF2_RANGE_FINDER)
|
|
||||||
resetEstimatorAidStatus(_aid_src_rng_hgt);
|
|
||||||
#endif // CONFIG_EKF2_RANGE_FINDER
|
|
||||||
|
|
||||||
_zero_velocity_update.reset();
|
_zero_velocity_update.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,7 +285,6 @@ void Ekf::predictState(const imuSample &imu_delayed)
|
|||||||
|
|
||||||
void Ekf::resetGlobalPosToExternalObservation(double lat_deg, double lon_deg, float accuracy, uint64_t timestamp_observation)
|
void Ekf::resetGlobalPosToExternalObservation(double lat_deg, double lon_deg, float accuracy, uint64_t timestamp_observation)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!_pos_ref.isInitialized()) {
|
if (!_pos_ref.isInitialized()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -344,7 +296,10 @@ void Ekf::resetGlobalPosToExternalObservation(double lat_deg, double lon_deg, fl
|
|||||||
|
|
||||||
Vector2f pos_corrected = _pos_ref.project(lat_deg, lon_deg) + _state.vel.xy() * dt;
|
Vector2f pos_corrected = _pos_ref.project(lat_deg, lon_deg) + _state.vel.xy() * dt;
|
||||||
|
|
||||||
resetHorizontalPositionToExternal(pos_corrected, math::max(accuracy, FLT_EPSILON));
|
resetHorizontalPositionTo(pos_corrected, sq(math::max(accuracy, 0.01f)));
|
||||||
|
|
||||||
|
ECL_INFO("reset position to external observation");
|
||||||
|
_information_events.flags.reset_pos_to_ext_obs = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::updateParameters()
|
void Ekf::updateParameters()
|
||||||
|
|||||||
@ -641,8 +641,6 @@ private:
|
|||||||
estimator_aid_source3d_s _aid_src_ev_vel{};
|
estimator_aid_source3d_s _aid_src_ev_vel{};
|
||||||
estimator_aid_source1d_s _aid_src_ev_yaw{};
|
estimator_aid_source1d_s _aid_src_ev_yaw{};
|
||||||
|
|
||||||
float _ev_yaw_pred_prev{}; ///< previous value of yaw state used by odometry fusion (m)
|
|
||||||
|
|
||||||
uint8_t _nb_ev_pos_reset_available{0};
|
uint8_t _nb_ev_pos_reset_available{0};
|
||||||
uint8_t _nb_ev_vel_reset_available{0};
|
uint8_t _nb_ev_vel_reset_available{0};
|
||||||
uint8_t _nb_ev_yaw_reset_available{0};
|
uint8_t _nb_ev_yaw_reset_available{0};
|
||||||
@ -747,7 +745,6 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update quaternion states and covariances using an innovation, observation variance and Jacobian vector
|
// update quaternion states and covariances using an innovation, observation variance and Jacobian vector
|
||||||
bool fuseYaw(estimator_aid_source1d_s &aid_src_status);
|
|
||||||
bool fuseYaw(estimator_aid_source1d_s &aid_src_status, const VectorState &H_YAW);
|
bool fuseYaw(estimator_aid_source1d_s &aid_src_status, const VectorState &H_YAW);
|
||||||
void computeYawInnovVarAndH(float variance, float &innovation_variance, VectorState &H_YAW) const;
|
void computeYawInnovVarAndH(float variance, float &innovation_variance, VectorState &H_YAW) const;
|
||||||
|
|
||||||
@ -802,8 +799,9 @@ private:
|
|||||||
void resetHorizontalVelocityToZero();
|
void resetHorizontalVelocityToZero();
|
||||||
|
|
||||||
void resetVerticalVelocityTo(float new_vert_vel, float new_vert_vel_var);
|
void resetVerticalVelocityTo(float new_vert_vel, float new_vert_vel_var);
|
||||||
|
|
||||||
|
|
||||||
void resetHorizontalPositionToLastKnown();
|
void resetHorizontalPositionToLastKnown();
|
||||||
void resetHorizontalPositionToExternal(const Vector2f &new_horiz_pos, float horiz_accuracy);
|
|
||||||
|
|
||||||
void resetHorizontalPositionTo(const Vector2f &new_horz_pos, const Vector2f &new_horz_pos_var);
|
void resetHorizontalPositionTo(const Vector2f &new_horz_pos, const Vector2f &new_horz_pos_var);
|
||||||
void resetHorizontalPositionTo(const Vector2f &new_horz_pos, const float pos_var = NAN) { resetHorizontalPositionTo(new_horz_pos, Vector2f(pos_var, pos_var)); }
|
void resetHorizontalPositionTo(const Vector2f &new_horz_pos, const float pos_var = NAN) { resetHorizontalPositionTo(new_horz_pos, Vector2f(pos_var, pos_var)); }
|
||||||
@ -815,20 +813,15 @@ private:
|
|||||||
void resetVerticalVelocityToZero();
|
void resetVerticalVelocityToZero();
|
||||||
|
|
||||||
// horizontal and vertical position aid source
|
// horizontal and vertical position aid source
|
||||||
void updateHorizontalPositionAidSrcStatus(const uint64_t &time_us, const Vector2f &obs, const Vector2f &obs_var, const float innov_gate, estimator_aid_source2d_s &aid_src) const;
|
void updateVerticalPositionAidStatus(estimator_aid_source1d_s &aid_src, const uint64_t &time_us, const float observation, const float observation_variance, const float innovation_gate = 1.f) const;
|
||||||
void updateVerticalPositionAidSrcStatus(const uint64_t &time_us, const float obs, const float obs_var, const float innov_gate, estimator_aid_source1d_s &aid_src) const;
|
|
||||||
|
|
||||||
// 2d & 3d velocity aid source
|
|
||||||
void updateHorizontalVelocityAidSrcStatus(const uint64_t &time_us, const Vector2f &obs, const Vector2f &obs_var, const float innov_gate, estimator_aid_source2d_s &aid_src) const;
|
|
||||||
void updateVelocityAidSrcStatus(const uint64_t &time_us, const Vector3f &obs, const Vector3f &obs_var, const float innov_gate, estimator_aid_source3d_s &aid_src) const;
|
|
||||||
|
|
||||||
// horizontal and vertical position fusion
|
// horizontal and vertical position fusion
|
||||||
void fuseHorizontalPosition(estimator_aid_source2d_s &pos_aid_src);
|
bool fuseHorizontalPosition(estimator_aid_source2d_s &pos_aid_src);
|
||||||
void fuseVerticalPosition(estimator_aid_source1d_s &hgt_aid_src);
|
bool fuseVerticalPosition(estimator_aid_source1d_s &hgt_aid_src);
|
||||||
|
|
||||||
// 2d & 3d velocity fusion
|
// 2d & 3d velocity fusion
|
||||||
void fuseHorizontalVelocity(estimator_aid_source2d_s &vel_aid_src);
|
bool fuseHorizontalVelocity(estimator_aid_source2d_s &vel_aid_src);
|
||||||
void fuseVelocity(estimator_aid_source3d_s &vel_aid_src);
|
bool fuseVelocity(estimator_aid_source3d_s &vel_aid_src);
|
||||||
|
|
||||||
#if defined(CONFIG_EKF2_TERRAIN)
|
#if defined(CONFIG_EKF2_TERRAIN)
|
||||||
// terrain vertical position estimator
|
// terrain vertical position estimator
|
||||||
@ -1130,88 +1123,200 @@ private:
|
|||||||
bool _ev_q_error_initialized{false};
|
bool _ev_q_error_initialized{false};
|
||||||
#endif // CONFIG_EKF2_EXTERNAL_VISION
|
#endif // CONFIG_EKF2_EXTERNAL_VISION
|
||||||
|
|
||||||
void resetEstimatorAidStatus(estimator_aid_source1d_s &status) const
|
// state was reset to aid source, keep observation and update all other fields appropriately (zero innovation, etc)
|
||||||
|
void resetAidSourceStatusZeroInnovation(estimator_aid_source1d_s &status) const
|
||||||
{
|
{
|
||||||
// only bother resetting if timestamp_sample is set
|
status.time_last_fuse = _time_delayed_us;
|
||||||
if (status.timestamp_sample != 0) {
|
|
||||||
status.timestamp_sample = 0;
|
|
||||||
|
|
||||||
// preserve status.time_last_fuse
|
status.innovation = 0.f;
|
||||||
|
status.innovation_filtered = 0.f;
|
||||||
|
status.innovation_variance = status.observation_variance;
|
||||||
|
|
||||||
status.observation = 0;
|
status.test_ratio = 0.f;
|
||||||
status.observation_variance = 0;
|
status.test_ratio_filtered = 0.f;
|
||||||
|
|
||||||
status.innovation = 0;
|
status.innovation_rejected = false;
|
||||||
status.innovation_variance = 0;
|
status.fused = true;
|
||||||
status.test_ratio = INFINITY;
|
|
||||||
|
|
||||||
status.innovation_rejected = true;
|
|
||||||
status.fused = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
// helper used for populating and filtering estimator aid source struct for logging
|
||||||
void resetEstimatorAidStatus(T &status) const
|
void updateAidSourceStatus(estimator_aid_source1d_s &status, const uint64_t ×tamp_sample,
|
||||||
{
|
const float &observation, const float &observation_variance,
|
||||||
// only bother resetting if timestamp_sample is set
|
const float &innovation, const float &innovation_variance,
|
||||||
if (status.timestamp_sample != 0) {
|
float innovation_gate = 1.f) const
|
||||||
status.timestamp_sample = 0;
|
|
||||||
|
|
||||||
// preserve status.time_last_fuse
|
|
||||||
|
|
||||||
for (size_t i = 0; i < (sizeof(status.observation) / sizeof(status.observation[0])); i++) {
|
|
||||||
status.observation[i] = 0;
|
|
||||||
status.observation_variance[i] = 0;
|
|
||||||
|
|
||||||
status.innovation[i] = 0;
|
|
||||||
status.innovation_variance[i] = 0;
|
|
||||||
status.test_ratio[i] = INFINITY;
|
|
||||||
}
|
|
||||||
|
|
||||||
status.innovation_rejected = true;
|
|
||||||
status.fused = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setEstimatorAidStatusTestRatio(estimator_aid_source1d_s &status, float innovation_gate) const
|
|
||||||
{
|
|
||||||
if (PX4_ISFINITE(status.innovation)
|
|
||||||
&& PX4_ISFINITE(status.innovation_variance)
|
|
||||||
&& (status.innovation_variance > 0.f)
|
|
||||||
) {
|
|
||||||
status.test_ratio = sq(status.innovation) / (sq(innovation_gate) * status.innovation_variance);
|
|
||||||
status.innovation_rejected = (status.test_ratio > 1.f);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
status.test_ratio = INFINITY;
|
|
||||||
status.innovation_rejected = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void setEstimatorAidStatusTestRatio(T &status, float innovation_gate) const
|
|
||||||
{
|
{
|
||||||
bool innovation_rejected = false;
|
bool innovation_rejected = false;
|
||||||
|
|
||||||
for (size_t i = 0; i < (sizeof(status.test_ratio) / sizeof(status.test_ratio[0])); i++) {
|
const float test_ratio = sq(innovation) / (sq(innovation_gate) * innovation_variance);
|
||||||
if (PX4_ISFINITE(status.innovation[i])
|
|
||||||
&& PX4_ISFINITE(status.innovation_variance[i])
|
|
||||||
&& (status.innovation_variance[i] > 0.f)
|
|
||||||
) {
|
|
||||||
status.test_ratio[i] = sq(status.innovation[i]) / (sq(innovation_gate) * status.innovation_variance[i]);
|
|
||||||
|
|
||||||
if (status.test_ratio[i] > 1.f) {
|
if ((status.timestamp_sample > 0) && (timestamp_sample > status.timestamp_sample)) {
|
||||||
innovation_rejected = true;
|
|
||||||
|
const float dt_s = math::constrain((timestamp_sample - status.timestamp_sample) * 1e-6f, 0.001f, 1.f);
|
||||||
|
|
||||||
|
static constexpr float tau = 0.5f;
|
||||||
|
const float alpha = math::constrain(dt_s / (dt_s + tau), 0.f, 1.f);
|
||||||
|
|
||||||
|
// test_ratio_filtered
|
||||||
|
if (PX4_ISFINITE(status.test_ratio_filtered)) {
|
||||||
|
status.test_ratio_filtered += alpha * (matrix::sign(innovation) * test_ratio - status.test_ratio_filtered);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// otherwise, init the filtered test ratio
|
||||||
|
status.test_ratio_filtered = test_ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
// innovation_filtered
|
||||||
|
if (PX4_ISFINITE(status.innovation_filtered)) {
|
||||||
|
status.innovation_filtered += alpha * (innovation - status.innovation_filtered);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// otherwise, init the filtered innovation
|
||||||
|
status.innovation_filtered = innovation;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// limit extremes in filtered values
|
||||||
|
static constexpr float kNormalizedInnovationLimit = 2.f;
|
||||||
|
static constexpr float kTestRatioLimit = sq(kNormalizedInnovationLimit);
|
||||||
|
|
||||||
|
if (test_ratio > kTestRatioLimit) {
|
||||||
|
|
||||||
|
status.test_ratio_filtered = math::constrain(status.test_ratio_filtered, -kTestRatioLimit, kTestRatioLimit);
|
||||||
|
|
||||||
|
const float innov_limit = kNormalizedInnovationLimit * innovation_gate * sqrtf(innovation_variance);
|
||||||
|
status.innovation_filtered = math::constrain(status.innovation_filtered, -innov_limit, innov_limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
status.test_ratio[i] = INFINITY;
|
// invalid timestamp_sample, reset
|
||||||
|
status.test_ratio_filtered = test_ratio;
|
||||||
|
status.innovation_filtered = innovation;
|
||||||
|
}
|
||||||
|
|
||||||
|
status.test_ratio = test_ratio;
|
||||||
|
|
||||||
|
status.observation = observation;
|
||||||
|
status.observation_variance = observation_variance;
|
||||||
|
|
||||||
|
status.innovation = innovation;
|
||||||
|
status.innovation_variance = innovation_variance;
|
||||||
|
|
||||||
|
if ((test_ratio > 1.f)
|
||||||
|
|| !PX4_ISFINITE(test_ratio)
|
||||||
|
|| !PX4_ISFINITE(status.innovation)
|
||||||
|
|| !PX4_ISFINITE(status.innovation_variance)
|
||||||
|
) {
|
||||||
|
innovation_rejected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
status.timestamp_sample = timestamp_sample;
|
||||||
|
|
||||||
|
// if any of the innovations are rejected, then the overall innovation is rejected
|
||||||
|
status.innovation_rejected = innovation_rejected;
|
||||||
|
|
||||||
|
// reset
|
||||||
|
status.fused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// state was reset to aid source, keep observation and update all other fields appropriately (zero innovation, etc)
|
||||||
|
template <typename T>
|
||||||
|
void resetAidSourceStatusZeroInnovation(T &status) const
|
||||||
|
{
|
||||||
|
status.time_last_fuse = _time_delayed_us;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < (sizeof(status.observation) / sizeof(status.observation[0])); i++) {
|
||||||
|
status.innovation[i] = 0.f;
|
||||||
|
status.innovation_filtered[i] = 0.f;
|
||||||
|
status.innovation_variance[i] = status.observation_variance[i];
|
||||||
|
|
||||||
|
status.test_ratio[i] = 0.f;
|
||||||
|
status.test_ratio_filtered[i] = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
status.innovation_rejected = false;
|
||||||
|
status.fused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper used for populating and filtering estimator aid source struct for logging
|
||||||
|
template <typename T, typename S>
|
||||||
|
void updateAidSourceStatus(T &status, const uint64_t ×tamp_sample,
|
||||||
|
const S &observation, const S &observation_variance,
|
||||||
|
const S &innovation, const S &innovation_variance,
|
||||||
|
float innovation_gate = 1.f) const
|
||||||
|
{
|
||||||
|
bool innovation_rejected = false;
|
||||||
|
|
||||||
|
const float dt_s = math::constrain((timestamp_sample - status.timestamp_sample) * 1e-6f, 0.001f, 1.f);
|
||||||
|
|
||||||
|
static constexpr float tau = 0.5f;
|
||||||
|
const float alpha = math::constrain(dt_s / (dt_s + tau), 0.f, 1.f);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < (sizeof(status.observation) / sizeof(status.observation[0])); i++) {
|
||||||
|
|
||||||
|
const float test_ratio = sq(innovation(i)) / (sq(innovation_gate) * innovation_variance(i));
|
||||||
|
|
||||||
|
if ((status.timestamp_sample > 0) && (timestamp_sample > status.timestamp_sample)) {
|
||||||
|
|
||||||
|
// test_ratio_filtered
|
||||||
|
if (PX4_ISFINITE(status.test_ratio_filtered[i])) {
|
||||||
|
status.test_ratio_filtered[i] += alpha * (matrix::sign(innovation(i)) * test_ratio - status.test_ratio_filtered[i]);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// otherwise, init the filtered test ratio
|
||||||
|
status.test_ratio_filtered[i] = test_ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
// innovation_filtered
|
||||||
|
if (PX4_ISFINITE(status.innovation_filtered[i])) {
|
||||||
|
status.innovation_filtered[i] += alpha * (innovation(i) - status.innovation_filtered[i]);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// otherwise, init the filtered innovation
|
||||||
|
status.innovation_filtered[i] = innovation(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// limit extremes in filtered values
|
||||||
|
static constexpr float kNormalizedInnovationLimit = 2.f;
|
||||||
|
static constexpr float kTestRatioLimit = sq(kNormalizedInnovationLimit);
|
||||||
|
|
||||||
|
if (test_ratio > kTestRatioLimit) {
|
||||||
|
|
||||||
|
status.test_ratio_filtered[i] = math::constrain(status.test_ratio_filtered[i], -kTestRatioLimit, kTestRatioLimit);
|
||||||
|
|
||||||
|
const float innov_limit = kNormalizedInnovationLimit * innovation_gate * sqrtf(innovation_variance(i));
|
||||||
|
status.innovation_filtered[i] = math::constrain(status.innovation_filtered[i], -innov_limit, innov_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// invalid timestamp_sample, reset
|
||||||
|
status.test_ratio_filtered[i] = test_ratio;
|
||||||
|
status.innovation_filtered[i] = innovation(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
status.test_ratio[i] = test_ratio;
|
||||||
|
|
||||||
|
status.observation[i] = observation(i);
|
||||||
|
status.observation_variance[i] = observation_variance(i);
|
||||||
|
|
||||||
|
status.innovation[i] = innovation(i);
|
||||||
|
status.innovation_variance[i] = innovation_variance(i);
|
||||||
|
|
||||||
|
if ((test_ratio > 1.f)
|
||||||
|
|| !PX4_ISFINITE(test_ratio)
|
||||||
|
|| !PX4_ISFINITE(status.innovation[i])
|
||||||
|
|| !PX4_ISFINITE(status.innovation_variance[i])
|
||||||
|
) {
|
||||||
innovation_rejected = true;
|
innovation_rejected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status.timestamp_sample = timestamp_sample;
|
||||||
|
|
||||||
// if any of the innovations are rejected, then the overall innovation is rejected
|
// if any of the innovations are rejected, then the overall innovation is rejected
|
||||||
status.innovation_rejected = innovation_rejected;
|
status.innovation_rejected = innovation_rejected;
|
||||||
|
|
||||||
|
// reset
|
||||||
|
status.fused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ZeroGyroUpdate _zero_gyro_update{};
|
ZeroGyroUpdate _zero_gyro_update{};
|
||||||
|
|||||||
@ -401,7 +401,6 @@ protected:
|
|||||||
|
|
||||||
# if defined(CONFIG_EKF2_GNSS_YAW)
|
# if defined(CONFIG_EKF2_GNSS_YAW)
|
||||||
// innovation consistency check monitoring ratios
|
// innovation consistency check monitoring ratios
|
||||||
AlphaFilter<float> _gnss_yaw_signed_test_ratio_lpf{0.1f}; // average signed test ratio used to detect a bias in the state
|
|
||||||
uint64_t _time_last_gps_yaw_buffer_push{0};
|
uint64_t _time_last_gps_yaw_buffer_push{0};
|
||||||
# endif // CONFIG_EKF2_GNSS_YAW
|
# endif // CONFIG_EKF2_GNSS_YAW
|
||||||
#endif // CONFIG_EKF2_GNSS
|
#endif // CONFIG_EKF2_GNSS
|
||||||
|
|||||||
@ -33,50 +33,27 @@
|
|||||||
|
|
||||||
#include "ekf.h"
|
#include "ekf.h"
|
||||||
|
|
||||||
void Ekf::updateHorizontalPositionAidSrcStatus(const uint64_t &time_us, const Vector2f &obs, const Vector2f &obs_var,
|
void Ekf::updateVerticalPositionAidStatus(estimator_aid_source1d_s &aid_src, const uint64_t &time_us,
|
||||||
const float innov_gate, estimator_aid_source2d_s &aid_src) const
|
const float observation, const float observation_variance, const float innovation_gate) const
|
||||||
{
|
{
|
||||||
resetEstimatorAidStatus(aid_src);
|
float innovation = _state.pos(2) - observation;
|
||||||
|
float innovation_variance = getStateVariance<State::pos>()(2) + observation_variance;
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++) {
|
updateAidSourceStatus(aid_src, time_us,
|
||||||
aid_src.observation[i] = obs(i);
|
observation, observation_variance,
|
||||||
aid_src.innovation[i] = _state.pos(i) - aid_src.observation[i];
|
innovation, innovation_variance,
|
||||||
|
innovation_gate);
|
||||||
aid_src.observation_variance[i] = math::max(sq(0.01f), obs_var(i));
|
|
||||||
const int state_index = State::pos.idx + i;
|
|
||||||
aid_src.innovation_variance[i] = P(state_index, state_index) + aid_src.observation_variance[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
setEstimatorAidStatusTestRatio(aid_src, innov_gate);
|
|
||||||
|
|
||||||
aid_src.timestamp_sample = time_us;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Ekf::updateVerticalPositionAidSrcStatus(const uint64_t &time_us, const float obs, const float obs_var,
|
|
||||||
const float innov_gate, estimator_aid_source1d_s &aid_src) const
|
|
||||||
{
|
|
||||||
resetEstimatorAidStatus(aid_src);
|
|
||||||
|
|
||||||
aid_src.observation = obs;
|
|
||||||
aid_src.innovation = _state.pos(2) - aid_src.observation;
|
|
||||||
|
|
||||||
aid_src.observation_variance = math::max(sq(0.01f), obs_var);
|
|
||||||
aid_src.innovation_variance = P(State::pos.idx + 2, State::pos.idx + 2) + aid_src.observation_variance;
|
|
||||||
|
|
||||||
setEstimatorAidStatusTestRatio(aid_src, innov_gate);
|
|
||||||
|
|
||||||
// z special case if there is bad vertical acceleration data, then don't reject measurement,
|
// z special case if there is bad vertical acceleration data, then don't reject measurement,
|
||||||
// but limit innovation to prevent spikes that could destabilise the filter
|
// but limit innovation to prevent spikes that could destabilise the filter
|
||||||
if (_fault_status.flags.bad_acc_vertical && aid_src.innovation_rejected) {
|
if (_fault_status.flags.bad_acc_vertical && aid_src.innovation_rejected) {
|
||||||
const float innov_limit = innov_gate * sqrtf(aid_src.innovation_variance);
|
const float innov_limit = innovation_gate * sqrtf(aid_src.innovation_variance);
|
||||||
aid_src.innovation = math::constrain(aid_src.innovation, -innov_limit, innov_limit);
|
aid_src.innovation = math::constrain(aid_src.innovation, -innov_limit, innov_limit);
|
||||||
aid_src.innovation_rejected = false;
|
aid_src.innovation_rejected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
aid_src.timestamp_sample = time_us;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::fuseHorizontalPosition(estimator_aid_source2d_s &aid_src)
|
bool Ekf::fuseHorizontalPosition(estimator_aid_source2d_s &aid_src)
|
||||||
{
|
{
|
||||||
// x & y
|
// x & y
|
||||||
if (!aid_src.innovation_rejected
|
if (!aid_src.innovation_rejected
|
||||||
@ -91,9 +68,11 @@ void Ekf::fuseHorizontalPosition(estimator_aid_source2d_s &aid_src)
|
|||||||
} else {
|
} else {
|
||||||
aid_src.fused = false;
|
aid_src.fused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return aid_src.fused;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::fuseVerticalPosition(estimator_aid_source1d_s &aid_src)
|
bool Ekf::fuseVerticalPosition(estimator_aid_source1d_s &aid_src)
|
||||||
{
|
{
|
||||||
// z
|
// z
|
||||||
if (!aid_src.innovation_rejected
|
if (!aid_src.innovation_rejected
|
||||||
@ -107,6 +86,8 @@ void Ekf::fuseVerticalPosition(estimator_aid_source1d_s &aid_src)
|
|||||||
} else {
|
} else {
|
||||||
aid_src.fused = false;
|
aid_src.fused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return aid_src.fused;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::resetHorizontalPositionTo(const Vector2f &new_horz_pos, const Vector2f &new_horz_pos_var)
|
void Ekf::resetHorizontalPositionTo(const Vector2f &new_horz_pos, const Vector2f &new_horz_pos_var)
|
||||||
@ -200,11 +181,3 @@ void Ekf::resetHorizontalPositionToLastKnown()
|
|||||||
// Used when falling back to non-aiding mode of operation
|
// Used when falling back to non-aiding mode of operation
|
||||||
resetHorizontalPositionTo(_last_known_pos.xy(), sq(_params.pos_noaid_noise));
|
resetHorizontalPositionTo(_last_known_pos.xy(), sq(_params.pos_noaid_noise));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::resetHorizontalPositionToExternal(const Vector2f &new_horiz_pos, float horiz_accuracy)
|
|
||||||
{
|
|
||||||
ECL_INFO("reset position to external observation");
|
|
||||||
_information_events.flags.reset_pos_to_ext_obs = true;
|
|
||||||
|
|
||||||
resetHorizontalPositionTo(new_horiz_pos, sq(horiz_accuracy));
|
|
||||||
}
|
|
||||||
|
|||||||
@ -205,8 +205,6 @@ void Ekf::stopHaglRngFusion()
|
|||||||
ECL_INFO("stopping HAGL range fusion");
|
ECL_INFO("stopping HAGL range fusion");
|
||||||
|
|
||||||
// reset flags
|
// reset flags
|
||||||
resetEstimatorAidStatus(_aid_src_terrain_range_finder);
|
|
||||||
|
|
||||||
_innov_check_fail_status.flags.reject_hagl = false;
|
_innov_check_fail_status.flags.reject_hagl = false;
|
||||||
|
|
||||||
_hagl_sensor_status.flags.range_finder = false;
|
_hagl_sensor_status.flags.range_finder = false;
|
||||||
@ -221,30 +219,19 @@ void Ekf::updateHaglRng(estimator_aid_source1d_s &aid_src) const
|
|||||||
// predict the hagl from the vehicle position and terrain height
|
// predict the hagl from the vehicle position and terrain height
|
||||||
const float pred_hagl = _terrain_vpos - _state.pos(2);
|
const float pred_hagl = _terrain_vpos - _state.pos(2);
|
||||||
|
|
||||||
// calculate the innovation
|
|
||||||
const float hagl_innov = pred_hagl - meas_hagl;
|
|
||||||
|
|
||||||
// calculate the observation variance adding the variance of the vehicles own height uncertainty
|
// calculate the observation variance adding the variance of the vehicles own height uncertainty
|
||||||
const float obs_variance = getRngVar();
|
const float obs_variance = getRngVar();
|
||||||
|
|
||||||
// calculate the innovation variance - limiting it to prevent a badly conditioned fusion
|
// calculate the innovation variance - limiting it to prevent a badly conditioned fusion
|
||||||
const float hagl_innov_var = fmaxf(_terrain_var + obs_variance, obs_variance);
|
const float hagl_innov_var = fmaxf(_terrain_var + obs_variance, obs_variance);
|
||||||
|
|
||||||
// perform an innovation consistency check and only fuse data if it passes
|
updateAidSourceStatus(aid_src,
|
||||||
const float innov_gate = fmaxf(_params.range_innov_gate, 1.0f);
|
_time_delayed_us, // sample timestamp
|
||||||
|
meas_hagl, // observation
|
||||||
|
obs_variance, // observation variance
|
||||||
aid_src.timestamp_sample = _time_delayed_us; // TODO
|
pred_hagl - meas_hagl, // innovation
|
||||||
|
hagl_innov_var, // innovation variance
|
||||||
aid_src.observation = meas_hagl;
|
math::max(_params.range_innov_gate, 1.f)); // innovation gate
|
||||||
aid_src.observation_variance = obs_variance;
|
|
||||||
|
|
||||||
aid_src.innovation = hagl_innov;
|
|
||||||
aid_src.innovation_variance = hagl_innov_var;
|
|
||||||
|
|
||||||
setEstimatorAidStatusTestRatio(aid_src, innov_gate);
|
|
||||||
|
|
||||||
aid_src.fused = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::fuseHaglRng(estimator_aid_source1d_s &aid_src)
|
void Ekf::fuseHaglRng(estimator_aid_source1d_s &aid_src)
|
||||||
@ -329,7 +316,6 @@ void Ekf::stopHaglFlowFusion()
|
|||||||
ECL_INFO("stopping HAGL flow fusion");
|
ECL_INFO("stopping HAGL flow fusion");
|
||||||
|
|
||||||
_hagl_sensor_status.flags.flow = false;
|
_hagl_sensor_status.flags.flow = false;
|
||||||
resetEstimatorAidStatus(_aid_src_terrain_optical_flow);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,9 +353,6 @@ void Ekf::fuseFlowForTerrain(estimator_aid_source2d_s &flow)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// run the innovation consistency check and record result
|
|
||||||
setEstimatorAidStatusTestRatio(flow, math::max(_params.flow_innov_gate, 1.f));
|
|
||||||
|
|
||||||
_innov_check_fail_status.flags.reject_optflow_X = (flow.test_ratio[0] > 1.f);
|
_innov_check_fail_status.flags.reject_optflow_X = (flow.test_ratio[0] > 1.f);
|
||||||
_innov_check_fail_status.flags.reject_optflow_Y = (flow.test_ratio[1] > 1.f);
|
_innov_check_fail_status.flags.reject_optflow_Y = (flow.test_ratio[1] > 1.f);
|
||||||
|
|
||||||
|
|||||||
@ -33,53 +33,7 @@
|
|||||||
|
|
||||||
#include "ekf.h"
|
#include "ekf.h"
|
||||||
|
|
||||||
void Ekf::updateHorizontalVelocityAidSrcStatus(const uint64_t &time_us, const Vector2f &obs, const Vector2f &obs_var,
|
bool Ekf::fuseHorizontalVelocity(estimator_aid_source2d_s &aid_src)
|
||||||
const float innov_gate, estimator_aid_source2d_s &aid_src) const
|
|
||||||
{
|
|
||||||
resetEstimatorAidStatus(aid_src);
|
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
aid_src.observation[i] = obs(i);
|
|
||||||
aid_src.innovation[i] = _state.vel(i) - aid_src.observation[i];
|
|
||||||
|
|
||||||
aid_src.observation_variance[i] = math::max(sq(0.01f), obs_var(i));
|
|
||||||
const int state_index = State::vel.idx + i;
|
|
||||||
aid_src.innovation_variance[i] = P(state_index, state_index) + aid_src.observation_variance[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
setEstimatorAidStatusTestRatio(aid_src, innov_gate);
|
|
||||||
|
|
||||||
aid_src.timestamp_sample = time_us;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Ekf::updateVelocityAidSrcStatus(const uint64_t &time_us, const Vector3f &obs, const Vector3f &obs_var,
|
|
||||||
const float innov_gate, estimator_aid_source3d_s &aid_src) const
|
|
||||||
{
|
|
||||||
resetEstimatorAidStatus(aid_src);
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
aid_src.observation[i] = obs(i);
|
|
||||||
aid_src.innovation[i] = _state.vel(i) - aid_src.observation[i];
|
|
||||||
|
|
||||||
aid_src.observation_variance[i] = math::max(sq(0.01f), obs_var(i));
|
|
||||||
const int state_index = State::vel.idx + i;
|
|
||||||
aid_src.innovation_variance[i] = P(state_index, state_index) + aid_src.observation_variance[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
setEstimatorAidStatusTestRatio(aid_src, innov_gate);
|
|
||||||
|
|
||||||
// vz special case if there is bad vertical acceleration data, then don't reject measurement,
|
|
||||||
// but limit innovation to prevent spikes that could destabilise the filter
|
|
||||||
if (_fault_status.flags.bad_acc_vertical && aid_src.innovation_rejected) {
|
|
||||||
const float innov_limit = innov_gate * sqrtf(aid_src.innovation_variance[2]);
|
|
||||||
aid_src.innovation[2] = math::constrain(aid_src.innovation[2], -innov_limit, innov_limit);
|
|
||||||
aid_src.innovation_rejected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
aid_src.timestamp_sample = time_us;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Ekf::fuseHorizontalVelocity(estimator_aid_source2d_s &aid_src)
|
|
||||||
{
|
{
|
||||||
// vx, vy
|
// vx, vy
|
||||||
if (!aid_src.innovation_rejected
|
if (!aid_src.innovation_rejected
|
||||||
@ -94,9 +48,11 @@ void Ekf::fuseHorizontalVelocity(estimator_aid_source2d_s &aid_src)
|
|||||||
} else {
|
} else {
|
||||||
aid_src.fused = false;
|
aid_src.fused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return aid_src.fused;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::fuseVelocity(estimator_aid_source3d_s &aid_src)
|
bool Ekf::fuseVelocity(estimator_aid_source3d_s &aid_src)
|
||||||
{
|
{
|
||||||
// vx, vy, vz
|
// vx, vy, vz
|
||||||
if (!aid_src.innovation_rejected
|
if (!aid_src.innovation_rejected
|
||||||
@ -113,6 +69,8 @@ void Ekf::fuseVelocity(estimator_aid_source3d_s &aid_src)
|
|||||||
} else {
|
} else {
|
||||||
aid_src.fused = false;
|
aid_src.fused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return aid_src.fused;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ekf::resetHorizontalVelocityTo(const Vector2f &new_horz_vel, const Vector2f &new_horz_vel_var)
|
void Ekf::resetHorizontalVelocityTo(const Vector2f &new_horz_vel, const Vector2f &new_horz_vel_var)
|
||||||
|
|||||||
@ -37,23 +37,8 @@
|
|||||||
|
|
||||||
#include <mathlib/mathlib.h>
|
#include <mathlib/mathlib.h>
|
||||||
|
|
||||||
// update quaternion states and covariances using the yaw innovation and yaw observation variance
|
|
||||||
bool Ekf::fuseYaw(estimator_aid_source1d_s &aid_src_status)
|
|
||||||
{
|
|
||||||
VectorState H_YAW;
|
|
||||||
sym::ComputeYawInnovVarAndH(_state.vector(), P, aid_src_status.observation_variance, &aid_src_status.innovation_variance, &H_YAW);
|
|
||||||
|
|
||||||
return fuseYaw(aid_src_status, H_YAW);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Ekf::fuseYaw(estimator_aid_source1d_s &aid_src_status, const VectorState &H_YAW)
|
bool Ekf::fuseYaw(estimator_aid_source1d_s &aid_src_status, const VectorState &H_YAW)
|
||||||
{
|
{
|
||||||
// define the innovation gate size
|
|
||||||
float gate_sigma = math::max(_params.heading_innov_gate, 1.f);
|
|
||||||
|
|
||||||
// innovation test ratio
|
|
||||||
setEstimatorAidStatusTestRatio(aid_src_status, gate_sigma);
|
|
||||||
|
|
||||||
// check if the innovation variance calculation is badly conditioned
|
// check if the innovation variance calculation is badly conditioned
|
||||||
if (aid_src_status.innovation_variance >= aid_src_status.observation_variance) {
|
if (aid_src_status.innovation_variance >= aid_src_status.observation_variance) {
|
||||||
// the innovation variance contribution from the state covariances is not negative, no fault
|
// the innovation variance contribution from the state covariances is not negative, no fault
|
||||||
@ -83,7 +68,7 @@ bool Ekf::fuseYaw(estimator_aid_source1d_s &aid_src_status, const VectorState &H
|
|||||||
Kfusion(row) *= heading_innov_var_inv;
|
Kfusion(row) *= heading_innov_var_inv;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the magnetometer unhealthy if the test fails
|
// set the heading unhealthy if the test fails
|
||||||
if (aid_src_status.innovation_rejected) {
|
if (aid_src_status.innovation_rejected) {
|
||||||
_innov_check_fail_status.flags.reject_yaw = true;
|
_innov_check_fail_status.flags.reject_yaw = true;
|
||||||
|
|
||||||
@ -97,7 +82,8 @@ bool Ekf::fuseYaw(estimator_aid_source1d_s &aid_src_status, const VectorState &H
|
|||||||
// constrain the innovation to the maximum set by the gate
|
// constrain the innovation to the maximum set by the gate
|
||||||
// we need to delay this forced fusion to avoid starting it
|
// we need to delay this forced fusion to avoid starting it
|
||||||
// immediately after touchdown, when the drone is still armed
|
// immediately after touchdown, when the drone is still armed
|
||||||
float gate_limit = sqrtf((sq(gate_sigma) * aid_src_status.innovation_variance));
|
const float gate_sigma = math::max(_params.heading_innov_gate, 1.f);
|
||||||
|
const float gate_limit = sqrtf((sq(gate_sigma) * aid_src_status.innovation_variance));
|
||||||
aid_src_status.innovation = math::constrain(aid_src_status.innovation, -gate_limit, gate_limit);
|
aid_src_status.innovation = math::constrain(aid_src_status.innovation, -gate_limit, gate_limit);
|
||||||
|
|
||||||
// also reset the yaw gyro variance to converge faster and avoid
|
// also reset the yaw gyro variance to converge faster and avoid
|
||||||
@ -122,13 +108,10 @@ bool Ekf::fuseYaw(estimator_aid_source1d_s &aid_src_status, const VectorState &H
|
|||||||
_fault_status.flags.bad_hdg = false;
|
_fault_status.flags.bad_hdg = false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} else {
|
|
||||||
_fault_status.flags.bad_hdg = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise
|
// otherwise
|
||||||
aid_src_status.fused = false;
|
_fault_status.flags.bad_hdg = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user