Skip to content

Commit ee859e0

Browse files
kamilritzpriseborough
authored andcommitted
Robustify timestamp checks (#729)
* Robustify timestamp checks * Remove lowerbound on sensor timestamp * Add tests for fusion timeouts * Inline and const time check functions * Rename dead_reckoning time variable
1 parent f20726d commit ee859e0

15 files changed

+429
-68
lines changed

EKF/control.cpp

+44-43
Large diffs are not rendered by default.

EKF/covariance.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ void Ekf::fixCovarianceErrors(bool force_symmetry)
811811

812812
// if we have failed for 7 seconds continuously, reset the accel bias covariances to fix bad conditioning of
813813
// the covariance matrix but preserve the variances (diagonals) to allow bias learning to continue
814-
if (_time_last_imu - _time_acc_bias_check > (uint64_t)7e6) {
814+
if (isTimedOut(_time_acc_bias_check, (uint64_t)7e6)) {
815815

816816
P.uncorrelateCovariance<3>(13);
817817

EKF/ekf.h

+11-1
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ class Ekf : public EstimatorInterface
331331
bool _tas_data_ready{false}; ///< true when new true airspeed data has fallen behind the fusion time horizon and is available to be fused
332332
bool _flow_for_terrain_data_ready{false}; /// same flag as "_flow_data_ready" but used for separate terrain estimator
333333

334-
uint64_t _time_ins_deadreckon_start{0}; ///< amount of time we have been doing inertial only deadreckoning (uSec)
334+
uint64_t _time_last_aiding{0}; ///< amount of time we have been doing inertial only deadreckoning (uSec)
335335
bool _using_synthetic_position{false}; ///< true if we are using a synthetic position to constrain drift
336336

337337
uint64_t _time_last_hor_pos_fuse{0}; ///< time the last fusion of horizontal position measurements was performed (uSec)
@@ -785,6 +785,16 @@ class Ekf : public EstimatorInterface
785785
// sensor measurement
786786
float calculate_synthetic_mag_z_measurement(const Vector3f& mag_meas, const Vector3f& mag_earth_predicted);
787787

788+
bool isTimedOut(uint64_t last_sensor_timestamp, uint64_t timeout_period) const
789+
{
790+
return last_sensor_timestamp + timeout_period < _time_last_imu;
791+
}
792+
793+
bool isRecent(uint64_t sensor_timestamp, uint64_t acceptance_interval) const
794+
{
795+
return sensor_timestamp + acceptance_interval > _time_last_imu;
796+
}
797+
788798
void stopGpsFusion();
789799

790800
void stopGpsPosFusion();

EKF/ekf_helper.cpp

+12-12
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ void Ekf::resetHeight()
246246
// initialize vertical position with newest baro measurement
247247
const baroSample &baro_newest = _baro_buffer.get_newest();
248248

249-
if (_time_last_imu - baro_newest.time_us < 2 * BARO_MAX_INTERVAL) {
249+
if (isRecent(baro_newest.time_us, 2 * BARO_MAX_INTERVAL)) {
250250
_state.pos(2) = _hgt_sensor_offset - baro_newest.hgt + _baro_hgt_offset;
251251

252252
// the state variance is the same as the observation
@@ -260,7 +260,7 @@ void Ekf::resetHeight()
260260

261261
} else if (_control_status.flags.gps_hgt) {
262262
// initialize vertical position and velocity with newest gps measurement
263-
if (_time_last_imu - gps_newest.time_us < 2 * GPS_MAX_INTERVAL) {
263+
if (isRecent(gps_newest.time_us, 2 * GPS_MAX_INTERVAL)) {
264264
_state.pos(2) = _hgt_sensor_offset - gps_newest.hgt + _gps_alt_ref;
265265

266266
// the state variance is the same as the observation
@@ -296,7 +296,7 @@ void Ekf::resetHeight()
296296
}
297297

298298
// reset the vertical velocity state
299-
if (_control_status.flags.gps && (_time_last_imu - gps_newest.time_us < 2 * GPS_MAX_INTERVAL)) {
299+
if (_control_status.flags.gps && isRecent(gps_newest.time_us, 2 * GPS_MAX_INTERVAL)) {
300300
// If we are using GPS, then use it to reset the vertical velocity
301301
_state.vel(2) = gps_newest.vel(2);
302302

@@ -1386,22 +1386,23 @@ bool Ekf::global_position_is_valid()
13861386
void Ekf::update_deadreckoning_status()
13871387
{
13881388
bool velPosAiding = (_control_status.flags.gps || _control_status.flags.ev_pos || _control_status.flags.ev_vel)
1389-
&& (((_time_last_imu - _time_last_hor_pos_fuse) <= _params.no_aid_timeout_max)
1390-
|| ((_time_last_imu - _time_last_hor_vel_fuse) <= _params.no_aid_timeout_max)
1391-
|| ((_time_last_imu - _time_last_delpos_fuse) <= _params.no_aid_timeout_max));
1392-
bool optFlowAiding = _control_status.flags.opt_flow && ((_time_last_imu - _time_last_of_fuse) <= _params.no_aid_timeout_max);
1393-
bool airDataAiding = _control_status.flags.wind && ((_time_last_imu - _time_last_arsp_fuse) <= _params.no_aid_timeout_max) && ((_time_last_imu - _time_last_beta_fuse) <= _params.no_aid_timeout_max);
1389+
&& (isRecent(_time_last_hor_pos_fuse, _params.no_aid_timeout_max)
1390+
|| isRecent(_time_last_hor_vel_fuse, _params.no_aid_timeout_max)
1391+
|| isRecent(_time_last_delpos_fuse, _params.no_aid_timeout_max));
1392+
bool optFlowAiding = _control_status.flags.opt_flow && isRecent(_time_last_of_fuse, _params.no_aid_timeout_max);
1393+
bool airDataAiding = _control_status.flags.wind &&
1394+
isRecent(_time_last_arsp_fuse, _params.no_aid_timeout_max) &&
1395+
isRecent(_time_last_beta_fuse, _params.no_aid_timeout_max);
13941396

13951397
_is_wind_dead_reckoning = !velPosAiding && !optFlowAiding && airDataAiding;
13961398
_is_dead_reckoning = !velPosAiding && !optFlowAiding && !airDataAiding;
13971399

1398-
// record the time we start inertial dead reckoning
13991400
if (!_is_dead_reckoning) {
1400-
_time_ins_deadreckon_start = _time_last_imu - _params.no_aid_timeout_max;
1401+
_time_last_aiding = _time_last_imu - _params.no_aid_timeout_max;
14011402
}
14021403

14031404
// report if we have been deadreckoning for too long
1404-
_deadreckon_time_exceeded = ((_time_last_imu - _time_ins_deadreckon_start) > (unsigned)_params.valid_timeout_max);
1405+
_deadreckon_time_exceeded = isTimedOut(_time_last_aiding, (uint64_t)_params.valid_timeout_max);
14051406
}
14061407

14071408
// calculate the inverse rotation matrix from a quaternion rotation
@@ -1748,7 +1749,6 @@ float Ekf::kahanSummation(float sum_previous, float input, float &accumulator) c
17481749
return t;
17491750
}
17501751

1751-
17521752
void Ekf::stopGpsFusion()
17531753
{
17541754
stopGpsPosFusion();

EKF/estimator_interface.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,6 @@ void EstimatorInterface::setGpsData(const gps_message &gps)
196196

197197
gps_sample_new.time_us = gps.time_usec - _params.gps_delay_ms * 1000;
198198
gps_sample_new.time_us -= FILTER_UPDATE_PERIOD_MS * 1000 / 2;
199-
gps_sample_new.time_us = math::max(gps_sample_new.time_us, _imu_sample_delayed.time_us);
200199

201200
gps_sample_new.vel = gps.vel_ned;
202201

@@ -267,7 +266,6 @@ void EstimatorInterface::setBaroData(const baroSample &baro_sample)
267266
baro_sample_new.time_us = 1000 * (_baro_timestamp_sum / _baro_sample_count);
268267
baro_sample_new.time_us -= _params.baro_delay_ms * 1000;
269268
baro_sample_new.time_us -= FILTER_UPDATE_PERIOD_MS * 1000 / 2;
270-
baro_sample_new.time_us = math::max(baro_sample_new.time_us, _imu_sample_delayed.time_us);
271269

272270
_baro_buffer.push(baro_sample_new);
273271

EKF/gps_checks.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ bool Ekf::gps_is_good(const gps_message &gps)
137137

138138
// Calculate time lapsed since last update, limit to prevent numerical errors and calculate a lowpass filter coefficient
139139
const float filt_time_const = 10.0f;
140-
const float dt = fminf(fmaxf(float(_time_last_imu - _gps_pos_prev.timestamp) * 1e-6f, 0.001f), filt_time_const);
140+
const float dt = fminf(fmaxf(float(int64_t(_time_last_imu) - int64_t(_gps_pos_prev.timestamp)) * 1e-6f, 0.001f), filt_time_const);
141141
const float filter_coef = dt / filt_time_const;
142142

143143
// The following checks are only valid when the vehicle is at rest
@@ -232,5 +232,5 @@ bool Ekf::gps_is_good(const gps_message &gps)
232232
}
233233

234234
// continuous period without fail of x seconds required to return a healthy status
235-
return _time_last_imu - _last_gps_fail_us > (uint64_t)_min_gps_health_time_us;
235+
return isTimedOut(_last_gps_fail_us, (uint64_t)_min_gps_health_time_us);
236236
}

EKF/terrain_estimator.cpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ bool Ekf::initHagl()
5757
initialized = true;
5858

5959
} else if (_rng_hgt_valid
60-
&& (_time_last_imu - latest_measurement.time_us) < (uint64_t)2e5
60+
&& isRecent(latest_measurement.time_us, (uint64_t)2e5)
6161
&& _R_rng_to_earth_2_2 > _params.range_cos_max_tilt) {
6262
// if we have a fresh measurement, use it to initialise the terrain estimator
6363
_terrain_vpos = _state.pos(2) + latest_measurement.rng * _R_rng_to_earth_2_2;
@@ -168,7 +168,7 @@ void Ekf::fuseHagl()
168168

169169
} else {
170170
// If we have been rejecting range data for too long, reset to measurement
171-
if ((_time_last_imu - _time_last_hagl_fuse) > (uint64_t)10E6) {
171+
if (isTimedOut(_time_last_hagl_fuse, (uint64_t)10E6)) {
172172
_terrain_vpos = _state.pos(2) + meas_hagl;
173173
_terrain_var = obs_variance;
174174

@@ -290,12 +290,12 @@ bool Ekf::isTerrainEstimateValid() const
290290
void Ekf::updateTerrainValidity()
291291
{
292292
// we have been fusing range finder measurements in the last 5 seconds
293-
const bool recent_range_fusion = (_time_last_imu - _time_last_hagl_fuse) < (uint64_t)5e6;
293+
const bool recent_range_fusion = isRecent(_time_last_hagl_fuse, (uint64_t)5e6);
294294

295295
// we have been fusing optical flow measurements for terrain estimation within the last 5 seconds
296296
// this can only be the case if the main filter does not fuse optical flow
297-
const bool recent_flow_for_terrain_fusion = ((_time_last_imu - _time_last_of_fuse) < (uint64_t)5e6)
298-
&& !_control_status.flags.opt_flow;
297+
const bool recent_flow_for_terrain_fusion = isRecent(_time_last_of_fuse, (uint64_t)5e6)
298+
&& !_control_status.flags.opt_flow;
299299

300300
_hagl_valid = (_terrain_initialised && (recent_range_fusion || recent_flow_for_terrain_fusion));
301301
}

test/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ set(SRCS
4444
test_AlphaFilter.cpp
4545
test_EKF_fusionLogic.cpp
4646
test_EKF_initialization.cpp
47+
test_EKF_gps.cpp
4748
test_EKF_externalVision.cpp
4849
test_EKF_airspeed.cpp
4950
)

test/sensor_simulator/ekf_wrapper.cpp

+48
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,54 @@ EkfWrapper::~EkfWrapper()
1010
{
1111
}
1212

13+
void EkfWrapper::setBaroHeight()
14+
{
15+
_ekf_params->vdist_sensor_type = VDIST_SENSOR_BARO;
16+
}
17+
18+
bool EkfWrapper::isIntendingBaroHeightFusion() const
19+
{
20+
filter_control_status_u control_status;
21+
_ekf->get_control_mode(&control_status.value);
22+
return control_status.flags.baro_hgt;
23+
}
24+
25+
void EkfWrapper::setGpsHeight()
26+
{
27+
_ekf_params->vdist_sensor_type = VDIST_SENSOR_GPS;
28+
}
29+
30+
bool EkfWrapper::isIntendingGpsHeightFusion() const
31+
{
32+
filter_control_status_u control_status;
33+
_ekf->get_control_mode(&control_status.value);
34+
return control_status.flags.gps_hgt;
35+
}
36+
37+
void EkfWrapper::setRangeHeight()
38+
{
39+
_ekf_params->vdist_sensor_type = VDIST_SENSOR_RANGE;
40+
}
41+
42+
bool EkfWrapper::isIntendingRangeHeightFusion() const
43+
{
44+
filter_control_status_u control_status;
45+
_ekf->get_control_mode(&control_status.value);
46+
return control_status.flags.rng_hgt;
47+
}
48+
49+
void EkfWrapper::setVisionHeight()
50+
{
51+
_ekf_params->vdist_sensor_type = VDIST_SENSOR_EV;
52+
}
53+
54+
bool EkfWrapper::isIntendingVisionHeightFusion() const
55+
{
56+
filter_control_status_u control_status;
57+
_ekf->get_control_mode(&control_status.value);
58+
return control_status.flags.ev_hgt;
59+
}
60+
1361
void EkfWrapper::enableGpsFusion()
1462
{
1563
_ekf_params->fusion_mode |= MASK_USE_GPS;

test/sensor_simulator/ekf_wrapper.h

+13
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,19 @@ class EkfWrapper
4747
EkfWrapper(std::shared_ptr<Ekf> ekf);
4848
~EkfWrapper();
4949

50+
51+
void setBaroHeight();
52+
bool isIntendingBaroHeightFusion() const;
53+
54+
void setGpsHeight();
55+
bool isIntendingGpsHeightFusion() const;
56+
57+
void setRangeHeight();
58+
bool isIntendingRangeHeightFusion() const;
59+
60+
void setVisionHeight();
61+
bool isIntendingVisionHeightFusion() const;
62+
5063
void enableGpsFusion();
5164
void disableGpsFusion();
5265
bool isIntendingGpsFusion() const;

test/sensor_simulator/gps.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,21 @@ void Gps::setVelocity(const Vector3f& vel)
4444
_gps_data.vel_ned = vel;
4545
}
4646

47+
void Gps::setFixType(int n)
48+
{
49+
_gps_data.fix_type = n;
50+
}
51+
52+
void Gps::setNumberOfSatellites(int n)
53+
{
54+
_gps_data.nsats = n;
55+
}
56+
57+
void Gps::setPdop(float pdop)
58+
{
59+
_gps_data.pdop = pdop;
60+
}
61+
4762
void Gps::stepHeightByMeters(float hgt_change)
4863
{
4964
_gps_data.alt += hgt_change * 1e3f;

test/sensor_simulator/gps.h

+3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ class Gps: public Sensor
5757
void setLatitude(int32_t lat);
5858
void setLongitude(int32_t lon);
5959
void setVelocity(const Vector3f& vel);
60+
void setFixType(int n);
61+
void setNumberOfSatellites(int n);
62+
void setPdop(float pdop);
6063

6164
gps_message getDefaultGpsData();
6265

test/sensor_simulator/sensor_simulator.h

+3
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ class SensorSimulator
7878
void runSeconds(float duration_seconds);
7979
void runMicroseconds(uint32_t duration);
8080

81+
void startBaro(){ _baro.start(); }
82+
void stopBaro(){ _baro.stop(); }
83+
8184
void startGps(){ _gps.start(); }
8285
void stopGps(){ _gps.stop(); }
8386

0 commit comments

Comments
 (0)