-
Notifications
You must be signed in to change notification settings - Fork 15.5k
refactor(ekf2): separate GNSS heading from position into independent topic #27102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ec556f1
5bdb5d0
a5f3726
33fc150
63dabc7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Validated GNSS heading from dual-antenna / moving baseline receiver pair. | ||
| # Published by the sensors module after selecting and validating heading from sensor_gnss_relative | ||
| # or falling back to sensor_gps heading fields. Consumed by EKF2 independently from position/velocity. | ||
|
|
||
| uint64 timestamp # time since system start (microseconds) | ||
| uint64 timestamp_sample # time since system start (microseconds) - actual measurement time | ||
|
|
||
| uint32 device_id # unique device ID for the sensor that does not change between power cycles | ||
|
|
||
| float32 heading # NED heading from dual antenna array (rad, [-PI, PI]) | ||
| float32 heading_accuracy # 1-sigma heading accuracy (rad) | ||
| float32 heading_offset # body-frame offset of antenna array (rad, [-PI, PI]) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,6 +48,9 @@ EstimatorInterface::~EstimatorInterface() | |
| { | ||
| #if defined(CONFIG_EKF2_GNSS) | ||
| delete _gps_buffer; | ||
| # if defined(CONFIG_EKF2_GNSS_YAW) | ||
| delete _gnss_yaw_buffer; | ||
| # endif // CONFIG_EKF2_GNSS_YAW | ||
| #endif // CONFIG_EKF2_GNSS | ||
| #if defined(CONFIG_EKF2_MAGNETOMETER) | ||
| delete _mag_buffer; | ||
|
|
@@ -192,19 +195,47 @@ void EstimatorInterface::setGpsData(const gnssSample &gnss_sample) | |
| _gps_buffer->push(gnss_sample_new); | ||
| _time_last_gps_buffer_push = _time_latest_us; | ||
|
|
||
| } else { | ||
| ECL_WARN("GPS data too fast %" PRIi64 " < %" PRIu64 " + %d", time_us, _gps_buffer->get_newest().time_us, | ||
| _min_obs_interval_us); | ||
| } | ||
| } | ||
|
|
||
| #if defined(CONFIG_EKF2_GNSS_YAW) | ||
| void EstimatorInterface::setGnssYawData(const gnssYawSample &gnss_yaw_sample) | ||
| { | ||
| if (!_initialised) { | ||
| return; | ||
| } | ||
|
|
||
| if (_gnss_yaw_buffer == nullptr) { | ||
| _gnss_yaw_buffer = new TimestampedRingBuffer<gnssYawSample>(_obs_buffer_length); | ||
|
|
||
| if (PX4_ISFINITE(gnss_sample.yaw)) { | ||
| _time_last_gnss_yaw_buffer_push = _time_latest_us; | ||
| if (_gnss_yaw_buffer == nullptr || !_gnss_yaw_buffer->valid()) { | ||
| delete _gnss_yaw_buffer; | ||
| _gnss_yaw_buffer = nullptr; | ||
| printBufferAllocationFailed("GNSS yaw"); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| #endif // CONFIG_EKF2_GNSS_YAW | ||
| const int64_t time_us = gnss_yaw_sample.time_us - static_cast<int64_t>(_dt_ekf_avg * 5e5f); | ||
|
|
||
| if (time_us >= static_cast<int64_t>(_gnss_yaw_buffer->get_newest().time_us + _min_obs_interval_us)) { | ||
|
|
||
| gnssYawSample sample_new(gnss_yaw_sample); | ||
| sample_new.time_us = time_us; | ||
|
|
||
| _gnss_yaw_buffer->push(sample_new); | ||
| _time_last_gnss_yaw_buffer_push = _time_latest_us; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| } else { | ||
| ECL_WARN("GPS data too fast %" PRIi64 " < %" PRIu64 " + %d", time_us, _gps_buffer->get_newest().time_us, | ||
| _min_obs_interval_us); | ||
| ECL_WARN("GNSS yaw data too fast %" PRIi64 " < %" PRIu64 " + %d", time_us, | ||
| _gnss_yaw_buffer->get_newest().time_us, _min_obs_interval_us); | ||
| } | ||
| } | ||
| #endif // CONFIG_EKF2_GNSS_YAW | ||
|
|
||
| #endif // CONFIG_EKF2_GNSS | ||
|
|
||
| #if defined(CONFIG_EKF2_BAROMETER) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -807,6 +807,9 @@ void EKF2::Run() | |
| #endif // CONFIG_EKF2_OPTICAL_FLOW | ||
| #if defined(CONFIG_EKF2_GNSS) | ||
| UpdateGpsSample(ekf2_timestamps); | ||
| # if defined(CONFIG_EKF2_GNSS_YAW) | ||
| UpdateGnssYawSample(ekf2_timestamps); | ||
| # endif // CONFIG_EKF2_GNSS_YAW | ||
| #endif // CONFIG_EKF2_GNSS | ||
| #if defined(CONFIG_EKF2_MAGNETOMETER) | ||
| UpdateMagSample(ekf2_timestamps); | ||
|
|
@@ -2623,15 +2626,6 @@ void EKF2::UpdateGpsSample(ekf2_timestamps_s &ekf2_timestamps) | |
| return; //TODO: change and set to NAN | ||
| } | ||
|
|
||
| if (fabsf(_param_ekf2_gps_yaw_off.get()) > 0.f) { | ||
| if (!PX4_ISFINITE(vehicle_gps_position.heading_offset) && PX4_ISFINITE(vehicle_gps_position.heading)) { | ||
| // Apply offset | ||
| float yaw_offset = matrix::wrap_pi(math::radians(_param_ekf2_gps_yaw_off.get())); | ||
| vehicle_gps_position.heading_offset = yaw_offset; | ||
| vehicle_gps_position.heading = matrix::wrap_pi(vehicle_gps_position.heading - yaw_offset); | ||
| } | ||
| } | ||
|
|
||
| const float altitude_amsl = static_cast<float>(vehicle_gps_position.altitude_msl_m); | ||
| const float altitude_ellipsoid = static_cast<float>(vehicle_gps_position.altitude_ellipsoid_m); | ||
|
|
||
|
|
@@ -2652,9 +2646,6 @@ void EKF2::UpdateGpsSample(ekf2_timestamps_s &ekf2_timestamps) | |
| .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, | ||
| .spoofed = vehicle_gps_position.spoofing_state == sensor_gps_s::SPOOFING_STATE_DETECTED, | ||
| .jammed = vehicle_gps_position.jamming_state == sensor_gps_s::JAMMING_STATE_DETECTED, | ||
| .pos_body = Vector3f(vehicle_gps_position.antenna_offset_x, | ||
|
|
@@ -2680,6 +2671,40 @@ void EKF2::UpdateGpsSample(ekf2_timestamps_s &ekf2_timestamps) | |
| } | ||
| } | ||
|
|
||
| #if defined(CONFIG_EKF2_GNSS_YAW) | ||
| void EKF2::UpdateGnssYawSample(ekf2_timestamps_s &ekf2_timestamps) | ||
| { | ||
| vehicle_gnss_heading_s gnss_heading; | ||
|
|
||
| if (_vehicle_gnss_heading_sub.update(&gnss_heading)) { | ||
|
|
||
|
Comment on lines
+2674
to
+2680
|
||
| float yaw = gnss_heading.heading; | ||
| float yaw_offset = gnss_heading.heading_offset; | ||
|
|
||
| // Apply EKF2_GPS_YAW_OFF if the driver didn't set an offset | ||
| if (fabsf(_param_ekf2_gps_yaw_off.get()) > 0.f) { | ||
| if (!PX4_ISFINITE(yaw_offset) && PX4_ISFINITE(yaw)) { | ||
| yaw_offset = matrix::wrap_pi(math::radians(_param_ekf2_gps_yaw_off.get())); | ||
| yaw = matrix::wrap_pi(yaw - yaw_offset); | ||
| } | ||
| } | ||
|
|
||
| if (!PX4_ISFINITE(yaw_offset)) { | ||
| yaw_offset = 0.f; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This |
||
| } | ||
|
|
||
| gnssYawSample gnss_yaw_sample{ | ||
| .time_us = (gnss_heading.timestamp_sample > 0) ? gnss_heading.timestamp_sample : gnss_heading.timestamp, | ||
| .yaw = yaw, | ||
| .yaw_acc = gnss_heading.heading_accuracy, | ||
| .yaw_offset = yaw_offset, | ||
| }; | ||
|
|
||
| _ekf.setGnssYawData(gnss_yaw_sample); | ||
| } | ||
| } | ||
| #endif // CONFIG_EKF2_GNSS_YAW | ||
|
|
||
| float EKF2::altEllipsoidToAmsl(float ellipsoid_alt) const | ||
| { | ||
| return ellipsoid_alt - _geoid_height_lpf.getState(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GNSS yaw fusion is now popped independently from the position buffer, but
starting_conditions_passingstill requires_gnss_checks.passed(), which is driven by the position/velocitygnssSamplequality gates (nsats/PDOP/EPH/…). This keeps the heading startup coupled to position quality and contradicts the stated decoupling goal. If the intent is truly independent yaw fusion, consider removing this dependency or replacing it with yaw-specific validity checks (eg based onheading_accuracy/ a dedicated heading health check).