-
Notifications
You must be signed in to change notification settings - Fork 13.7k
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
ekf2: Add support for use of multiple GPS receivers #9765
Conversation
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.
@priseborough I'm 80% done with with review but need to run to another project now. Will finish this after work.
src/modules/ekf2/ekf2_main.cpp
Outdated
_gps_select_index = 1; | ||
|
||
} else if (_gps_select_index == 2) { | ||
// use last recever we received data from |
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.
typo
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.
will fix
src/modules/ekf2/ekf2_main.cpp
Outdated
@@ -572,6 +628,13 @@ void Ekf2::run() | |||
{ | |||
bool imu_bias_reset_request = false; | |||
|
|||
// becasue we can have a second GPS used as a truth reference to asisst with tuning and algorithm development using replay |
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.
typo
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.
will fix
src/modules/ekf2/ekf2_main.cpp
Outdated
|
||
for (uint8_t i = 0; i < GPS_MAX_RECEIVERS; i++) { | ||
if (_gps_state[i].fix_type >= 3 && _gps_state[i].sacc >= 0.001f) { | ||
spd_blend_weights[i] = speed_accuracy_sum_sq / (_gps_state[i].sacc * _gps_state[i].sacc); |
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.
@priseborough Just for my understanding. You could also write
spd_blend_weights[i] = 1.0f / (_gps_state[i].sacc * _gps_state[i].sacc);
correct? It should not matter because below you are normalizing the weights again.
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.
yes that is correct
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.
fixed
src/modules/ekf2/ekf2_main.cpp
Outdated
// report the best valid accuracies and DOP metrics | ||
|
||
if (_gps_state[i].eph > 0.0f | ||
&& _gps_state[i].eph < _gps_state[GPS_BLENDED_INSTANCE].eph) { |
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.
@priseborough Did you mean to write
_gps_state[i].eph > _gps_state[GPS_BLENDED_INSTANCE].eph
?
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.
We need to take the smallest non zero value so that won't work either. I need to initialise _gps_state[GPS_BLENDED_INSTANCE].eph to the largest float.
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.
fixed
src/modules/ekf2/ekf2_main.cpp
Outdated
} | ||
|
||
if (_gps_state[i].epv > 0.0f | ||
&& _gps_state[i].epv < _gps_state[GPS_BLENDED_INSTANCE].epv) { |
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.
@priseborough Also here and all the other cases below.
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.
fixed
src/modules/ekf2/ekf2_main.cpp
Outdated
@@ -572,6 +628,13 @@ void Ekf2::run() | |||
{ | |||
bool imu_bias_reset_request = false; | |||
|
|||
// support for dual GPS inputs if available | |||
int gps_subs[2]; |
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.
delete? _gps_subs in class
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.
I don't understand
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.
You have both gps_subs[2] and _gps_subs[ORB_MULTI_MAX_INSTANCES].
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.
I thought I needed a separate one for publishing. will fix.
src/modules/ekf2/ekf2_main.cpp
Outdated
int gps_subs[2]; | ||
|
||
for (unsigned i = 0; i < 2; i++) { | ||
gps_subs[i] = orb_subscribe_multi(ORB_ID(vehicle_gps_position), i); |
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.
Should be with _gps_subs in constructor.
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.
will fix
src/modules/ekf2/ekf2_main.cpp
Outdated
@@ -512,6 +565,8 @@ Ekf2::Ekf2(): | |||
_vehicle_land_detected_sub = orb_subscribe(ORB_ID(vehicle_land_detected)); | |||
|
|||
for (unsigned i = 0; i < ORB_MULTI_MAX_INSTANCES; i++) { | |||
_gps_subs[i] = -1; | |||
_gps_subs[i] = orb_subscribe_multi(ORB_ID(vehicle_gps_position), i); |
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.
GPS_MAX_RECEIVERS for _gps_subs, not ORB_MULTI_MAX_INSTANCES.
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.
fixed
src/modules/ekf2/ekf2_main.cpp
Outdated
@@ -584,6 +647,12 @@ void Ekf2::run() | |||
vehicle_status_s vehicle_status = {}; | |||
sensor_selection_s sensor_selection = {}; | |||
|
|||
// Position of local NED origin in GPS / WGS84 frame | |||
map_projection_reference_s ekf_origin = {}; |
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.
Move back?
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.
I don't understand
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.
@dagar Can you elaborate on this?
src/modules/ekf2/ekf2_main.cpp
Outdated
|
||
ekf2_timestamps.gps_timestamp_rel = (int16_t)((int64_t)gps.timestamp / 100 - (int64_t)ekf2_timestamps.timestamp / 100); | ||
} | ||
} | ||
|
||
// check for second GPS receiver data | ||
bool gps2_updated = false; | ||
orb_check(gps_subs[1], &gps2_updated); |
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.
gps_subs[1] -> _gps_subs[1]
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.
will fix
src/modules/ekf2/ekf2_main.cpp
Outdated
@@ -165,11 +198,21 @@ class Ekf2 final : public ModuleBase<Ekf2>, public ModuleParams | |||
const float _vel_innov_spike_lim = 2.0f * _vel_innov_test_lim; ///< preflight velocity innovation spike limit (m/sec) | |||
const float _hgt_innov_spike_lim = 2.0f * _hgt_innov_test_lim; ///< preflight position innovation spike limit (m) | |||
|
|||
// GPS blending and switching | |||
struct gps_message _gps_state[GPS_MAX_RECEIVERS + 1]; ///< internal state data for the physical and blended GPS |
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.
Suggestion, the code below might be a bit easier to follow if you kept the blended GPS in a separate struct rather than the end.
_gps_state[GPS_BLENDED_INSTANCE] -> _gps_blended
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.
fixed
src/modules/ekf2/ekf2_main.cpp
Outdated
@@ -165,11 +198,21 @@ class Ekf2 final : public ModuleBase<Ekf2>, public ModuleParams | |||
const float _vel_innov_spike_lim = 2.0f * _vel_innov_test_lim; ///< preflight velocity innovation spike limit (m/sec) | |||
const float _hgt_innov_spike_lim = 2.0f * _hgt_innov_test_lim; ///< preflight position innovation spike limit (m) | |||
|
|||
// GPS blending and switching | |||
struct gps_message _gps_state[GPS_MAX_RECEIVERS + 1]; ///< internal state data for the physical and blended GPS | |||
struct gps_message _gps_output[GPS_MAX_RECEIVERS + 1]; ///< output state data for the physical and blended GPS |
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.
Why is it necessary to store _gps_output? Couldn't it be discarded at the end of each update_gps_blend_states?
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.
We can't discard it until after GPS data has been written to the EKF buffer.
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.
I will refactor the code to remove the need for that class variable. Refactoring needs to happen anyway because there is a corner case that is not being handled correctly at the moment that results in the output not updating if one of the receivers loses lock. This was picked up in dynamic testing.
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.
I've split the calc_gps_blend_outputs into 3 functions for updating of receiver offsets, application of receiver offsets and calculation of blended receiver output.
I'm using a class variable for outputs because I am unsure how to pass an array of structs by reference without creating a new type, and the compiler would not let me #include and use std::array to create a new type
@priseborough I'm done with review (can't approve on my phone). We will test it tomorrow. |
027b2b1
to
115711a
Compare
There was a bug found during dynamic ground testing that caused the GPS output state to not be updated f one of the receivers was disconnected or lost lock. I've split the calc_gps_blend_outputs() function into three separate functions for updating steady state (filtered) offsets, correcting physical receiver data using the offsets and calculating a blended output using the corrected receiver data. Testing on replay shows the algorithm is working properly. However, although printing the lat and lon values in the gps struct one line above where they are published shows the correct values, when I review the log generated by the ekf2 replay, the vehicle_gps_position_2.lat and lon values are not correct so I have a mistake in the publishing of the blended GPS. |
The CI is showing px4fmu v2 to be out of flash space. |
Bench testing at commit 935b701 shows logging of the third (blended) GPS instance working as expected, so the logging problem appears to a replay issue. The remaining issue is the large initialisation error in the blended solution for lat and lon that takes several seconds to remove - see bench test result below: Replaying the same data gives a much smaller startup transient - see |
Last commit 79f41ce seems to have resolved remaining issues. Here are GPS alt, lat and lon from the last test: |
After experimenting with some logging options with bench testing, it appears that unless estimator replay data logging is enabled via SDLOG_PROFILE, then the GPS blending does not work correctly and the blended instance logged closely tracks the second receiver . I suspect that publishing of the blended instance could be overwriting data from a physical receiver. |
@priseborough We did two flights with dual gps: In the first one something went wrong, one gps position was showing showing somewhere in the pacific The second try was good, we managed to fly in position control mode. Maybe it was drifting a bit more than usual but it might not be related to the changes I haven't had a chance to look into them. |
I hope that when this support is in system through to QGC "someone" will let me know so we can update the docs. |
@hamishwillee Good reminder. We will be setting the parameters so that the feature is disabled by default. Is there a way to flag a feature in master as 'beta' until we are happy it has had enough community test coverage? |
There is no "formal" way. What I normally do is either Is there any specialized QGC UI for setup, or is this just going to be a parameter? Obviously we now have to explain what ports you can add the second GPS to, how to turn it on, any tuning, and how to turn off anything else that might be using the port. I would probably add doc here: https://docs.px4.io/en/gps_compass/, and possibly also here: https://docs.px4.io/en/advanced_features/ |
@hamish Just one parameter for most applications. There is second time constant parameter controlling how fast the individual GPS offsets are corrected, but I am hoping we can tune that so that 99% of users don't need to touch it. |
@RomanBapst The first log appears to be afflicted by the same initialisation bug affecting the lat and lon output that I was chasing and fixed yesterday. The second log shows the blending working as expected for alt, lat, lon and velocity other than 0.4m 'switching' noise on the lat/lon output that appears at certain times. Running the data through the latest version reproduces the noise so this can be investigated and fixed. It does not affect the altitude, so is not an issue with the weighting calculation and appears to be a problem in the way global coordinates are converted to local position deltas and vice versa. Data from the the second flight: |
Thanks. YOu could put in a Note in the param definition itself warning that the feature is still not robust, but you would have to remove it later. I suspect users will also need to use a param to turn off whatever is using the port to which you attach the second GPS. ie SYS_COMPANION |
Writing the blended GPS to a third instance of the GPS message is causing problems on replay with the lat,lon values in vehicle_gps_position_2 not matching the values logged using a custom message because the replay and ekf2 are both publishing to that instance. @dagar is there any way I can make ekf2 write the blended output to a specified instance (eg index 3) when doing replay? |
Prevents large transients at startup.
Previous calculation attempted to use a shortcut that created a circular data dependency.
Also fix formatting.
Rebased on master and currently fitting into px4fmu-v2. |
Let's get this in! |
If the eph or epv or evh is not change,but the lon or lat is jump,how can I do? |
The min_us in here is a bug??? |
@lingbuweibu no, that's initializes min_us to the largest uint64 value so that the comparison sets min_us to an actual gps time. |
OK |
Today, I did an experiment,the first was RTK,the fix was six,the second gps was M8N,the fix was four,the "_gps_select_index" was always = "2",when I plug the RTK gps,the data of RTK gps was not changed,the fix was always six, the "_gps_select_index" was always = "0",what can I do to solve the problem? |
Submitted in response to #9431
This PR gives ekf2 the ability to use two GPS receivers if available to create a blended solution where the blending weights and position/height offset correction adapts to the reported accuracy. If one receiver loses lock or data, it will automatically switch across to the good receiver.
Because a long term position offset is calculated separately for each receiver, switching between either GPS1, GPS2 and the blended solution can be performed without large position and altitude jumps.
This feature relies on use of the reported accuracy metrics contained in the vehicle_gps_position.eph, epv and s_variance_m_s messages. The EKF2_GPS_MASK parameter controls which accuracy metrics are used to set the blending ratios. By default it is zero which disables the blending. If different model receivers are used, they must both have the same update rate and both output the accuracy metrics selected by the EKF2_GPS_MASK parameter. An RTK and non RTK receiver can be used.
The EKF2_GPS_TAU parameter controls the time constant used to adjust the position offset that is applied to each GPS solution.
The blended GPS solution is published as a third GPS instance and can be found in the log as vehicle_gps_position_2
Bench test log here: https://logs.px4.io/plot_app?log=1bfcfab1-1652-48e3-b349-a42cd493e3cd
Method used to log blended GPS to a third instance may not be correct. I am relying on the uORB orb_publish_auto function to correctly assign a new instance.
Noise on blended lat/lon in log needs investigating. It was not present on logs generated using earlier replay data.