-
Notifications
You must be signed in to change notification settings - Fork 510
[ekf] controlMagFusion refactor and mag field strength check #662
Conversation
Revolutionary! |
f1d7748
to
c8fd26a
Compare
Test of the EKF2_MAG_TYPE parameter:
Log: https://logs.px4.io/plot_app?log=e08a96ab-c5bd-4b4a-86c0-d8a73a14009b |
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 haven't gone through the entire logic in detail, I'll try to do that tomorrow. Some small things i noticed:
EKF/mag_control.cpp
Outdated
|
||
bool Ekf::isStrongMagneticDisturbance() const | ||
{ | ||
return _mag_sample_delayed.mag.length() > 2.f * _mag_strength_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.
Do we want to check for the length being too small as well? Or does that never really happen?
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.
That's a good question. I think it's really rare but it might be worth adding for completeness. I'll add a check for _mag_sample_delayed.mag.length() < .5f * _mag_strength_gps
bool Ekf::isStrongMagneticDisturbance() const | ||
{ | ||
return _mag_sample_delayed.mag.length() > 2.f * _mag_strength_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.
I guess we need to check that _mag_strength_gps is valid? I assume we drop back to 2D fusion if we don't have GPS... but what happens to this check?
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.
Right _mag_strength_gps = 0.f
when there is no GPS fix, I can protect it with _NED_origin_initialised
and compare with an average value and increased margins if false
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.
This is a very nice refactor. Great Work!
EKF/mag_control.cpp
Outdated
|
||
if ((_params.mag_fusion_type >= MAG_FUSE_TYPE_NONE) | ||
|| _control_status.flags.mag_fault | ||
|| !_control_status.flags.tilt_align) { |
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.
So we do not want to fuse mag before we are tilt aligned? Was this also like this before?
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 already denied 3D fusion before tilt alignment: https://github.com/PX4/ecl/blob/7c1e38d3adc79db7586f86dba64f7b15635792b2/EKF/control.cpp#L1412
And it doesn't make sense to me to fuse magnetic heading if the tilt isn't aligned. Does that help to align tilt? I don't think so.
What do you think @priseborough ?
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 shouldn't be using mag data before tilt is know because regardless of the method used, it requires comparison with earth frame field data.
The _control_status.flags.yaw_align flag should be true before fusing magnetic data and the _control_status.flags.tilt_align flag should be true before aligning the yaw using magnetic data.
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.
The tilt_align flag is set true on initial filter alignment and ever goes false unless the EKF is restarted. It would make more sense to use _control_status.flags.yaw_align as a requirement for fusing mag data.
if (_control_status.flags.in_air) { | ||
checkHaglYawResetReq(); | ||
runInAirYawReset(); | ||
runVelPosReset(); |
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.
Previously a velocity or position reset was only called in the fixed wing loop. Is this functional change intended?
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.
The _velpos_reset_request
flag is currently only set in realignYawGPS
which is only called -as you said- for fixed-wings. However, I don't see why this flag should be reserved for fixed-wings; the logic isn't there yet, but it would make sense to me that a large heading realignment on a multicopter triggers a vel/pos reset. Don't you think so?
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.
Independent of platform type it could be conditional on the level of velocity and position innovation test ratios. If the vehicle is hovering or on ground and has small velocities, then a velocity and position reset is probably unnecessary. When doing a reset to GPS there is always a possibility of resetting to a noise spike.
EKF/mag_control.cpp
Outdated
controlMagStateOnlyFusion(); | ||
break; | ||
|
||
case MAG_FUSE_TYPE_HEADING: |
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.
Not sure if it would be better to add here the repeated function call and a break, such that it is harder to make an unintentional change to the MAG_FUSE_TYPE_HEADING.
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'll swap them and add a fall-through
comment
{ | ||
// Use of 3D fusion requires an in-air heading alignment but it should not | ||
// be used when the heading and mag biases are not observable for more than 2 seconds | ||
return _control_status.flags.mag_aligned_in_flight |
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.
Previously we checked also for in_air and tilt align. Is this not necessary?
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.
mag_aligned_in_flight
cannot be true if not in_air
and we don't run mag fusion at all if the tilt isn't aligned
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.
Agree
EKF/mag_control.cpp
Outdated
const bool not_using_gps = !(_params.fusion_mode & MASK_USE_GPS) || !_control_status.flags.gps; | ||
const bool not_using_evpos = !(_params.fusion_mode & MASK_USE_EVPOS) || !_control_status.flags.ev_pos; | ||
const bool not_using_evvel = !(_params.fusion_mode & MASK_USE_EVVEL) || !_control_status.flags.ev_vel; | ||
const bool not_selected_evyaw = !(_params.fusion_mode & MASK_USE_EVYAW); |
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 think that the EVYAW should not belong here, since when we fuse EVYAW we do not use MAG anyway.
EKF/control.cpp
Outdated
// Check if height has increased sufficiently to be away from ground magnetic anomalies | ||
// and request a yaw reset if not already requested. | ||
float terrain_vpos_estimate = isTerrainEstimateValid() ? _terrain_vpos : _last_on_ground_posD; | ||
_mag_yaw_reset_req |= (terrain_vpos_estimate - _state.pos(2)) > 1.5f; | ||
static constexpr float mag_reset_altitude = 1.5f; |
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.
maybe put this at the top of the file to have an overview of hardcoded values?
EKF/control.cpp
Outdated
if (_mag_yaw_reset_req && !_mag_use_inhibit | ||
&& _control_status.flags.tilt_align | ||
&& _control_status.flags.in_air) { | ||
const bool has_realigned_yaw = canRealignYawUsingGps() |
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 would avoid complicated ternary conditionals and can not accept nested ones.
EKF/control.cpp
Outdated
bool Ekf::isStrongMagneticDisturbance() const | ||
{ | ||
static constexpr float average_earth_mag_field_strength = 0.45f; // Gauss | ||
return _mag_sample_delayed.mag.length() > 3.f * average_earth_mag_field_strength; |
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.
It would probably also be reasonable to check for not enough field strength because the direction does not make sense anymore in that case.
eb8f7bc
to
0890824
Compare
@jkflying @kamilritz @MaEtUgR Thanks for the review guys, I made the modifications. I also made a mission comparison before and after to show that the mag switch is correct. After: You can see in the second log that there is one additional switch to heading fusion at 65s. This makes sense to me as it is during a straight line; I don't understand why it was not switching before. |
0890824
to
2a52d12
Compare
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.
Nice refactoring!!
I added some comments. This is the largest change in a long time to the EKF so please be very careful before merging this
EKF/mag_control.cpp
Outdated
bool Ekf::isMeasuredMatchingAverageMagStrength() const | ||
{ | ||
constexpr float average_earth_mag_field_strength = 0.45f; // Gauss | ||
constexpr float average_earth_mag_gate_size = 2.5f; | ||
return isMeasuredMatchingExpected(_mag_sample_delayed.mag.length(), | ||
average_earth_mag_field_strength, | ||
average_earth_mag_gate_size); | ||
} |
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.
This check is too tight for places with a low magnetic field strength.
E.g. in south america there are places with a mag strength of 0.18 Gauss ->
0.18 * 2.5 = 0.45, and this is with a perfect magnetometer calibration.
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.
@CarlOlsson Good catch. which gate would you suggest?
0.18 * 3.0 = 0.54
0.18 * 3.5 = 0.63
0.18 * 4.0 = 0.72
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'm changing to a expected +/- gate check
EKF/mag_control.cpp
Outdated
bool has_realigned_yaw = false; | ||
|
||
if (canRealignYawUsingGps()) { has_realigned_yaw = realignYawGPS(); } | ||
else if (canResetMagHeading()) { has_realigned_yaw = resetMagHeading(_mag_sample_delayed.mag); } |
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.
This issue was there before but since you anyway change a lot of things regarding mag fusion you could consider low pass filtering the mag sample going into resetMagHeading(). As noted in the linked issue, there is no consistency check or outlier detection at all on this sample, so if this one sample is bad it have critical consequences.
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 in 9937d67
EKF/mag_control.cpp
Outdated
selectMagAuto(); | ||
break; | ||
|
||
case MAG_FUSE_TYPE_AUTOFW: |
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.
This mode was developed by us to prevent a badly aligned magnetometer to deteriorate the accelerometer biases leading to bad velocity tracking after a large attitude change (transition to hover mode).
However, many flights have shown that it is unsafe for a fixed wing to fly without some sort of sensor fusion (mag or synthetic etc) stabilizing the heading estimate. We hence stopped using this mode more than a year ago and I would strongly suggest no one to use it. I.e. I would vote for removing it
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.
Perfect, less code is better :) I'll remove it.
EKF/mag_control.cpp
Outdated
{ | ||
if (!_mag_decl_cov_reset) { | ||
// After any magnetic field covariance reset event the earth field state | ||
// covariances need to be corrected to incorporate knowedge of the declination |
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.
// covariances need to be corrected to incorporate knowedge of the declination | |
// covariances need to be corrected to incorporate knowledge of the declination |
3907dd6
to
9937d67
Compare
Okay, I think I'm done here. The mag field strength check is disabled by default and will be enabled later from the firmware side via a parameter. |
I made a mistake on the unit test, will fix that |
Not a part of this PR directly, but what do people think of moving every sensor into its own source file? e.g Everything related to magnetometer fusion and outlier checking lives it in it's own file. |
790fa31
to
4797df7
Compare
Move mag inhibition check in separate function
- yaw abd mag bias observability checks - mag 3D conditions - load mag covariances - set and clear mag control modes
…tom) Also split inAirYawReset from onGroundYawReset
- transform if-else into switch-case for parameter fusion type selection
flag naming in Test script
Reset some variables on ground even if mag fusion is not running yet. It could be that it runs later so we need to make sure that those variables are properly set.
to average earth mag field with larger gate if not valid
0.1/0.9 type low-pass filters are commonly used to smooth data, this class is meant to abstract the computation of this filter
…n outlier fixes ecl issue #525
add parameter to enable or disable mag field strength check
…lemented for VTOL but did not solve the problem and should not be used anymore
4797df7
to
b806b8b
Compare
@mhkabir I think this is necessary, yes. At least own file (as we refactor each component) and maybe even its own class to avoid having a massive class with 1 trillion global variables. |
Yes, exactly 😆 |
as there is no reason to fuse mag when the ekf isn't aligned
69a1c6c
to
d349676
Compare
This PR has been tested by the Dronecode test team: PX4/PX4-Autopilot#13380
I'm quite confident that this PR is now ready to be merged. I don't think that waiting more will help. |
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.
Nice cleanup!
Youhou, thanks for your reviews guys. |
dropped during the mag fusion refactor PR #662
dropped during the mag fusion refactor PR #662
I basically tried to abstract the overall mag fusion type switching from the implementation details and get rid of the multiple checks for the same flags (e.g.:
in_air
).I still need to test a lot and maybe I'll move the whole controlMagFusion and sub-functions to a new file.
Feature added: the mag fusion is inhibited when the norm of the magnetic field exceeds X times the expected strength (from WMM table).
fixes #525