forked from Archive/PX4-Autopilot
Merge pull request #254 from PX4/pr-ekfAccelErrorHandling
EKF: improve recovery from bad IMU accel data
This commit is contained in:
commit
b07acd8cf3
|
@ -169,6 +169,11 @@ struct extVisionSample {
|
||||||
#define RNG_MAX_INTERVAL 2e5
|
#define RNG_MAX_INTERVAL 2e5
|
||||||
#define EV_MAX_INTERVAL 2e5
|
#define EV_MAX_INTERVAL 2e5
|
||||||
|
|
||||||
|
// bad accelerometer detection and mitigation
|
||||||
|
#define BADACC_PROBATION 10E6 // Number of usec that accel data declared bad must continuously pass checks to be declared good
|
||||||
|
#define BADACC_HGT_RESET 1E6 // Number of usec that accel data must continuously fail checks to trigger a height reset
|
||||||
|
#define BADACC_BIAS_PNOISE 4.9f // The delta velocity process noise is set to this when accel data is declared bad (m/s**2)
|
||||||
|
|
||||||
struct parameters {
|
struct parameters {
|
||||||
// measurement source control
|
// measurement source control
|
||||||
int fusion_mode; // bitmasked integer that selects which of the GPS and optical flow aiding sources will be used
|
int fusion_mode; // bitmasked integer that selects which of the GPS and optical flow aiding sources will be used
|
||||||
|
@ -501,6 +506,7 @@ union ekf_solution_status {
|
||||||
uint16_t pred_pos_horiz_rel : 1; // 8 - True if the EKF has sufficient data to enter a mode that will provide a (relative) position estimate
|
uint16_t pred_pos_horiz_rel : 1; // 8 - True if the EKF has sufficient data to enter a mode that will provide a (relative) position estimate
|
||||||
uint16_t pred_pos_horiz_abs : 1; // 9 - True if the EKF has sufficient data to enter a mode that will provide a (absolute) position estimate
|
uint16_t pred_pos_horiz_abs : 1; // 9 - True if the EKF has sufficient data to enter a mode that will provide a (absolute) position estimate
|
||||||
uint16_t gps_glitch : 1; // 10 - True if the EKF has detected a GPS glitch
|
uint16_t gps_glitch : 1; // 10 - True if the EKF has detected a GPS glitch
|
||||||
|
uint16_t accel_error : 1; // 11 - True if the EKF has detected bad accelerometer data
|
||||||
} flags;
|
} flags;
|
||||||
uint16_t value;
|
uint16_t value;
|
||||||
};
|
};
|
||||||
|
|
|
@ -468,9 +468,26 @@ void Ekf::controlHeightSensorTimeouts()
|
||||||
// record time of last bad vert accel
|
// record time of last bad vert accel
|
||||||
if (bad_vert_accel) {
|
if (bad_vert_accel) {
|
||||||
_time_bad_vert_accel = _time_last_imu;
|
_time_bad_vert_accel = _time_last_imu;
|
||||||
|
} else {
|
||||||
|
_time_good_vert_accel = _time_last_imu;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((P[9][9] > sq(_params.hgt_reset_lim)) && ((_time_last_imu - _time_last_hgt_fuse) > 5e6)) {
|
// declare a bad vertical acceleration measurement and make the declaration persist
|
||||||
|
// for a minimum of 10 seconds
|
||||||
|
if (_bad_vert_accel_detected) {
|
||||||
|
_bad_vert_accel_detected = (_time_last_imu - _time_bad_vert_accel < BADACC_PROBATION);
|
||||||
|
} else {
|
||||||
|
_bad_vert_accel_detected = bad_vert_accel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if height is continuously failing becasue of accel errors
|
||||||
|
bool continuous_bad_accel_hgt = ((_time_last_imu - _time_good_vert_accel) > BADACC_HGT_RESET);
|
||||||
|
|
||||||
|
// check if height has been inertial deadreckoning for too long
|
||||||
|
bool hgt_fusion_timeout = ((_time_last_imu - _time_last_hgt_fuse) > 5e6);
|
||||||
|
|
||||||
|
// reset the vertical position and velocity states
|
||||||
|
if ((P[9][9] > sq(_params.hgt_reset_lim)) && (hgt_fusion_timeout || continuous_bad_accel_hgt)) {
|
||||||
// boolean that indicates we will do a height reset
|
// boolean that indicates we will do a height reset
|
||||||
bool reset_height = false;
|
bool reset_height = false;
|
||||||
|
|
||||||
|
@ -484,7 +501,7 @@ void Ekf::controlHeightSensorTimeouts()
|
||||||
bool baro_hgt_available = ((_time_last_imu - baro_init.time_us) < 2 * BARO_MAX_INTERVAL);
|
bool baro_hgt_available = ((_time_last_imu - baro_init.time_us) < 2 * BARO_MAX_INTERVAL);
|
||||||
|
|
||||||
// check for inertial sensing errors in the last 10 seconds
|
// check for inertial sensing errors in the last 10 seconds
|
||||||
bool prev_bad_vert_accel = (_time_last_imu - _time_bad_vert_accel < 10E6);
|
bool prev_bad_vert_accel = (_time_last_imu - _time_bad_vert_accel < BADACC_PROBATION);
|
||||||
|
|
||||||
// reset to GPS if adequate GPS data is available and the timeout cannot be blamed on IMU data
|
// reset to GPS if adequate GPS data is available and the timeout cannot be blamed on IMU data
|
||||||
bool reset_to_gps = gps_hgt_available && gps_hgt_accurate && !_gps_hgt_faulty && !prev_bad_vert_accel;
|
bool reset_to_gps = gps_hgt_available && gps_hgt_accurate && !_gps_hgt_faulty && !prev_bad_vert_accel;
|
||||||
|
|
|
@ -154,11 +154,13 @@ void Ekf::predictCovariance()
|
||||||
// convert rate of change of accelerometer bias (m/s**3) as specified by the parameter to an expected change in delta velocity (m/s) since the last update
|
// convert rate of change of accelerometer bias (m/s**3) as specified by the parameter to an expected change in delta velocity (m/s) since the last update
|
||||||
float d_vel_bias_sig = dt * dt * math::constrain(_params.accel_bias_p_noise, 0.0f, 1.0f);
|
float d_vel_bias_sig = dt * dt * math::constrain(_params.accel_bias_p_noise, 0.0f, 1.0f);
|
||||||
|
|
||||||
// inhibit learning of imu acccel bias if the manoeuvre levels are too high to protect against the effect of sensor nonlinearities
|
// inhibit learning of imu acccel bias if the manoeuvre levels are too high to protect against the effect of sensor nonlinearities or bad accel data is detected
|
||||||
float alpha = 1.0f - math::constrain((dt / _params.acc_bias_learn_tc), 0.0f, 1.0f);
|
float alpha = 1.0f - math::constrain((dt / _params.acc_bias_learn_tc), 0.0f, 1.0f);
|
||||||
_ang_rate_mag_filt = fmax(_imu_sample_delayed.delta_ang.norm(), alpha * _ang_rate_mag_filt);
|
_ang_rate_mag_filt = fmax(_imu_sample_delayed.delta_ang.norm(), alpha * _ang_rate_mag_filt);
|
||||||
_accel_mag_filt = fmax(_imu_sample_delayed.delta_vel.norm(), alpha * _accel_mag_filt);
|
_accel_mag_filt = fmax(_imu_sample_delayed.delta_vel.norm(), alpha * _accel_mag_filt);
|
||||||
if (_ang_rate_mag_filt > dt * _params.acc_bias_learn_gyr_lim || _accel_mag_filt > dt * _params.acc_bias_learn_acc_lim) {
|
if (_ang_rate_mag_filt > dt * _params.acc_bias_learn_gyr_lim
|
||||||
|
|| _accel_mag_filt > dt * _params.acc_bias_learn_acc_lim
|
||||||
|
|| _bad_vert_accel_detected) {
|
||||||
// store the bias state variances to be reinstated later
|
// store the bias state variances to be reinstated later
|
||||||
if (!_accel_bias_inhibit) {
|
if (!_accel_bias_inhibit) {
|
||||||
_prev_dvel_bias_var(0) = P[13][13];
|
_prev_dvel_bias_var(0) = P[13][13];
|
||||||
|
@ -232,6 +234,11 @@ void Ekf::predictCovariance()
|
||||||
float gyro_noise = math::constrain(_params.gyro_noise, 0.0f, 1.0f);
|
float gyro_noise = math::constrain(_params.gyro_noise, 0.0f, 1.0f);
|
||||||
daxVar = dayVar = dazVar = sq(dt * gyro_noise);
|
daxVar = dayVar = dazVar = sq(dt * gyro_noise);
|
||||||
float accel_noise = math::constrain(_params.accel_noise, 0.0f, 1.0f);
|
float accel_noise = math::constrain(_params.accel_noise, 0.0f, 1.0f);
|
||||||
|
if (_bad_vert_accel_detected) {
|
||||||
|
// Increase accelerometer process noise if bad accel data is detected. Measurement errors due to
|
||||||
|
// vibration induced clipping commonly reach an equivalent 0.5g offset.
|
||||||
|
accel_noise = BADACC_BIAS_PNOISE;
|
||||||
|
}
|
||||||
dvxVar = dvyVar = dvzVar = sq(dt * accel_noise);
|
dvxVar = dvyVar = dvzVar = sq(dt * accel_noise);
|
||||||
|
|
||||||
// predict the covariance
|
// predict the covariance
|
||||||
|
|
|
@ -123,7 +123,9 @@ Ekf::Ekf():
|
||||||
_gps_hgt_faulty(false),
|
_gps_hgt_faulty(false),
|
||||||
_rng_hgt_faulty(false),
|
_rng_hgt_faulty(false),
|
||||||
_primary_hgt_source(VDIST_SENSOR_BARO),
|
_primary_hgt_source(VDIST_SENSOR_BARO),
|
||||||
_time_bad_vert_accel(0)
|
_time_bad_vert_accel(0),
|
||||||
|
_time_good_vert_accel(0),
|
||||||
|
_bad_vert_accel_detected(false)
|
||||||
{
|
{
|
||||||
_state = {};
|
_state = {};
|
||||||
_last_known_posNE.setZero();
|
_last_known_posNE.setZero();
|
||||||
|
|
|
@ -349,6 +349,8 @@ private:
|
||||||
|
|
||||||
// imu fault status
|
// imu fault status
|
||||||
uint64_t _time_bad_vert_accel; // last time a bad vertical accel was detected (usec)
|
uint64_t _time_bad_vert_accel; // last time a bad vertical accel was detected (usec)
|
||||||
|
uint64_t _time_good_vert_accel; // last time a good vertical accel was detected (usec)
|
||||||
|
bool _bad_vert_accel_detected; // true when bad vertical accelerometer data has been detected
|
||||||
|
|
||||||
// update the real time complementary filter states. This includes the prediction
|
// update the real time complementary filter states. This includes the prediction
|
||||||
// and the correction step
|
// and the correction step
|
||||||
|
|
|
@ -862,6 +862,7 @@ void Ekf::get_ekf_soln_status(uint16_t *status)
|
||||||
bool gps_pos_innov_bad = (_vel_pos_test_ratio[3] > 1.0f) || (_vel_pos_test_ratio[4] > 1.0f);
|
bool gps_pos_innov_bad = (_vel_pos_test_ratio[3] > 1.0f) || (_vel_pos_test_ratio[4] > 1.0f);
|
||||||
bool mag_innov_good = (_mag_test_ratio[0] < 1.0f) && (_mag_test_ratio[1] < 1.0f) && (_mag_test_ratio[2] < 1.0f) && (_yaw_test_ratio < 1.0f);
|
bool mag_innov_good = (_mag_test_ratio[0] < 1.0f) && (_mag_test_ratio[1] < 1.0f) && (_mag_test_ratio[2] < 1.0f) && (_yaw_test_ratio < 1.0f);
|
||||||
soln_status.flags.gps_glitch = (gps_vel_innov_bad || gps_pos_innov_bad) && mag_innov_good;
|
soln_status.flags.gps_glitch = (gps_vel_innov_bad || gps_pos_innov_bad) && mag_innov_good;
|
||||||
|
soln_status.flags.accel_error = _bad_vert_accel_detected;
|
||||||
*status = soln_status.value;
|
*status = soln_status.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue