Skip to content

Commit 0adb2e8

Browse files
committed
First pass at adding the magnetometer to the comp filt.
1 parent 59640d6 commit 0adb2e8

File tree

4 files changed

+160
-17
lines changed

4 files changed

+160
-17
lines changed

include/estimator.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class Estimator : public ParamListenerInterface
8282

8383
bool is_initialized_ = false;
8484
uint64_t last_acc_update_us_;
85+
uint64_t last_mag_update_us_;
8586
uint64_t last_extatt_update_us_;
8687

8788
turbomath::Vector w1_;
@@ -91,18 +92,29 @@ class Estimator : public ParamListenerInterface
9192

9293
turbomath::Vector accel_LPF_;
9394
turbomath::Vector gyro_LPF_;
95+
turbomath::Vector mag_LPF_;
96+
turbomath::Vector mag_dir_;
97+
98+
bool mag_direction_found_ = false; // TODO: Init correctly
99+
100+
float mag_[3];
94101

95102
turbomath::Vector w_acc_;
96103

97104
bool extatt_update_next_run_;
98105
turbomath::Quaternion q_extatt_;
99106

100-
void run_LPF();
107+
void run_accel_LPF();
108+
void run_mag_LPF();
101109

102110
bool can_use_accel() const;
103111
bool can_use_extatt() const;
112+
bool can_use_mag();
113+
bool mag_disturbance_is_large();
104114
turbomath::Vector accel_correction() const;
105115
turbomath::Vector extatt_correction() const;
116+
turbomath::Vector mag_correction() const;
117+
turbomath::Vector update_correction(turbomath::Vector w_err, float kp, float ki, float dt, const uint64_t now_us);
106118
turbomath::Vector smoothed_gyro_measurement();
107119
void integrate_angular_rate(turbomath::Quaternion & quat, const turbomath::Vector & omega,
108120
const float dt) const;

include/param.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,20 +280,23 @@ enum : uint16_t
280280
/*** ESTIMATOR CONFIGURATION ***/
281281
/*******************************/
282282
PARAM_INIT_TIME,
283-
PARAM_FILTER_KP_ACC,
284283
PARAM_FILTER_KI,
284+
PARAM_FILTER_KP_ACC,
285285
PARAM_FILTER_KP_EXT,
286+
PARAM_FILTER_KP_MAG,
286287
PARAM_FILTER_ACCEL_MARGIN,
287288

288289
PARAM_FILTER_USE_QUAD_INT,
289290
PARAM_FILTER_USE_MAT_EXP,
290291
PARAM_FILTER_USE_ACC,
292+
PARAM_FILTER_USE_MAG,
291293

292294
PARAM_CALIBRATE_GYRO_ON_ARM,
293295

294296
PARAM_GYRO_XY_ALPHA,
295297
PARAM_GYRO_Z_ALPHA,
296298
PARAM_ACC_ALPHA,
299+
PARAM_MAG_ALPHA,
297300

298301
PARAM_GYRO_X_BIAS,
299302
PARAM_GYRO_Y_BIAS,
@@ -317,6 +320,8 @@ enum : uint16_t
317320
PARAM_MAG_X_BIAS,
318321
PARAM_MAG_Y_BIAS,
319322
PARAM_MAG_Z_BIAS,
323+
PARAM_MAG_INCLINATION,
324+
PARAM_MAG_DECLINATION,
320325

321326
PARAM_BARO_BIAS,
322327
PARAM_GROUND_LEVEL,

src/estimator.cpp

Lines changed: 136 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ void Estimator::init()
9999

100100
void Estimator::param_change_callback(uint16_t param_id) { (void) param_id; }
101101

102-
void Estimator::run_LPF()
102+
void Estimator::run_accel_LPF()
103103
{
104104
float alpha_acc = RF_.params_.get_param_float(PARAM_ACC_ALPHA);
105105
accel_LPF_.x = (1.0f - alpha_acc) * RF_.sensors_.get_imu()->accel[0] + alpha_acc * accel_LPF_.x;
@@ -113,6 +113,14 @@ void Estimator::run_LPF()
113113
gyro_LPF_.z = (1.0f - alpha_gyro_z) * RF_.sensors_.get_imu()->gyro[2] + alpha_gyro_z * gyro_LPF_.z;
114114
}
115115

116+
void Estimator::run_mag_LPF()
117+
{
118+
float alpha_mag = RF_.params_.get_param_float(PARAM_MAG_ALPHA);
119+
mag_LPF_.x = (1.0f - alpha_mag) * RF_.sensors_.get_mag()->flux[0] + alpha_mag * mag_LPF_.x;
120+
mag_LPF_.y = (1.0f - alpha_mag) * RF_.sensors_.get_mag()->flux[1] + alpha_mag * mag_LPF_.y;
121+
mag_LPF_.z = (1.0f - alpha_mag) * RF_.sensors_.get_mag()->flux[2] + alpha_mag * mag_LPF_.z;
122+
}
123+
116124
void Estimator::set_external_attitude_update(const turbomath::Quaternion & q)
117125
{
118126
extatt_update_next_run_ = true;
@@ -136,23 +144,80 @@ void Estimator::run(const float dt)
136144
}
137145

138146
// Low-pass filter accel and gyro measurements
139-
run_LPF();
147+
run_accel_LPF();
140148

141149
//
142150
// Gyro Correction Term (werr)
143151
//
144152

145153
float kp = 0.0f;
146-
float ki = RF_.params_.get_param_float(PARAM_FILTER_KI);
154+
float ki = RF_.params_.get_param_float(PARAM_FILTER_KI); // TODO: Should this be specific to the sensor?
147155

148156
turbomath::Vector w_err;
157+
turbomath::Vector correction_term;
149158

150159
if (can_use_accel()) {
151160
// Get error estimated by accelerometer measurement
152161
w_err = accel_correction();
153162
kp = RF_.params_.get_param_float(PARAM_FILTER_KP_ACC);
154163

155164
last_acc_update_us_ = now_us;
165+
166+
correction_term += update_correction(w_err, kp, ki, dt, now_us);
167+
}
168+
169+
if (can_use_mag()) {
170+
171+
run_mag_LPF();
172+
173+
// Get error estimated by magnetometer measurement
174+
w_err = mag_correction();
175+
kp = RF_.params_.get_param_float(PARAM_FILTER_KP_MAG);
176+
177+
last_mag_update_us_ = now_us;
178+
179+
correction_term += update_correction(w_err, kp, ki, dt, now_us);
180+
181+
} else if (RF_.params_.get_param_float(PARAM_MAG_INCLINATION) > 0.0f) {
182+
183+
// If we know the inclination we can find the mag direction.
184+
185+
turbomath::Vector e_0(0.0, 0.0, 1.0);
186+
187+
float inclination = RF_.params_.get_param_float(PARAM_MAG_INCLINATION);
188+
float declination = RF_.params_.get_param_float(PARAM_MAG_DECLINATION);
189+
190+
turbomath::Quaternion mag_frame_to_inertial(0.0, inclination, declination);
191+
192+
// Use the magnetometer measurement in the inertial frame as the reference vector.
193+
mag_dir_ = mag_frame_to_inertial.rotate(e_0);
194+
195+
}
196+
else if (!mag_direction_found_ && now_us > static_cast<uint64_t>(RF_.params_.get_param_int(PARAM_INIT_TIME)) * 1000) {
197+
198+
// If the mag direction found has not been found, use the assumption that
199+
// we have the correct attitude after the init period to backout what the mag_dir is.
200+
201+
float roll = 0.0;
202+
float pitch = 0.0;
203+
float yaw = 0.0;
204+
205+
state_.attitude.get_RPY(&roll, &pitch, &yaw);
206+
207+
turbomath::Quaternion tilt_compensation(roll, pitch, 0.0);
208+
209+
turbomath::Vector tilt_adjusted_mag = tilt_compensation.rotate(mag_LPF_);
210+
211+
yaw = atan2(tilt_adjusted_mag.y, tilt_adjusted_mag.x);
212+
213+
float declination = RF_.params_.get_param_float(PARAM_MAG_DECLINATION);
214+
215+
turbomath::Quaternion body_to_inertial(roll, pitch, yaw + declination);
216+
217+
mag_dir_ = body_to_inertial.rotate(mag_LPF_);
218+
mag_dir_.normalize();
219+
220+
mag_direction_found_ = true;
156221
}
157222

158223
if (can_use_extatt()) {
@@ -171,26 +236,19 @@ void Estimator::run(const float dt)
171236

172237
last_extatt_update_us_ = now_us;
173238
extatt_update_next_run_ = false;
174-
}
175239

176-
// Crank up the gains for the first few seconds for quick convergence
177-
if (now_us < static_cast<uint64_t>(RF_.params_.get_param_int(PARAM_INIT_TIME)) * 1000) {
178-
kp = RF_.params_.get_param_float(PARAM_FILTER_KP_ACC) * 10.0f;
179-
ki = RF_.params_.get_param_float(PARAM_FILTER_KI) * 10.0f;
240+
correction_term += update_correction(w_err, kp, ki, dt, now_us);
241+
180242
}
181243

182244
//
183245
// Composite Bias-Free Angular Rate (wfinal)
184246
//
185247

186-
// Integrate biases driven by measured angular error
187-
// eq 47b Mahony Paper, using correction term w_err found above
188-
bias_ -= ki * w_err * dt;
189-
190248
// Build the composite omega vector for kinematic propagation
191249
// This the stuff inside the p function in eq. 47a - Mahony Paper
192250
turbomath::Vector wbar = smoothed_gyro_measurement();
193-
turbomath::Vector wfinal = wbar - bias_ + kp * w_err;
251+
turbomath::Vector wfinal = wbar - bias_ + correction_term;
194252

195253
//
196254
// Propagate Dynamics
@@ -218,10 +276,24 @@ void Estimator::run(const float dt)
218276
}
219277
}
220278

279+
turbomath::Vector Estimator::update_correction(turbomath::Vector w_err, float kp, float ki, float dt, const uint64_t now_us)
280+
{
281+
// Crank up the gains for the first few seconds for quick convergence
282+
if (now_us < static_cast<uint64_t>(RF_.params_.get_param_int(PARAM_INIT_TIME)) * 1000) {
283+
kp *= 10.;
284+
ki *= 10.;
285+
}
286+
287+
// Integrate biases driven by measured angular error
288+
// eq 47b Mahony Paper, using correction term w_err found above
289+
bias_ -= ki * w_err * dt;
290+
return kp * w_err;
291+
}
292+
221293
bool Estimator::can_use_accel() const
222294
{
223-
// if we are not using accel, just bail
224-
if (!RF_.params_.get_param_int(PARAM_FILTER_USE_ACC)) { return false; }
295+
// if we are not using accel or we are using extatt, just bail
296+
if (!RF_.params_.get_param_int(PARAM_FILTER_USE_ACC) || can_use_extatt()) { return false; }
225297

226298
// current magnitude of LPF'd accelerometer
227299
const float a_sqrd_norm = accel_LPF_.sqrd_norm();
@@ -240,6 +312,32 @@ bool Estimator::can_use_accel() const
240312
return (lowerbound < a_sqrd_norm && a_sqrd_norm < upperbound);
241313
}
242314

315+
bool Estimator::can_use_mag() // TODO: make const
316+
{
317+
// if we are not using mag, just bail
318+
if (!RF_.params_.get_param_int(PARAM_FILTER_USE_MAG)
319+
|| mag_disturbance_is_large()
320+
|| !mag_direction_found_
321+
|| can_use_extatt())
322+
{ return false; }
323+
324+
if (mag_ == RF_.sensors_.get_mag()->flux) {
325+
return false;
326+
}
327+
else {
328+
for (int i = 0; i < 3; i++) {
329+
mag_[i] = RF_.sensors_.get_mag()->flux[i];
330+
}
331+
}
332+
333+
return true;
334+
}
335+
336+
bool Estimator::mag_disturbance_is_large() {
337+
// TODO: Somehow check if the disturbance is large.
338+
return false;
339+
}
340+
243341
bool Estimator::can_use_extatt() const { return extatt_update_next_run_; }
244342

245343
turbomath::Vector Estimator::accel_correction() const
@@ -265,6 +363,29 @@ turbomath::Vector Estimator::accel_correction() const
265363
return w_acc;
266364
}
267365

366+
turbomath::Vector Estimator::mag_correction() const
367+
{
368+
// turn measurement into a unit vector
369+
turbomath::Vector m = mag_LPF_.normalized();
370+
371+
// Get the quaternion from accelerometer (low-frequency measure q)
372+
// (Not in either paper)
373+
turbomath::Quaternion q_mag_inv(mag_dir_, m);
374+
375+
// Get the error quaternion between observer and low-freq q
376+
// Below Eq. 45 Mahony Paper
377+
turbomath::Quaternion q_tilde = q_mag_inv * state_.attitude;
378+
379+
// Correction Term of Eq. 47a and 47b Mahony Paper
380+
// w_acc = 2*s_tilde*v_tilde
381+
turbomath::Vector w_mag;
382+
w_mag.x = -2.0f * q_tilde.w * q_tilde.x;
383+
w_mag.y = -2.0f * q_tilde.w * q_tilde.y;
384+
w_mag.z = -2.0f * q_tilde.w * q_tilde.z;
385+
386+
return w_mag;
387+
}
388+
268389
turbomath::Vector Estimator::extatt_correction() const
269390
{
270391
// DCM rows of attitude estimate and external measurement (world w.r.t body).

src/param.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,17 +330,20 @@ void Params::set_defaults(void)
330330
init_param_float(PARAM_FILTER_KP_ACC, "FILTER_KP_ACC", 0.5f); // estimator proportional gain on accel-based error - See estimator documentation | 0 | 10.0
331331
init_param_float(PARAM_FILTER_KI, "FILTER_KI", 0.01f); // estimator integral gain - See estimator documentation | 0 | 1.0
332332
init_param_float(PARAM_FILTER_KP_EXT, "FILTER_KP_EXT", 1.5f); // estimator proportional gain on external attitude-based error - See estimator documentation | 0 | 10.0
333+
init_param_float(PARAM_FILTER_KP_MAG, "FILTER_KP_MAG", 1.0f); // estimator proportional gain on magnetometer-based error - See estimator documentation | 0 | 10.0
333334
init_param_float(PARAM_FILTER_ACCEL_MARGIN, "FILTER_ACCMARGIN", 0.1f); // allowable accel norm margin around 1g to determine if accel is usable | 0 | 1.0
334335

335336
init_param_int(PARAM_FILTER_USE_QUAD_INT, "FILTER_QUAD_INT", 1); // Perform a quadratic averaging of LPF gyro data prior to integration (adds ~20 us to estimation loop on F1 processors) | 0 | 1
336337
init_param_int(PARAM_FILTER_USE_MAT_EXP, "FILTER_MAT_EXP", 1); // 1 - Use matrix exponential to improve gyro integration (adds ~90 us to estimation loop in F1 processors) 0 - use euler integration | 0 | 1
337338
init_param_int(PARAM_FILTER_USE_ACC, "FILTER_USE_ACC", 1); // Use accelerometer to correct gyro integration drift (adds ~70 us to estimation loop) | 0 | 1
339+
init_param_int(PARAM_FILTER_USE_MAG, "FILTER_USE_MAG", 1); // Use magnetometer to correct gyro integration drift (adds ?? us to estimation loop) | 0 | 1
338340

339341
init_param_int(PARAM_CALIBRATE_GYRO_ON_ARM, "CAL_GYRO_ARM", false); // True if desired to calibrate gyros on arm | 0 | 1
340342

341343
init_param_float(PARAM_GYRO_XY_ALPHA, "GYROXY_LPF_ALPHA", 0.3f); // Low-pass filter constant on gyro X and Y axes - See estimator documentation | 0 | 1.0
342344
init_param_float(PARAM_GYRO_Z_ALPHA, "GYROZ_LPF_ALPHA", 0.3f); // Low-pass filter constant on gyro Z axis - See estimator documentation | 0 | 1.0
343345
init_param_float(PARAM_ACC_ALPHA, "ACC_LPF_ALPHA", 0.5f); // Low-pass filter constant on all accel axes - See estimator documentation | 0 | 1.0
346+
init_param_float(PARAM_MAG_ALPHA, "MAG_LPF_ALPHA", 0.3f); // Low-pass filter constant on all mag axes - See estimator documentation | 0 | 1.0
344347

345348
init_param_float(PARAM_GYRO_X_BIAS, "GYRO_X_BIAS", 0.0f); // Constant x-bias of gyroscope readings | -1.0 | 1.0
346349
init_param_float(PARAM_GYRO_Y_BIAS, "GYRO_Y_BIAS", 0.0f); // Constant y-bias of gyroscope readings | -1.0 | 1.0
@@ -364,6 +367,8 @@ void Params::set_defaults(void)
364367
init_param_float(PARAM_MAG_X_BIAS, "MAG_X_BIAS", 0.0f); // Hard iron compensation constant | -999.0 | 999.0
365368
init_param_float(PARAM_MAG_Y_BIAS, "MAG_Y_BIAS", 0.0f); // Hard iron compensation constant | -999.0 | 999.0
366369
init_param_float(PARAM_MAG_Z_BIAS, "MAG_Z_BIAS", 0.0f); // Hard iron compensation constant | -999.0 | 999.0
370+
init_param_float(PARAM_MAG_INCLINATION, "MAG_INCLINATION", 0.0f); // Inclination of the magnetic field in radians | 0.0 | pi/2
371+
init_param_float(PARAM_MAG_DECLINATION, "MAG_DECLINATION", 0.069132f); // Declination of the magnetic field in radians | -pi | pi
367372

368373
init_param_float(PARAM_BARO_BIAS, "BARO_BIAS", 0.0f); // Barometer measurement bias (Pa) | 0 | inf
369374
init_param_float(PARAM_GROUND_LEVEL, "GROUND_LEVEL", 1387.0f); // Altitude of ground level (m) | -1000 | 10000

0 commit comments

Comments
 (0)