PX4 Firmware
PX4 Autopilot Software http://px4.io
mag_control.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2019 Estimation and Control Library (ECL). All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name ECL nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file mag_control.cpp
36  * Control functions for ekf magnetic field fusion
37  */
38 
39 #include "ekf.h"
40 #include <mathlib/mathlib.h>
41 
43 {
46 
47  // If we are on ground, store the local position and time to use as a reference
48  // Also reset the flight alignment flag so that the mag fields will be re-initialised next time we achieve flight altitude
53  }
54 
58  stopMagFusion();
59  return;
60  }
61 
62  if (canRunMagFusion()) {
67 
68  } else {
70  }
71 
72  // Determine if we should use simple magnetic heading fusion which works better when
73  // there are large external disturbances or the more accurate 3-axis fusion
74  switch (_params.mag_fusion_type) {
75  case MAG_FUSE_TYPE_AUTO:
76  selectMagAuto();
77  break;
78 
80  /* fallthrough */
83  break;
84 
85  case MAG_FUSE_TYPE_3D:
87  break;
88 
89  default:
90  selectMagAuto();
91  break;
92  }
93 
96 
98  }
99 }
100 
102 {
103  if (_mag_data_ready) {
105  }
106 }
107 
109 {
110  // check for new magnetometer data that has fallen behind the fusion time horizon
111  // If we are using external vision data or GPS-heading for heading then no magnetometer fusion is used
113 }
114 
116 {
117  // We need to reset the yaw angle after climbing away from the ground to enable
118  // recovery from ground level magnetic interference.
120  // Check if height has increased sufficiently to be away from ground magnetic anomalies
121  // and request a yaw reset if not already requested.
122  static constexpr float mag_anomalies_max_hagl = 1.5f;
123  const bool above_mag_anomalies = (getTerrainVPos() - _state.pos(2)) > mag_anomalies_max_hagl;
124  _mag_yaw_reset_req = _mag_yaw_reset_req || above_mag_anomalies;
125  }
126 }
127 
128 float Ekf::getTerrainVPos() const
129 {
131 }
132 
134 {
136  const bool has_realigned_yaw = canResetMagHeading()
138  : false;
139 
140  _mag_yaw_reset_req = !has_realigned_yaw;
141  }
142 }
143 
145 {
146  return !_mag_use_inhibit;
147 }
148 
150 {
151  return !isStrongMagneticDisturbance();
152 }
153 
155 {
157  bool has_realigned_yaw = false;
158 
159  if (canRealignYawUsingGps()) { has_realigned_yaw = realignYawGPS(); }
160  else if (canResetMagHeading()) { has_realigned_yaw = resetMagHeading(_mag_lpf.getState()); }
161 
162  _mag_yaw_reset_req = !has_realigned_yaw;
163  _control_status.flags.mag_aligned_in_flight = has_realigned_yaw;
164  }
165 }
166 
168 {
170 }
171 
173 {
174  if (_velpos_reset_request) {
175  resetVelocity();
176  resetPosition();
177  _velpos_reset_request = false;
178  }
179 }
180 
182 {
185 }
186 
188 {
191 
194  }
195 }
196 
198 {
199  // Check if there has been enough change in horizontal velocity to make yaw observable
200  // Apply hysteresis to check to avoid rapid toggling
203  : _accel_lpf_NE.norm() > 2.0f * _params.mag_acc_gate;
204 
206  && (_control_status.flags.gps || _control_status.flags.ev_pos); // Do we have to add ev_vel here?
207 }
208 
210 {
211  // check if there is enough yaw rotation to make the mag bias states observable
213  // initial yaw motion is detected
214  _mag_bias_observable = true;
215 
216  } else if (_mag_bias_observable) {
217  // require sustained yaw motion of 50% the initial yaw rate threshold
218  const float yaw_dt = 1e-6f * (float)(_imu_sample_delayed.time_us - _time_yaw_started);
219  const float min_yaw_change_req = 0.5f * _params.mag_yaw_rate_gate * yaw_dt;
220  _mag_bias_observable = fabsf(_yaw_delta_ef) > min_yaw_change_req;
221  }
222 
223  _yaw_delta_ef = 0.0f;
225 }
226 
228 {
229  return _yaw_angle_observable;
230 }
231 
233 {
234  return _mag_bias_observable;
235 }
236 
238 {
239  // Use of 3D fusion requires an in-air heading alignment but it should not
240  // be used when the heading and mag biases are not observable for more than 2 seconds
243 }
244 
246 {
247  // if we are using 3-axis magnetometer fusion, but without external NE aiding,
248  // then the declination must be fused as an observation to prevent long term heading drift
249  // fusing declination when gps aiding is available is optional, but recommended to prevent
250  // problem if the vehicle is static for extended periods of time
251  const bool user_selected = (_params.mag_declination_source & MASK_FUSE_DECL);
252  const bool not_using_ne_aiding = !_control_status.flags.gps;
253  _control_status.flags.mag_dec = (_control_status.flags.mag_3D && (not_using_ne_aiding || user_selected));
254 }
255 
257 {
259  if (!_mag_use_inhibit) {
261  }
262 
263  // If magnetometer use has been inhibited continuously then a yaw reset is required for a valid heading
264  if (uint32_t(_imu_sample_delayed.time_us - _mag_use_not_inhibit_us) > (uint32_t)5e6) {
266  }
267 }
268 
270 {
271  // If the user has selected auto protection against indoor magnetic field errors, only use the magnetometer
272  // if a yaw angle relative to true North is required for navigation. If no GPS or other earth frame aiding
273  // is available, assume that we are operating indoors and the magnetometer should not be used.
274  // Also inhibit mag fusion when a strong magnetic field interference is detected
275  const bool user_selected = (_params.mag_fusion_type == MAG_FUSE_TYPE_INDOOR);
276 
277  const bool heading_not_required_for_navigation = !_control_status.flags.gps
280 
281  return (user_selected && heading_not_required_for_navigation)
283 }
284 
286 {
291 
292  } else {
294  }
295 }
296 
298 {
300 }
301 
303 {
304  constexpr float wmm_gate_size = 0.2f; // +/- Gauss
305  return isMeasuredMatchingExpected(_mag_sample_delayed.mag.length(), _mag_strength_gps, wmm_gate_size);
306 }
307 
309 {
310  constexpr float average_earth_mag_field_strength = 0.45f; // Gauss
311  constexpr float average_earth_mag_gate_size = 0.40f; // +/- Gauss
313  average_earth_mag_field_strength,
314  average_earth_mag_gate_size);
315 }
316 
317 bool Ekf::isMeasuredMatchingExpected(const float measured, const float expected, const float gate)
318 {
319  return (measured >= expected - gate)
320  && (measured <= expected + gate);
321 }
322 
324 {
327 
328  } else if (_control_status.flags.mag_hdg) {
329  fuseHeading();
330  }
331 }
332 
334 {
335  if (!_mag_decl_cov_reset) {
336  // After any magnetic field covariance reset event the earth field state
337  // covariances need to be corrected to incorporate knowledge of the declination
338  // before fusing magnetomer data to prevent rapid rotation of the earth field
339  // states for the first few observations.
340  fuseDeclination(0.02f);
341  _mag_decl_cov_reset = true;
342  fuseMag();
343 
344  } else {
345  // The normal sequence is to fuse the magnetometer data first before fusing
346  // declination angle at a higher uncertainty to allow some learning of
347  // declination angle over time.
348  fuseMag();
350  fuseDeclination(0.5f);
351  }
352  }
353 }
354 
bool canUse3DMagFusion() const
void fuseDeclination(float decl_sigma)
Definition: mag_fusion.cpp:815
struct estimator::filter_control_status_u::@60 flags
uint32_t gps_yaw
22 - true when yaw (not ground course) data from a GPS receiver is being fused
Definition: common.h:463
stateSample _state
state struct of the ekf running at the delayed time horizon
Definition: ekf.h:288
uint64_t _time_last_mov_3d_mag_suitable
last system time that sufficient movement to use 3-axis magnetometer fusion was detected (uSec) ...
Definition: ekf.h:442
uint32_t yaw_align
1 - true if the filter yaw alignment is complete
Definition: common.h:442
float _yaw_rate_lpf_ef
Filtered angular rate about earth frame D axis (rad/sec)
Definition: ekf.h:353
bool resetMagHeading(const Vector3f &mag_init, bool increase_yaw_var=true, bool update_buffer=true)
Definition: ekf_helper.cpp:554
void checkMagBiasObservability()
#define MAG_FUSE_TYPE_3D
Magnetometer 3-axis fusion will always be used. This is more accurate, but more affected by localised...
Definition: common.h:202
void runOnGroundYawReset()
bool shouldInhibitMag() const
bool _mag_yaw_reset_req
true when a reset of the yaw using the magnetometer data has been requested
Definition: ekf.h:363
float _yaw_delta_ef
Recent change in yaw angle measured about the earth frame D axis (rad)
Definition: ekf.h:352
uint32_t fixed_wing
17 - true when the vehicle is operating as a fixed wing vehicle
Definition: common.h:458
bool resetVelocity()
Definition: ekf_helper.cpp:50
uint32_t mag_hdg
4 - true if a simple magnetic yaw heading is being fused
Definition: common.h:445
void selectMagAuto()
void runVelPosReset()
Vector2f _accel_lpf_NE
Low pass filtered horizontal earth frame acceleration (m/sec**2)
Definition: ekf.h:351
bool canRunMagFusion() const
uint32_t mag_fault
18 - true when the magnetometer has been declared faulty and is no longer being used ...
Definition: common.h:459
#define MAG_FUSE_TYPE_HEADING
Simple yaw angle fusion will always be used. This is less accurate, but less affected by earth field ...
Definition: common.h:201
uint32_t mag_field_disturbed
16 - true when the mag field does not match the expected strength
Definition: common.h:457
bool _mag_decl_cov_reset
true after the fuseDeclination() function has been used to modify the earth field covariances after a...
Definition: ekf.h:364
#define MAG_FUSE_TYPE_INDOOR
The same as option 0, but magnetometer or yaw fusion will not be used unless earth frame external aid...
Definition: common.h:204
static bool isMeasuredMatchingExpected(float measured, float expected, float gate)
bool _mag_inhibit_yaw_reset_req
true when magnetometer inhibit has been active for long enough to require a yaw reset when conditions...
Definition: ekf.h:361
const T & getState() const
Definition: AlphaFilter.hpp:67
void checkMagFieldStrength()
float _terrain_vpos
estimated vertical position of the terrain underneath the vehicle in local NED frame (m) ...
Definition: ekf.h:457
AlphaFilterVector3f _mag_lpf
filtered magnetometer measurement (Gauss)
Definition: ekf.h:433
bool isStrongMagneticDisturbance() const
uint32_t ev_vel
24 - true when local earth frame velocity data from external vision measurements are being fused ...
Definition: common.h:465
void runMagAndMagDeclFusions()
void updateMagFilter()
int32_t mag_declination_source
bitmask used to control the handling of declination data
Definition: common.h:274
bool realignYawGPS()
Definition: ekf_helper.cpp:405
bool isYawResetAuthorized() const
bool isMeasuredMatchingGpsMagStrength() const
bool canResetMagHeading() const
float getTerrainVPos() const
uint64_t time_us
timestamp of the measurement (uSec)
Definition: common.h:111
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
void update(const T &input, float tau, float dt)
Definition: AlphaFilter.hpp:50
filter_control_status_u _control_status
float mag_acc_gate
when in auto select mode, heading fusion will be used when manoeuvre accel is lower than this (m/sec*...
Definition: common.h:276
void checkMagInhibition()
int32_t mag_fusion_type
integer used to specify the type of magnetometer fusion used
Definition: common.h:275
void controlMagFusion()
Definition: mag_control.cpp:42
uint32_t ev_yaw
13 - true when yaw data from external vision measurements is being fused
Definition: common.h:454
bool _velpos_reset_request
true when a large yaw error has been fixed and a velocity and position state reset is required ...
Definition: ekf.h:445
bool canRealignYawUsingGps() const
uint32_t mag_aligned_in_flight
23 - true when the in-flight mag field alignment has been completed
Definition: common.h:464
void checkMagDeclRequired()
void fuseMag()
Definition: mag_fusion.cpp:47
Vector3f pos
NED position in earth frame in m.
Definition: common.h:370
int32_t check_mag_strength
Definition: common.h:364
bool _mag_use_inhibit
true when magnetometer use is being inhibited
Definition: ekf.h:359
uint32_t mag_dec
6 - true if synthetic magnetic declination measurements are being fused
Definition: common.h:447
#define MAG_FUSE_TYPE_NONE
Do not use magnetometer under any circumstance. Other sources of yaw may be used if selected via the ...
Definition: common.h:205
uint64_t _time_yaw_started
last system time in usec that a yaw rotation manoeuvre was detected
Definition: ekf.h:356
uint32_t mag_3D
5 - true if 3-axis magnetometer measurement are being fused
Definition: common.h:446
uint32_t in_air
7 - true when the vehicle is airborne
Definition: common.h:448
bool isYawAngleObservable() const
bool _yaw_angle_observable
true when there is enough horizontal acceleration to make yaw observable
Definition: ekf.h:355
void startMag3DFusion()
void run3DMagAndDeclFusions()
float mag_yaw_rate_gate
yaw rate threshold used by mode select logic (rad/sec)
Definition: common.h:277
uint32_t ev_pos
12 - true when local position data from external vision is being fused
Definition: common.h:453
float _last_on_ground_posD
last vertical position when the in_air status was false (m)
Definition: ekf.h:439
void checkHaglYawResetReq()
uint8_t _num_bad_flight_yaw_events
number of times a bad heading has been detected in flight and required a yaw reset ...
Definition: ekf.h:357
void runInAirYawReset()
void fuseHeading()
Definition: mag_fusion.cpp:438
uint32_t gps
2 - true if GPS measurements are being fused
Definition: common.h:443
void check3DMagFusionSuitability()
void stopMagFusion()
Vector3f mag
NED magnetometer body frame measurements (Gauss)
Definition: common.h:126
bool resetPosition()
Definition: ekf_helper.cpp:139
#define MAG_FUSE_TYPE_AUTO
The selection of either heading or 3D magnetometer fusion will be automatic.
Definition: common.h:200
bool isMagBiasObservable() const
Class for core functions for ekf attitude and position estimator.
bool isTerrainEstimateValid() const override
#define MASK_FUSE_DECL
set to true if the declination is always fused as an observation to constrain drift when 3-axis fusio...
Definition: common.h:186
void startMagHdgFusion()
uint64_t _mag_use_not_inhibit_us
last system time in usec before magnetometer use was inhibited
Definition: ekf.h:358
bool _mag_data_ready
true when new magnetometer data has fallen behind the fusion time horizon and is available to be fuse...
Definition: ekf.h:318
bool isMeasuredMatchingAverageMagStrength() const
bool _mag_bias_observable
true when there is enough rotation to make magnetometer bias errors observable
Definition: ekf.h:354
void checkYawAngleObservability()