ekf2: perform GNSS checks at delayed-time horizon

- never fuse a measurement that is not passing the checks
- cleanup and simplify GNSS vel/pos control logic
This commit is contained in:
bresch
2023-12-18 18:11:20 +01:00
committed by Mathieu Bresciani
parent b0566cd8aa
commit a653073d4f
15 changed files with 371 additions and 419 deletions
+15 -29
View File
@@ -160,25 +160,6 @@ enum class MagCheckMask : uint8_t {
FORCE_WMM = (1 << 2)
};
struct gpsMessage {
uint64_t time_usec{};
int32_t lat{}; ///< Latitude in 1E-7 degrees
int32_t lon{}; ///< Longitude in 1E-7 degrees
int32_t alt{}; ///< Altitude in 1E-3 meters (millimeters) above MSL
float yaw{}; ///< yaw angle. NaN if not set (used for dual antenna GPS), (rad, [-PI, PI])
float yaw_offset{}; ///< Heading/Yaw offset for dual antenna GPS - refer to description for GPS_YAW_OFFSET
float yaw_accuracy{}; ///< yaw measurement accuracy (rad, [0, 2PI])
uint8_t fix_type{}; ///< 0-1: no fix, 2: 2D fix, 3: 3D fix, 4: RTCM code differential, 5: Real-Time Kinematic
float eph{}; ///< GPS horizontal position accuracy in m
float epv{}; ///< GPS vertical position accuracy in m
float sacc{}; ///< GPS speed accuracy in m/s
float vel_m_s{}; ///< GPS ground speed (m/sec)
Vector3f vel_ned{}; ///< GPS ground speed NED
bool vel_ned_valid{}; ///< GPS ground speed is valid
uint8_t nsats{}; ///< number of satellites used
float pdop{}; ///< position dilution of precision
};
struct imuSample {
uint64_t time_us{}; ///< timestamp of the measurement (uSec)
Vector3f delta_ang{}; ///< delta angle in body frame (integrated gyro measurements) (rad)
@@ -188,16 +169,21 @@ struct imuSample {
bool delta_vel_clipping[3] {}; ///< true (per axis) if this sample contained any accelerometer clipping
};
struct gpsSample {
uint64_t time_us{}; ///< timestamp of the measurement (uSec)
Vector2f pos{}; ///< NE earth frame gps horizontal position measurement (m)
float hgt{}; ///< gps height measurement (m)
Vector3f vel{}; ///< NED earth frame gps velocity measurement (m/sec)
float yaw{}; ///< yaw angle. NaN if not set (used for dual antenna GPS), (rad, [-PI, PI])
float hacc{}; ///< 1-std horizontal position error (m)
float vacc{}; ///< 1-std vertical position error (m)
float sacc{}; ///< 1-std speed error (m/sec)
float yaw_acc{}; ///< 1-std yaw error (rad)
struct gnssSample {
uint64_t time_us{}; ///< timestamp of the measurement (uSec)
double lat{}; ///< latitude (degrees)
double lon{}; ///< longitude (degrees)
float alt{}; ///< GNSS altitude above MSL (m)
Vector3f vel{}; ///< NED earth frame GNSS velocity measurement (m/sec)
float hacc{}; ///< 1-std horizontal position error (m)
float vacc{}; ///< 1-std vertical position error (m)
float sacc{}; ///< 1-std speed error (m/sec)
uint8_t fix_type{}; ///< 0-1: no fix, 2: 2D fix, 3: 3D fix, 4: RTCM code differential, 5: Real-Time
uint8_t nsats{}; ///< number of satellites used
float pdop{}; ///< position dilution of precision
float yaw{}; ///< yaw angle. NaN if not set (used for dual antenna GPS), (rad, [-PI, PI])
float yaw_acc{}; ///< 1-std yaw error (rad)
float yaw_offset{}; ///< Heading/Yaw offset for dual antenna GPS - refer to description for GPS_YAW_OFFSET
};
struct magSample {
+18 -11
View File
@@ -431,8 +431,7 @@ public:
#endif // CONFIG_EKF2_EXTERNAL_VISION
#if defined(CONFIG_EKF2_GNSS)
// ask estimator for sensor data collection decision and do any preprocessing if required, returns true if not defined
bool collect_gps(const gpsMessage &gps) override;
void collect_gps(const gnssSample &gps);
// set minimum continuous period without GPS fail required to mark a healthy GPS status
void set_min_required_gps_health_time(uint32_t time_us) { _min_gps_health_time_us = time_us; }
@@ -636,7 +635,6 @@ private:
#endif // CONFIG_EKF2_EXTERNAL_VISION
#if defined(CONFIG_EKF2_GNSS)
// booleans true when fresh sensor data is available at the fusion time horizon
bool _gps_data_ready{false}; ///< true when new GPS data has fallen behind the fusion time horizon and is available to be fused
// variables used for the GPS quality checks
@@ -989,29 +987,38 @@ private:
// control fusion of GPS observations
void controlGpsFusion(const imuSample &imu_delayed);
void stopGpsFusion();
void updateGnssVel(const gnssSample &gnss_sample, estimator_aid_source3d_s &aid_src);
void updateGnssPos(const gnssSample &gnss_sample, estimator_aid_source2d_s &aid_src);
void controlGnssYawEstimator(estimator_aid_source3d_s &aid_src_vel);
bool tryYawEmergencyReset();
void resetVelocityToGnss(estimator_aid_source3d_s &aid_src);
void resetHorizontalPositionToGnss(estimator_aid_source2d_s &aid_src);
bool shouldResetGpsFusion() const;
// return true id the GPS quality is good enough to set an origin and start aiding
bool gps_is_good(const gpsMessage &gps);
/*
* Return true if the GPS solution quality is adequate.
* Checks are activated using the EKF2_GPS_CHECK bitmask parameter
* Checks are adjusted using the EKF2_REQ_* parameters
*/
bool runGnssChecks(const gnssSample &gps);
void controlGnssHeightFusion(const gpsSample &gps_sample);
void controlGnssHeightFusion(const gnssSample &gps_sample);
void stopGpsHgtFusion();
void resetGpsDriftCheckFilters();
# if defined(CONFIG_EKF2_GNSS_YAW)
void controlGpsYawFusion(const gpsSample &gps_sample, bool gps_checks_passing, bool gps_checks_failing);
void controlGpsYawFusion(const gnssSample &gps_sample);
void stopGpsYawFusion();
// fuse the yaw angle obtained from a dual antenna GPS unit
void fuseGpsYaw();
void fuseGpsYaw(float antenna_yaw_offset);
// reset the quaternions states using the yaw angle obtained from a dual antenna GPS unit
// return true if the reset was successful
bool resetYawToGps(const float gnss_yaw);
bool resetYawToGps(float gnss_yaw, float gnss_yaw_offset);
void updateGpsYaw(const gpsSample &gps_sample);
void updateGpsYaw(const gnssSample &gps_sample);
# endif // CONFIG_EKF2_GNSS_YAW
+11 -48
View File
@@ -147,7 +147,7 @@ void EstimatorInterface::setMagData(const magSample &mag_sample)
#endif // CONFIG_EKF2_MAGNETOMETER
#if defined(CONFIG_EKF2_GNSS)
void EstimatorInterface::setGpsData(const gpsMessage &gps)
void EstimatorInterface::setGpsData(const gnssSample &gnss_sample)
{
if (!_initialised) {
return;
@@ -155,7 +155,7 @@ void EstimatorInterface::setGpsData(const gpsMessage &gps)
// Allocate the required buffer size if not previously done
if (_gps_buffer == nullptr) {
_gps_buffer = new RingBuffer<gpsSample>(_obs_buffer_length);
_gps_buffer = new RingBuffer<gnssSample>(_obs_buffer_length);
if (_gps_buffer == nullptr || !_gps_buffer->valid()) {
delete _gps_buffer;
@@ -165,61 +165,24 @@ void EstimatorInterface::setGpsData(const gpsMessage &gps)
}
}
const int64_t time_us = gps.time_usec
const int64_t time_us = gnss_sample.time_us
- static_cast<int64_t>(_params.gps_delay_ms * 1000)
- static_cast<int64_t>(_dt_ekf_avg * 5e5f); // seconds to microseconds divided by 2
if (time_us >= static_cast<int64_t>(_gps_buffer->get_newest().time_us + _min_obs_interval_us)) {
if (!gps.vel_ned_valid || (gps.fix_type == 0)) {
return;
}
gnssSample gnss_sample_new(gnss_sample);
gpsSample gps_sample_new;
gnss_sample_new.time_us = time_us;
gps_sample_new.time_us = time_us;
gps_sample_new.vel = gps.vel_ned;
gps_sample_new.sacc = gps.sacc;
gps_sample_new.hacc = gps.eph;
gps_sample_new.vacc = gps.epv;
gps_sample_new.hgt = (float)gps.alt * 1e-3f;
#if defined(CONFIG_EKF2_GNSS_YAW)
if (PX4_ISFINITE(gps.yaw)) {
_time_last_gps_yaw_buffer_push = _time_latest_us;
gps_sample_new.yaw = gps.yaw;
gps_sample_new.yaw_acc = PX4_ISFINITE(gps.yaw_accuracy) ? gps.yaw_accuracy : 0.f;
} else {
gps_sample_new.yaw = NAN;
gps_sample_new.yaw_acc = 0.f;
}
if (PX4_ISFINITE(gps.yaw_offset)) {
_gps_yaw_offset = gps.yaw_offset;
} else {
_gps_yaw_offset = 0.0f;
}
#endif // CONFIG_EKF2_GNSS_YAW
// Only calculate the relative position if the WGS-84 location of the origin is set
if (collect_gps(gps)) {
gps_sample_new.pos = _pos_ref.project((gps.lat / 1.0e7), (gps.lon / 1.0e7));
} else {
gps_sample_new.pos(0) = 0.0f;
gps_sample_new.pos(1) = 0.0f;
}
_gps_buffer->push(gps_sample_new);
_gps_buffer->push(gnss_sample_new);
_time_last_gps_buffer_push = _time_latest_us;
#if defined(CONFIG_EKF2_GNSS_YAW)
if (PX4_ISFINITE(gnss_sample.yaw)) {
_time_last_gps_yaw_buffer_push = _time_latest_us;
}
#endif // CONFIG_EKF2_GNSS_YAW
} else {
ECL_WARN("GPS data too fast %" PRIi64 " < %" PRIu64 " + %d", time_us, _gps_buffer->get_newest().time_us, _min_obs_interval_us);
+5 -7
View File
@@ -84,15 +84,14 @@ public:
void setIMUData(const imuSample &imu_sample);
#if defined(CONFIG_EKF2_GNSS)
// ask estimator for sensor data collection decision and do any preprocessing if required, returns true if not defined
virtual bool collect_gps(const gpsMessage &gps) = 0;
void setGpsData(const gpsMessage &gps);
void setGpsData(const gnssSample &gnss_sample);
const gpsSample &get_gps_sample_delayed() const { return _gps_sample_delayed; }
const gnssSample &get_gps_sample_delayed() const { return _gps_sample_delayed; }
float gps_horizontal_position_drift_rate_m_s() const { return _gps_horizontal_position_drift_rate_m_s; }
float gps_vertical_position_drift_rate_m_s() const { return _gps_vertical_position_drift_rate_m_s; }
float gps_filtered_horizontal_velocity_m_s() const { return _gps_filtered_horizontal_velocity_m_s; }
#endif // CONFIG_EKF2_GNSS
#if defined(CONFIG_EKF2_MAGNETOMETER)
@@ -390,10 +389,10 @@ protected:
float _gpos_origin_epv{0.0f}; // vertical position uncertainty of the global origin
#if defined(CONFIG_EKF2_GNSS)
RingBuffer<gpsSample> *_gps_buffer{nullptr};
RingBuffer<gnssSample> *_gps_buffer{nullptr};
uint64_t _time_last_gps_buffer_push{0};
gpsSample _gps_sample_delayed{};
gnssSample _gps_sample_delayed{};
float _gps_horizontal_position_drift_rate_m_s{NAN}; // Horizontal position drift rate (m/s)
float _gps_vertical_position_drift_rate_m_s{NAN}; // Vertical position drift rate (m/s)
@@ -403,7 +402,6 @@ protected:
float _gps_alt_prev{0.0f}; // height from the previous GPS message (m)
# if defined(CONFIG_EKF2_GNSS_YAW)
float _gps_yaw_offset{0.0f}; // Yaw offset angle for dual GPS antennas used for yaw estimation (radians).
// 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};
+8 -10
View File
@@ -38,7 +38,7 @@
#include "ekf.h"
void Ekf::controlGnssHeightFusion(const gpsSample &gps_sample)
void Ekf::controlGnssHeightFusion(const gnssSample &gps_sample)
{
static constexpr const char *HGT_SRC_NAME = "GNSS";
@@ -60,7 +60,11 @@ void Ekf::controlGnssHeightFusion(const gpsSample &gps_sample)
}
}
const float measurement = gps_sample.hgt - getEkfGlobalOriginAltitude();
const Vector3f pos_offset_body = _params.gps_pos_body - _params.imu_pos_body;
const Vector3f pos_offset_earth = _R_to_earth * pos_offset_body;
const float gnss_alt = _gps_sample_delayed.alt + pos_offset_earth(2);
const float measurement = gnss_alt - getEkfGlobalOriginAltitude();
const float measurement_var = sq(noise);
const float innov_gate = math::max(_params.gps_pos_innov_gate, 1.f);
@@ -74,12 +78,9 @@ void Ekf::controlGnssHeightFusion(const gpsSample &gps_sample)
innov_gate,
aid_src);
const bool gps_checks_passing = isTimedOut(_last_gps_fail_us, (uint64_t)5e6);
const bool gps_checks_failing = isTimedOut(_last_gps_pass_us, (uint64_t)5e6);
// update the bias estimator before updating the main filter but after
// using its current state to compute the vertical position innovation
if (measurement_valid && gps_checks_passing && !gps_checks_failing) {
if (measurement_valid) {
bias_est.setMaxStateNoise(sqrtf(measurement_var));
bias_est.setProcessNoiseSpectralDensity(_params.gps_hgt_bias_nsd);
bias_est.fuseBias(measurement - (-_state.pos(2)), measurement_var + P(State::pos.idx + 2, State::pos.idx + 2));
@@ -92,10 +93,7 @@ void Ekf::controlGnssHeightFusion(const gpsSample &gps_sample)
&& _gps_checks_passed;
const bool starting_conditions_passing = continuing_conditions_passing
&& isNewestSampleRecent(_time_last_gps_buffer_push, 2 * GNSS_MAX_INTERVAL)
&& _gps_checks_passed
&& gps_checks_passing
&& !gps_checks_failing;
&& isNewestSampleRecent(_time_last_gps_buffer_push, 2 * GNSS_MAX_INTERVAL);
if (_control_status.flags.gps_hgt) {
if (continuing_conditions_passing) {
+27 -41
View File
@@ -58,38 +58,35 @@
#define MASK_GPS_HSPD (1<<7)
#define MASK_GPS_VSPD (1<<8)
bool Ekf::collect_gps(const gpsMessage &gps)
void Ekf::collect_gps(const gnssSample &gps)
{
// Run GPS checks always
_gps_checks_passed = gps_is_good(gps);
if (_filter_initialised && !_NED_origin_initialised && _gps_checks_passed) {
// If we have good GPS data set the origin's WGS-84 position to the last gps fix
const double lat = gps.lat * 1.0e-7;
const double lon = gps.lon * 1.0e-7;
const double lat = gps.lat;
const double lon = gps.lon;
if (!_pos_ref.isInitialized()) {
_pos_ref.initReference(lat, lon, gps.time_usec);
_pos_ref.initReference(lat, lon, gps.time_us);
// if we are already doing aiding, correct for the change in position since the EKF started navigating
if (isHorizontalAidingActive()) {
double est_lat;
double est_lon;
_pos_ref.reproject(-_state.pos(0), -_state.pos(1), est_lat, est_lon);
_pos_ref.initReference(est_lat, est_lon, gps.time_usec);
_pos_ref.initReference(est_lat, est_lon, gps.time_us);
}
}
// Take the current GPS height and subtract the filter height above origin to estimate the GPS height of the origin
if (!PX4_ISFINITE(_gps_alt_ref)) {
_gps_alt_ref = 1e-3f * (float)gps.alt + _state.pos(2);
_gps_alt_ref = gps.alt + _state.pos(2);
}
_NED_origin_initialised = true;
// save the horizontal and vertical position uncertainty of the origin
_gpos_origin_eph = gps.eph;
_gpos_origin_epv = gps.epv;
_gpos_origin_eph = gps.hacc;
_gpos_origin_epv = gps.vacc;
_information_events.flags.gps_checks_passed = true;
ECL_INFO("GPS checks passed");
@@ -97,15 +94,15 @@ bool Ekf::collect_gps(const gpsMessage &gps)
if ((isTimedOut(_wmm_gps_time_last_checked, 1e6)) || (_wmm_gps_time_last_set == 0)) {
// a rough 2D fix is sufficient to lookup declination
const bool gps_rough_2d_fix = (gps.fix_type >= 2) && (gps.eph < 1000);
const bool gps_rough_2d_fix = (gps.fix_type >= 2) && (gps.hacc < 1000);
if (gps_rough_2d_fix && (_gps_checks_passed || !_NED_origin_initialised)) {
// If we have good GPS data set the origin's WGS-84 position to the last gps fix
const double lat = gps.lat * 1.0e-7;
const double lat = gps.lat;
#if defined(CONFIG_EKF2_MAGNETOMETER)
const double lon = gps.lon * 1.0e-7;
const double lon = gps.lon;
// set the magnetic field data returned by the geo library using the current GPS position
const float mag_declination_gps = get_mag_declination_radians(lat, lon);
@@ -138,19 +135,9 @@ bool Ekf::collect_gps(const gpsMessage &gps)
_wmm_gps_time_last_checked = _time_delayed_us;
}
// start collecting GPS if there is a 3D fix and the NED origin has been set
return _NED_origin_initialised && (gps.fix_type >= 3);
}
/*
* Return true if the GPS solution quality is adequate to set an origin for the EKF
* and start GPS aiding.
* All activated checks must pass for 10 seconds.
* Checks are activated using the EKF2_GPS_CHECK bitmask parameter
* Checks are adjusted using the EKF2_REQ_* parameters
*/
bool Ekf::gps_is_good(const gpsMessage &gps)
bool Ekf::runGnssChecks(const gnssSample &gps)
{
// Check the fix type
_gps_check_fail_status.flags.fix = (gps.fix_type < 3);
@@ -162,24 +149,24 @@ bool Ekf::gps_is_good(const gpsMessage &gps)
_gps_check_fail_status.flags.pdop = (gps.pdop > _params.req_pdop);
// Check the reported horizontal and vertical position accuracy
_gps_check_fail_status.flags.hacc = (gps.eph > _params.req_hacc);
_gps_check_fail_status.flags.vacc = (gps.epv > _params.req_vacc);
_gps_check_fail_status.flags.hacc = (gps.hacc > _params.req_hacc);
_gps_check_fail_status.flags.vacc = (gps.vacc > _params.req_vacc);
// Check the reported speed accuracy
_gps_check_fail_status.flags.sacc = (gps.sacc > _params.req_sacc);
// check if GPS quality is degraded
_gps_error_norm = fmaxf((gps.eph / _params.req_hacc), (gps.epv / _params.req_vacc));
_gps_error_norm = fmaxf((gps.hacc / _params.req_hacc), (gps.vacc / _params.req_vacc));
_gps_error_norm = fmaxf(_gps_error_norm, (gps.sacc / _params.req_sacc));
// Calculate time lapsed since last update, limit to prevent numerical errors and calculate a lowpass filter coefficient
constexpr float filt_time_const = 10.0f;
const float dt = math::constrain(float(int64_t(gps.time_usec) - int64_t(_gps_pos_prev.getProjectionReferenceTimestamp())) * 1e-6f, 0.001f, filt_time_const);
const float dt = math::constrain(float(int64_t(gps.time_us) - int64_t(_gps_pos_prev.getProjectionReferenceTimestamp())) * 1e-6f, 0.001f, filt_time_const);
const float filter_coef = dt / filt_time_const;
// The following checks are only valid when the vehicle is at rest
const double lat = gps.lat * 1.0e-7;
const double lon = gps.lon * 1.0e-7;
const double lat = gps.lat;
const double lon = gps.lon;
if (!_control_status.flags.in_air && _control_status.flags.vehicle_at_rest) {
// Calculate position movement since last measurement
@@ -192,13 +179,13 @@ bool Ekf::gps_is_good(const gpsMessage &gps)
} else {
// no previous position has been set
_gps_pos_prev.initReference(lat, lon, gps.time_usec);
_gps_alt_prev = 1e-3f * (float)gps.alt;
_gps_pos_prev.initReference(lat, lon, gps.time_us);
_gps_alt_prev = gps.alt;
}
// Calculate the horizontal and vertical drift velocity components and limit to 10x the threshold
const Vector3f vel_limit(_params.req_hdrift, _params.req_hdrift, _params.req_vdrift);
Vector3f pos_derived(delta_pos_n, delta_pos_e, (_gps_alt_prev - 1e-3f * (float)gps.alt));
Vector3f pos_derived(delta_pos_n, delta_pos_e, (_gps_alt_prev - gps.alt));
pos_derived = matrix::constrain(pos_derived / dt, -10.0f * vel_limit, 10.0f * vel_limit);
// Apply a low pass filter
@@ -213,7 +200,7 @@ bool Ekf::gps_is_good(const gpsMessage &gps)
_gps_check_fail_status.flags.vdrift = (_gps_vertical_position_drift_rate_m_s > _params.req_vdrift);
// Check the magnitude of the filtered horizontal GPS velocity
const Vector2f gps_velNE = matrix::constrain(Vector2f(gps.vel_ned.xy()),
const Vector2f gps_velNE = matrix::constrain(Vector2f(gps.vel.xy()),
-10.0f * _params.req_hdrift,
10.0f * _params.req_hdrift);
_gps_velNE_filt = gps_velNE * filter_coef + _gps_velNE_filt * (1.0f - filter_coef);
@@ -235,12 +222,12 @@ bool Ekf::gps_is_good(const gpsMessage &gps)
}
// save GPS fix for next time
_gps_pos_prev.initReference(lat, lon, gps.time_usec);
_gps_alt_prev = 1e-3f * (float)gps.alt;
_gps_pos_prev.initReference(lat, lon, gps.time_us);
_gps_alt_prev = gps.alt;
// Check the filtered difference between GPS and EKF vertical velocity
const float vz_diff_limit = 10.0f * _params.req_vdrift;
const float vertVel = math::constrain(gps.vel_ned(2) - _state.vel(2), -vz_diff_limit, vz_diff_limit);
const float vertVel = math::constrain(gps.vel(2) - _state.vel(2), -vz_diff_limit, vz_diff_limit);
_gps_velD_diff_filt = vertVel * filter_coef + _gps_velD_diff_filt * (1.0f - filter_coef);
_gps_check_fail_status.flags.vspeed = (fabsf(_gps_velD_diff_filt) > _params.req_vdrift);
@@ -263,13 +250,12 @@ bool Ekf::gps_is_good(const gpsMessage &gps)
(_gps_check_fail_status.flags.vspeed && (_params.gps_check_mask & MASK_GPS_VSPD))
) {
_last_gps_fail_us = _time_delayed_us;
return false;
} else {
_last_gps_pass_us = _time_delayed_us;
return true;
}
// continuous period without fail of x seconds required to return a healthy status
return isTimedOut(_last_gps_fail_us, (uint64_t)_min_gps_health_time_us);
}
void Ekf::resetGpsDriftCheckFilters()
+203 -183
View File
@@ -46,31 +46,6 @@ void Ekf::controlGpsFusion(const imuSample &imu_delayed)
return;
}
_gps_intermittent = !isNewestSampleRecent(_time_last_gps_buffer_push, 2 * GNSS_MAX_INTERVAL);
// check for arrival of new sensor data at the fusion time horizon
_gps_data_ready = _gps_buffer->pop_first_older_than(imu_delayed.time_us, &_gps_sample_delayed);
if (_gps_data_ready) {
// correct velocity for offset relative to IMU
const Vector3f pos_offset_body = _params.gps_pos_body - _params.imu_pos_body;
const Vector3f vel_offset_body = _ang_rate_delayed_raw % pos_offset_body;
const Vector3f vel_offset_earth = _R_to_earth * vel_offset_body;
_gps_sample_delayed.vel -= vel_offset_earth;
// correct position and height for offset relative to IMU
const Vector3f pos_offset_earth = _R_to_earth * pos_offset_body;
_gps_sample_delayed.pos -= pos_offset_earth.xy();
_gps_sample_delayed.hgt += pos_offset_earth(2);
// update GSF yaw estimator velocity (basic sanity check on GNSS velocity data)
if ((_gps_sample_delayed.sacc > 0.f) && (_gps_sample_delayed.sacc < _params.req_sacc)
&& _gps_sample_delayed.vel.isAllFinite()
) {
_yawEstimator.setVelocity(_gps_sample_delayed.vel.xy(), math::max(_gps_sample_delayed.sacc, _params.gps_vel_noise));
}
}
if (!gyro_bias_inhibited()) {
_yawEstimator.setGyroBias(getGyroBias());
}
@@ -78,155 +53,93 @@ void Ekf::controlGpsFusion(const imuSample &imu_delayed)
// run EKF-GSF yaw estimator once per imu_delayed update
_yawEstimator.update(imu_delayed, _control_status.flags.in_air && !_control_status.flags.vehicle_at_rest);
// Check for new GPS data that has fallen behind the fusion time horizon
_gps_intermittent = !isNewestSampleRecent(_time_last_gps_buffer_push, 2 * GNSS_MAX_INTERVAL);
// check for arrival of new sensor data at the fusion time horizon
_gps_data_ready = _gps_buffer->pop_first_older_than(imu_delayed.time_us, &_gps_sample_delayed);
if (_gps_data_ready) {
const gnssSample &gnss_sample = _gps_sample_delayed;
const gpsSample &gps_sample{_gps_sample_delayed};
if (runGnssChecks(gnss_sample) && isTimedOut(_last_gps_fail_us, (uint64_t)_min_gps_health_time_us / 2)) {
if (isTimedOut(_last_gps_fail_us, (uint64_t)_min_gps_health_time_us)) {
// First time checks are passing, latching.
_gps_checks_passed = true;
}
const bool gps_checks_passing = isTimedOut(_last_gps_fail_us, (uint64_t)5e6);
const bool gps_checks_failing = isTimedOut(_last_gps_pass_us, (uint64_t)5e6);
collect_gps(gnss_sample);
} else {
// Skip this sample
_gps_data_ready = false;
if (_control_status.flags.gps && isTimedOut(_last_gps_pass_us, _params.reset_timeout_max)) {
stopGpsFusion();
_warning_events.flags.gps_quality_poor = true;
ECL_WARN("GPS quality poor - stopping use");
}
}
updateGnssVel(gnss_sample, _aid_src_gnss_vel);
updateGnssPos(gnss_sample, _aid_src_gnss_pos);
} else if (_control_status.flags.gps) {
if (!isNewestSampleRecent(_time_last_gps_buffer_push, _params.reset_timeout_max)) {
stopGpsFusion();
_warning_events.flags.gps_data_stopped = true;
ECL_WARN("GPS data stopped");
}
}
if (_gps_data_ready) {
#if defined(CONFIG_EKF2_GNSS_YAW)
controlGpsYawFusion(gps_sample, gps_checks_passing, gps_checks_failing);
const gnssSample &gnss_sample = _gps_sample_delayed;
controlGpsYawFusion(gnss_sample);
#endif // CONFIG_EKF2_GNSS_YAW
// GNSS velocity
const Vector3f velocity{gps_sample.vel};
const float vel_var = sq(math::max(gps_sample.sacc, _params.gps_vel_noise));
const Vector3f vel_obs_var(vel_var, vel_var, vel_var * sq(1.5f));
updateVelocityAidSrcStatus(gps_sample.time_us,
velocity, // observation
vel_obs_var, // observation variance
math::max(_params.gps_vel_innov_gate, 1.f), // innovation gate
_aid_src_gnss_vel);
controlGnssYawEstimator(_aid_src_gnss_vel);
const bool gnss_vel_enabled = (_params.gnss_ctrl & static_cast<int32_t>(GnssCtrl::VEL));
// GNSS position
const Vector2f position{gps_sample.pos};
// relax the upper observation noise limit which prevents bad GPS perturbing the position estimate
float pos_noise = math::max(gps_sample.hacc, _params.gps_pos_noise);
if (!isOtherSourceOfHorizontalAidingThan(_control_status.flags.gps)) {
// if we are not using another source of aiding, then we are reliant on the GPS
// observations to constrain attitude errors and must limit the observation noise value.
if (pos_noise > _params.pos_noaid_noise) {
pos_noise = _params.pos_noaid_noise;
}
}
const float pos_var = sq(pos_noise);
const Vector2f pos_obs_var(pos_var, pos_var);
updateHorizontalPositionAidSrcStatus(gps_sample.time_us,
position, // observation
pos_obs_var, // observation variance
math::max(_params.gps_pos_innov_gate, 1.f), // innovation gate
_aid_src_gnss_pos);
const bool gnss_pos_enabled = (_params.gnss_ctrl & static_cast<int32_t>(GnssCtrl::HPOS));
// Determine if we should use GPS aiding for velocity and horizontal position
// To start using GPS we need angular alignment completed, the local NED origin set and GPS data that has not failed checks recently
bool mandatory_conditions_passing = false;
if ((gnss_pos_enabled || gnss_vel_enabled)
&& _control_status.flags.tilt_align
&& _NED_origin_initialised
) {
// if GPS is otherwise ready to go other than yaw align
if (!_control_status.flags.yaw_align && gps_checks_passing && !gps_checks_failing) {
if (resetYawToEKFGSF()) {
ECL_INFO("GPS yaw aligned using IMU");
}
}
if (_control_status.flags.yaw_align) {
mandatory_conditions_passing = true;
}
}
const bool continuing_conditions_passing = mandatory_conditions_passing && !gps_checks_failing;
const bool starting_conditions_passing = continuing_conditions_passing && gps_checks_passing;
const bool continuing_conditions_passing = (gnss_vel_enabled || gnss_pos_enabled)
&& _control_status.flags.tilt_align
&& _control_status.flags.yaw_align
&& _NED_origin_initialised;
const bool starting_conditions_passing = continuing_conditions_passing && _gps_checks_passed;
if (_control_status.flags.gps) {
if (mandatory_conditions_passing) {
if (continuing_conditions_passing
|| !isOtherSourceOfHorizontalAidingThan(_control_status.flags.gps)) {
if (continuing_conditions_passing) {
if (gnss_vel_enabled) {
fuseVelocity(_aid_src_gnss_vel);
}
if (gnss_pos_enabled) {
fuseHorizontalPosition(_aid_src_gnss_pos);
}
bool do_vel_pos_reset = shouldResetGpsFusion();
if (isYawFailure()
&& _control_status.flags.in_air
&& isTimedOut(_time_last_hor_vel_fuse, _params.EKFGSF_reset_delay)
&& (_time_last_hor_vel_fuse > _time_last_on_ground_us)) {
do_vel_pos_reset = tryYawEmergencyReset();
}
if (do_vel_pos_reset) {
ECL_WARN("GPS fusion timeout, resetting velocity / position");
if (gnss_vel_enabled) {
fuseVelocity(_aid_src_gnss_vel);
resetVelocityToGnss(_aid_src_gnss_vel);
}
if (gnss_pos_enabled) {
fuseHorizontalPosition(_aid_src_gnss_pos);
resetHorizontalPositionToGnss(_aid_src_gnss_pos);
}
bool do_vel_pos_reset = shouldResetGpsFusion();
if (isYawFailure()
&& _control_status.flags.in_air
&& isTimedOut(_time_last_hor_vel_fuse, _params.EKFGSF_reset_delay)
&& (_time_last_hor_vel_fuse > _time_last_on_ground_us)) {
/* A rapid reset to the yaw emergency estimate is performed if horizontal velocity innovation checks continuously
* fails while the difference between the yaw emergency estimator and the yas estimate is large.
* This enables recovery from a bad yaw estimate. A reset is not performed if the fault condition was
* present before flight to prevent triggering due to GPS glitches or other sensor errors.
*/
if (resetYawToEKFGSF()) {
ECL_WARN("GPS emergency yaw reset");
if (_control_status.flags.mag_hdg || _control_status.flags.mag_3D) {
// stop using the magnetometer in the main EKF otherwise its fusion could drag the yaw around
// and cause another navigation failure
_control_status.flags.mag_fault = true;
_warning_events.flags.emergency_yaw_reset_mag_stopped = true;
}
#if defined(CONFIG_EKF2_GNSS_YAW)
if (_control_status.flags.gps_yaw) {
_control_status.flags.gps_yaw_fault = true;
_warning_events.flags.emergency_yaw_reset_gps_yaw_stopped = true;
}
#endif // CONFIG_EKF2_GNSS_YAW
#if defined(CONFIG_EKF2_EXTERNAL_VISION)
if (_control_status.flags.ev_yaw) {
_control_status.flags.ev_yaw_fault = true;
}
#endif // CONFIG_EKF2_EXTERNAL_VISION
do_vel_pos_reset = true;
}
}
if (do_vel_pos_reset) {
ECL_WARN("GPS fusion timeout, resetting velocity and position");
if (gnss_vel_enabled) {
// reset velocity
_information_events.flags.reset_vel_to_gps = true;
resetVelocityTo(velocity, vel_obs_var);
_aid_src_gnss_vel.time_last_fuse = _time_delayed_us;
}
if (gnss_pos_enabled) {
// reset position
_information_events.flags.reset_pos_to_gps = true;
resetHorizontalPositionTo(position, pos_obs_var);
_aid_src_gnss_pos.time_last_fuse = _time_delayed_us;
}
}
} else {
stopGpsFusion();
_warning_events.flags.gps_quality_poor = true;
ECL_WARN("GPS quality poor - stopping use");
}
} else { // mandatory conditions are not passing
} else {
stopGpsFusion();
}
@@ -242,40 +155,146 @@ void Ekf::controlGpsFusion(const imuSample &imu_delayed)
) {
// reset velocity
if (gnss_vel_enabled) {
_information_events.flags.reset_vel_to_gps = true;
resetVelocityTo(velocity, vel_obs_var);
_aid_src_gnss_vel.time_last_fuse = _time_delayed_us;
resetVelocityToGnss(_aid_src_gnss_vel);
}
}
if (gnss_pos_enabled) {
// reset position
_information_events.flags.reset_pos_to_gps = true;
resetHorizontalPositionTo(position, pos_obs_var);
_gpos_origin_eph = 0.f; // The uncertainty of the global origin is now contained in the local position uncertainty
_aid_src_gnss_pos.time_last_fuse = _time_delayed_us;
resetHorizontalPositionToGnss(_aid_src_gnss_pos);
}
_control_status.flags.gps = true;
}
}
} else if (_control_status.flags.gps && !isNewestSampleRecent(_time_last_gps_buffer_push, (uint64_t)10e6)) {
stopGpsFusion();
_warning_events.flags.gps_data_stopped = true;
ECL_WARN("GPS data stopped");
} else if (_control_status.flags.gps && !isNewestSampleRecent(_time_last_gps_buffer_push, (uint64_t)1e6)
&& isOtherSourceOfHorizontalAidingThan(_control_status.flags.gps)) {
// Handle the case where we are fusing another position source along GPS,
// stop waiting for GPS after 1 s of lost signal
stopGpsFusion();
_warning_events.flags.gps_data_stopped_using_alternate = true;
ECL_WARN("GPS data stopped, using only EV, OF or air data");
}
}
void Ekf::updateGnssVel(const gnssSample &gnss_sample, estimator_aid_source3d_s &aid_src)
{
// correct velocity for offset relative to IMU
const Vector3f pos_offset_body = _params.gps_pos_body - _params.imu_pos_body;
const Vector3f vel_offset_body = _ang_rate_delayed_raw % pos_offset_body;
const Vector3f vel_offset_earth = _R_to_earth * vel_offset_body;
const Vector3f velocity = gnss_sample.vel - vel_offset_earth;
const float vel_var = sq(math::max(gnss_sample.sacc, _params.gps_vel_noise));
const Vector3f vel_obs_var(vel_var, vel_var, vel_var * sq(1.5f));
updateVelocityAidSrcStatus(gnss_sample.time_us,
velocity, // observation
vel_obs_var, // observation variance
math::max(_params.gps_vel_innov_gate, 1.f), // innovation gate
aid_src);
}
void Ekf::updateGnssPos(const gnssSample &gnss_sample, estimator_aid_source2d_s &aid_src)
{
// correct position and height for offset relative to IMU
const Vector3f pos_offset_body = _params.gps_pos_body - _params.imu_pos_body;
const Vector3f pos_offset_earth = _R_to_earth * pos_offset_body;
const Vector2f position = _pos_ref.project(gnss_sample.lat, gnss_sample.lon) - pos_offset_earth.xy();
// relax the upper observation noise limit which prevents bad GPS perturbing the position estimate
float pos_noise = math::max(gnss_sample.hacc, _params.gps_pos_noise);
if (!isOtherSourceOfHorizontalAidingThan(_control_status.flags.gps)) {
// if we are not using another source of aiding, then we are reliant on the GPS
// observations to constrain attitude errors and must limit the observation noise value.
if (pos_noise > _params.pos_noaid_noise) {
pos_noise = _params.pos_noaid_noise;
}
}
const float pos_var = sq(pos_noise);
const Vector2f pos_obs_var(pos_var, pos_var);
updateHorizontalPositionAidSrcStatus(gnss_sample.time_us,
position, // observation
pos_obs_var, // observation variance
math::max(_params.gps_pos_innov_gate, 1.f), // innovation gate
aid_src);
}
void Ekf::controlGnssYawEstimator(estimator_aid_source3d_s &aid_src_vel)
{
// update yaw estimator velocity (basic sanity check on GNSS velocity data)
const float vel_var = aid_src_vel.observation_variance[0];
const Vector2f vel_xy(aid_src_vel.observation);
if ((vel_var > 0.f)
&& (vel_var < _params.req_sacc)
&& vel_xy.isAllFinite()) {
_yawEstimator.setVelocity(vel_xy, vel_var);
// Try to align yaw using estimate if available
if (((_params.gnss_ctrl & static_cast<int32_t>(GnssCtrl::VEL))
|| (_params.gnss_ctrl & static_cast<int32_t>(GnssCtrl::HPOS)))
&& !_control_status.flags.yaw_align
&& _control_status.flags.tilt_align) {
if (resetYawToEKFGSF()) {
ECL_INFO("GPS yaw aligned using IMU");
}
}
}
}
bool Ekf::tryYawEmergencyReset()
{
bool success = false;
/* A rapid reset to the yaw emergency estimate is performed if horizontal velocity innovation checks continuously
* fails while the difference between the yaw emergency estimator and the yas estimate is large.
* This enables recovery from a bad yaw estimate. A reset is not performed if the fault condition was
* present before flight to prevent triggering due to GPS glitches or other sensor errors.
*/
if (resetYawToEKFGSF()) {
ECL_WARN("GPS emergency yaw reset");
if (_control_status.flags.mag_hdg || _control_status.flags.mag_3D) {
// stop using the magnetometer in the main EKF otherwise its fusion could drag the yaw around
// and cause another navigation failure
_control_status.flags.mag_fault = true;
_warning_events.flags.emergency_yaw_reset_mag_stopped = true;
}
#if defined(CONFIG_EKF2_GNSS_YAW)
if (_control_status.flags.gps_yaw) {
_control_status.flags.gps_yaw_fault = true;
_warning_events.flags.emergency_yaw_reset_gps_yaw_stopped = true;
}
#endif // CONFIG_EKF2_GNSS_YAW
#if defined(CONFIG_EKF2_EXTERNAL_VISION)
if (_control_status.flags.ev_yaw) {
_control_status.flags.ev_yaw_fault = true;
}
#endif // CONFIG_EKF2_EXTERNAL_VISION
success = true;
}
return success;
}
void Ekf::resetVelocityToGnss(estimator_aid_source3d_s &aid_src)
{
_information_events.flags.reset_vel_to_gps = true;
resetVelocityTo(Vector3f(aid_src.observation), Vector3f(aid_src.observation_variance));
aid_src.time_last_fuse = _time_delayed_us;
}
void Ekf::resetHorizontalPositionToGnss(estimator_aid_source2d_s &aid_src)
{
_information_events.flags.reset_pos_to_gps = true;
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
aid_src.time_last_fuse = _time_delayed_us;
}
bool Ekf::shouldResetGpsFusion() const
{
/* We are relying on aiding to constrain drift so after a specified time
@@ -308,7 +327,7 @@ bool Ekf::shouldResetGpsFusion() const
}
#if defined(CONFIG_EKF2_GNSS_YAW)
void Ekf::controlGpsYawFusion(const gpsSample &gps_sample, bool gps_checks_passing, bool gps_checks_failing)
void Ekf::controlGpsYawFusion(const gnssSample &gps_sample)
{
if (!(_params.gnss_ctrl & static_cast<int32_t>(GnssCtrl::YAW))
|| _control_status.flags.gps_yaw_fault) {
@@ -323,14 +342,13 @@ void Ekf::controlGpsYawFusion(const gpsSample &gps_sample, bool gps_checks_passi
if (is_new_data_available) {
const bool continuing_conditions_passing = !gps_checks_failing;
const bool continuing_conditions_passing = _control_status.flags.tilt_align;
const bool is_gps_yaw_data_intermittent = !isNewestSampleRecent(_time_last_gps_yaw_buffer_push,
2 * GNSS_YAW_MAX_INTERVAL);
const bool starting_conditions_passing = continuing_conditions_passing
&& _control_status.flags.tilt_align
&& gps_checks_passing
&& _gps_checks_passed
&& !is_gps_yaw_data_intermittent
&& !_gps_intermittent;
@@ -338,14 +356,14 @@ void Ekf::controlGpsYawFusion(const gpsSample &gps_sample, bool gps_checks_passi
if (continuing_conditions_passing) {
fuseGpsYaw();
fuseGpsYaw(gps_sample.yaw_offset);
const bool is_fusion_failing = isTimedOut(_aid_src_gnss_yaw.time_last_fuse, _params.reset_timeout_max);
if (is_fusion_failing) {
if (_nb_gps_yaw_reset_available > 0) {
// Data seems good, attempt a reset
resetYawToGps(gps_sample.yaw);
resetYawToGps(gps_sample.yaw, gps_sample.yaw_offset);
if (_control_status.flags.in_air) {
_nb_gps_yaw_reset_available--;
@@ -374,7 +392,7 @@ void Ekf::controlGpsYawFusion(const gpsSample &gps_sample, bool gps_checks_passi
} else {
if (starting_conditions_passing) {
// Try to activate GPS yaw fusion
if (resetYawToGps(gps_sample.yaw)) {
if (resetYawToGps(gps_sample.yaw, gps_sample.yaw_offset)) {
ECL_INFO("starting GPS yaw fusion");
_aid_src_gnss_yaw.time_last_fuse = _time_delayed_us;
@@ -420,6 +438,8 @@ void Ekf::stopGpsFusion()
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_pass_us = 0;
_control_status.flags.gps = false;
}
+13 -8
View File
@@ -46,7 +46,7 @@
#include <mathlib/mathlib.h>
#include <cstdlib>
void Ekf::updateGpsYaw(const gpsSample &gps_sample)
void Ekf::updateGpsYaw(const gnssSample &gps_sample)
{
if (PX4_ISFINITE(gps_sample.yaw)) {
@@ -56,16 +56,17 @@ void Ekf::updateGpsYaw(const gpsSample &gps_sample)
// 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
const float measured_hdg = wrap_pi(gps_sample.yaw + _gps_yaw_offset);
const float measured_hdg = wrap_pi(gps_sample.yaw + gps_sample.yaw_offset);
const float R_YAW = sq(fmaxf(gps_sample.yaw_acc, _params.gps_heading_noise));
const float yaw_acc = PX4_ISFINITE(gps_sample.yaw_acc) ? gps_sample.yaw_acc : 0.f;
const float R_YAW = sq(fmaxf(yaw_acc, _params.gps_heading_noise));
float heading_pred;
float heading_innov_var;
{
VectorState H;
sym::ComputeGnssYawPredInnovVarAndH(_state.vector(), P, _gps_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;
@@ -80,7 +81,7 @@ void Ekf::updateGpsYaw(const gpsSample &gps_sample)
}
}
void Ekf::fuseGpsYaw()
void Ekf::fuseGpsYaw(float antenna_yaw_offset)
{
auto &gnss_yaw = _aid_src_gnss_yaw;
@@ -89,6 +90,10 @@ void Ekf::fuseGpsYaw()
return;
}
if (!PX4_ISFINITE(antenna_yaw_offset)) {
antenna_yaw_offset = 0.f;
}
VectorState H;
{
@@ -97,7 +102,7 @@ void Ekf::fuseGpsYaw()
// 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
sym::ComputeGnssYawPredInnovVarAndH(_state.vector(), P, _gps_yaw_offset, gnss_yaw.observation_variance, FLT_EPSILON, &heading_pred, &heading_innov_var, &H);
sym::ComputeGnssYawPredInnovVarAndH(_state.vector(), P, antenna_yaw_offset, gnss_yaw.observation_variance, FLT_EPSILON, &heading_pred, &heading_innov_var, &H);
}
// check if the innovation variance calculation is badly conditioned
@@ -139,10 +144,10 @@ void Ekf::fuseGpsYaw()
}
}
bool Ekf::resetYawToGps(const float gnss_yaw)
bool Ekf::resetYawToGps(const float gnss_yaw, const float gnss_yaw_offset)
{
// define the predicted antenna array vector and rotate into earth frame
const Vector3f ant_vec_bf = {cosf(_gps_yaw_offset), sinf(_gps_yaw_offset), 0.0f};
const Vector3f ant_vec_bf = {cosf(gnss_yaw_offset), sinf(gnss_yaw_offset), 0.0f};
const Vector3f ant_vec_ef = _R_to_earth * ant_vec_bf;
// check if antenna array vector is within 30 degrees of vertical and therefore unable to provide a reliable heading
+26 -20
View File
@@ -2435,32 +2435,38 @@ void EKF2::UpdateGpsSample(ekf2_timestamps_s &ekf2_timestamps)
perf_count(_msg_missed_gps_perf);
}
gpsMessage gps_msg{
.time_usec = vehicle_gps_position.timestamp,
.lat = static_cast<int32_t>(round(vehicle_gps_position.latitude_deg * 1e7)),
.lon = static_cast<int32_t>(round(vehicle_gps_position.longitude_deg * 1e7)),
.alt = static_cast<int32_t>(round(vehicle_gps_position.altitude_msl_m * 1e3)),
.yaw = vehicle_gps_position.heading,
.yaw_offset = vehicle_gps_position.heading_offset,
.yaw_accuracy = vehicle_gps_position.heading_accuracy,
.fix_type = vehicle_gps_position.fix_type,
.eph = vehicle_gps_position.eph,
.epv = vehicle_gps_position.epv,
Vector3f vel_ned;
if (vehicle_gps_position.vel_ned_valid) {
vel_ned = Vector3f(vehicle_gps_position.vel_n_m_s,
vehicle_gps_position.vel_e_m_s,
vehicle_gps_position.vel_d_m_s);
} else {
return; //TODO: change and set to NAN
}
gnssSample gnss_sample{
.time_us = vehicle_gps_position.timestamp,
.lat = vehicle_gps_position.latitude_deg,
.lon = vehicle_gps_position.longitude_deg,
.alt = static_cast<float>(vehicle_gps_position.altitude_msl_m),
.vel = vel_ned,
.hacc = vehicle_gps_position.eph,
.vacc = vehicle_gps_position.epv,
.sacc = vehicle_gps_position.s_variance_m_s,
.vel_m_s = vehicle_gps_position.vel_m_s,
.vel_ned = Vector3f{
vehicle_gps_position.vel_n_m_s,
vehicle_gps_position.vel_e_m_s,
vehicle_gps_position.vel_d_m_s
},
.vel_ned_valid = vehicle_gps_position.vel_ned_valid,
.fix_type = vehicle_gps_position.fix_type,
.nsats = vehicle_gps_position.satellites_used,
.pdop = sqrtf(vehicle_gps_position.hdop *vehicle_gps_position.hdop
+ vehicle_gps_position.vdop * vehicle_gps_position.vdop),
.yaw = vehicle_gps_position.heading, //TODO: move to different message
.yaw_acc = vehicle_gps_position.heading_accuracy,
.yaw_offset = vehicle_gps_position.heading_offset,
};
_ekf.setGpsData(gps_msg);
_gps_time_usec = gps_msg.time_usec;
_ekf.setGpsData(gnss_sample);
_gps_time_usec = gnss_sample.time_us;
_gps_alttitude_ellipsoid = static_cast<int32_t>(round(vehicle_gps_position.altitude_ellipsoid_m * 1e3));
}
}
+20 -22
View File
@@ -15,9 +15,9 @@ Gps::~Gps()
void Gps::send(const uint64_t time)
{
const float dt = static_cast<float>(time - _gps_data.time_usec) * 1e-6f;
const float dt = static_cast<float>(time - _gps_data.time_us) * 1e-6f;
_gps_data.time_usec = time;
_gps_data.time_us = time;
if (fabsf(_gps_pos_rate(0)) > FLT_EPSILON || fabsf(_gps_pos_rate(1)) > FLT_EPSILON) {
stepHorizontalPositionByMeters(Vector2f(_gps_pos_rate) * dt);
@@ -30,29 +30,29 @@ void Gps::send(const uint64_t time)
_ekf->setGpsData(_gps_data);
}
void Gps::setData(const gpsMessage &gps)
void Gps::setData(const gnssSample &gps)
{
_gps_data = gps;
}
void Gps::setAltitude(const int32_t alt)
void Gps::setAltitude(const float alt)
{
_gps_data.alt = alt;
}
void Gps::setLatitude(const int32_t lat)
void Gps::setLatitude(const double lat)
{
_gps_data.lat = lat;
}
void Gps::setLongitude(const int32_t lon)
void Gps::setLongitude(const double lon)
{
_gps_data.lon = lon;
}
void Gps::setVelocity(const Vector3f &vel)
{
_gps_data.vel_ned = vel;
_gps_data.vel = vel;
}
void Gps::setYaw(const float yaw)
@@ -87,7 +87,7 @@ void Gps::setPositionRateNED(const Vector3f &rate)
void Gps::stepHeightByMeters(const float hgt_change)
{
_gps_data.alt += hgt_change * 1e3f;
_gps_data.alt += hgt_change;
}
void Gps::stepHorizontalPositionByMeters(const Vector2f hpos_change)
@@ -98,32 +98,30 @@ void Gps::stepHorizontalPositionByMeters(const Vector2f hpos_change)
double lat_new {0.0};
double lon_new {0.0};
_ekf->global_origin().project(_gps_data.lat * 1e-7, _gps_data.lon * 1e-7, hposN_curr, hposE_curr);
_ekf->global_origin().project(_gps_data.lat, _gps_data.lon, hposN_curr, hposE_curr);
Vector2f hpos_new = Vector2f{hposN_curr, hposE_curr} + hpos_change;
_ekf->global_origin().reproject(hpos_new(0), hpos_new(1), lat_new, lon_new);
_gps_data.lon = static_cast<int32_t>(lon_new * 1e7);
_gps_data.lat = static_cast<int32_t>(lat_new * 1e7);
_gps_data.lon = lon_new;
_gps_data.lat = lat_new;
}
gpsMessage Gps::getDefaultGpsData()
gnssSample Gps::getDefaultGpsData()
{
gpsMessage gps_data{};
gps_data.time_usec = 0;
gps_data.lat = 473566094;
gps_data.lon = 85190237;
gps_data.alt = 422056;
gnssSample gps_data{};
gps_data.time_us = 0;
gps_data.lat = 47.3566094;
gps_data.lon = 8.5190237;
gps_data.alt = 422.056f;
gps_data.yaw = NAN;
gps_data.yaw_offset = 0.0f;
gps_data.fix_type = 3;
gps_data.eph = 0.5f;
gps_data.epv = 0.8f;
gps_data.hacc = 0.5f;
gps_data.vacc = 0.8f;
gps_data.sacc = 0.2f;
gps_data.vel_m_s = 0.0;
gps_data.vel_ned.setZero();
gps_data.vel_ned_valid = 1;
gps_data.vel.setZero();
gps_data.nsats = 16;
gps_data.pdop = 0.0f;
+6 -6
View File
@@ -51,13 +51,13 @@ public:
Gps(std::shared_ptr<Ekf> ekf);
~Gps();
void setData(const gpsMessage &gps);
void setData(const gnssSample &gps);
void stepHeightByMeters(const float hgt_change);
void stepHorizontalPositionByMeters(const Vector2f hpos_change);
void setPositionRateNED(const Vector3f &rate);
void setAltitude(const int32_t alt);
void setLatitude(const int32_t lat);
void setLongitude(const int32_t lon);
void setAltitude(const float alt);
void setLatitude(const double lat);
void setLongitude(const double lon);
void setVelocity(const Vector3f &vel);
void setYaw(const float yaw);
void setYawOffset(const float yaw);
@@ -65,12 +65,12 @@ public:
void setNumberOfSatellites(const int num_satellites);
void setPdop(const float pdop);
gpsMessage getDefaultGpsData();
gnssSample getDefaultGpsData();
private:
void send(uint64_t time) override;
gpsMessage _gps_data{};
gnssSample _gps_data{};
Vector3f _gps_pos_rate{};
};
@@ -395,20 +395,17 @@ void SensorSimulator::setSensorDataFromTrajectory()
void SensorSimulator::setGpsLatitude(const double latitude)
{
int32_t lat = static_cast<int32_t>(latitude * 1e7);
_gps.setLatitude(lat);
_gps.setLatitude(latitude);
}
void SensorSimulator::setGpsLongitude(const double longitude)
{
int32_t lon = static_cast<int32_t>(longitude * 1e7);
_gps.setLongitude(lon);
_gps.setLongitude(longitude);
}
void SensorSimulator::setGpsAltitude(const float altitude)
{
int32_t alt = static_cast<int32_t>(altitude * 1e3f);
_gps.setAltitude(alt);
_gps.setAltitude(altitude);
}
void SensorSimulator::setImuBias(Vector3f accel_bias, Vector3f gyro_bias)
@@ -195,15 +195,15 @@ TEST_F(EkfFusionLogicTest, fallbackFromGpsToFlow)
// WHEN: GPS data stops
_sensor_simulator.stopGps();
_sensor_simulator.runSeconds(2);
_sensor_simulator.runSeconds(10);
// THEN: immediately switch to flow only
// THEN: GNSS fusion stops after a timing out
EXPECT_FALSE(_ekf_wrapper.isIntendingGpsFusion());
EXPECT_TRUE(_ekf_wrapper.isIntendingFlowFusion());
// BUT WHEN: GPS starts again
// BUT WHEN: GPS starts after passing the checks again
_sensor_simulator.startGps();
_sensor_simulator.runSeconds(1);
_sensor_simulator.runSeconds(6);
// THEN: use it again
EXPECT_TRUE(_ekf_wrapper.isIntendingGpsFusion());
+11 -22
View File
@@ -84,28 +84,16 @@ TEST_F(EkfGpsTest, gpsTimeout)
// WHEN: the number of satellites drops below the minimum
_sensor_simulator._gps.setNumberOfSatellites(3);
// THEN: the GNSS fusion continues because it is the only source of position/velocity
_sensor_simulator.runSeconds(20);
EXPECT_TRUE(_ekf_wrapper.isIntendingGpsFusion());
// BUT: if we have another velocity aiding source
const float max_flow_rate = 5.f;
const float min_ground_distance = 0.f;
const float max_ground_distance = 50.f;
_ekf->set_optical_flow_limits(max_flow_rate, min_ground_distance, max_ground_distance);
_sensor_simulator._flow.setData(_sensor_simulator._flow.dataAtRest());
_ekf_wrapper.enableFlowFusion();
_sensor_simulator.startFlow();
_sensor_simulator._rng.setData(0.2, 100);
_sensor_simulator._rng.setLimits(0.1f, 9.f);
_sensor_simulator.startRangeFinder();
_sensor_simulator.runSeconds(5);
// THEN: the GNSS fusion stops
EXPECT_TRUE(_ekf_wrapper.isIntendingFlowFusion());
// THEN: the GNSS fusion stops after some time
_sensor_simulator.runSeconds(8);
EXPECT_FALSE(_ekf_wrapper.isIntendingGpsFusion());
// BUT WHEN: the number of satellites is good again
_sensor_simulator._gps.setNumberOfSatellites(16);
// THEN: the GNSS fusion restarts
_sensor_simulator.runSeconds(6);
EXPECT_TRUE(_ekf_wrapper.isIntendingGpsFusion());
}
TEST_F(EkfGpsTest, gpsFixLoss)
@@ -148,6 +136,7 @@ TEST_F(EkfGpsTest, resetToGpsVelocity)
_ekf->set_in_air_status(true);
_ekf->set_vehicle_at_rest(false);
_sensor_simulator.runSeconds(5.2); // required to pass the checks
_sensor_simulator.runMicroseconds(dt_us);
// THEN: a reset to GPS velocity should be done
@@ -178,7 +167,7 @@ TEST_F(EkfGpsTest, resetToGpsPosition)
const Vector3f simulated_position_change(2.0f, -1.0f, 0.f);
_sensor_simulator._gps.stepHorizontalPositionByMeters(
Vector2f(simulated_position_change));
_sensor_simulator.runMicroseconds(1e5);
_sensor_simulator.runSeconds(6);
// THEN: a reset to the new GPS position should be done
const Vector3f estimated_position = _ekf->getPosition();
@@ -81,8 +81,7 @@ public:
_ekf->set_vehicle_at_rest(true);
_ekf_wrapper.enableGpsFusion();
_sensor_simulator.runSeconds(2); // Run to pass the GPS checks
_sensor_simulator.runSeconds(3.5); // And a bit more to start the GPS fusion TODO: this shouldn't be necessary
_sensor_simulator.runSeconds(1.5); // Run to pass the GPS checks
EXPECT_TRUE(_ekf_wrapper.isIntendingGpsFusion());
const Vector3f simulated_velocity(0.5f, -1.0f, 0.f);