mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-05-02 05:04:08 +08:00
182 lines
5.6 KiB
C++
182 lines
5.6 KiB
C++
/****************************************************************************
|
|
*
|
|
* Copyright (c) 2015 Estimation and Control Library (ECL). All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* 3. Neither the name ECL nor the names of its contributors may be
|
|
* used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/**
|
|
* @file heading_fusion.cpp
|
|
* Magnetometer heading fusion.
|
|
*
|
|
* @author Roman Bast <bapstroman@gmail.com>
|
|
*
|
|
*/
|
|
#include "ekf.h"
|
|
#include <mathlib/mathlib.h>
|
|
|
|
void Ekf::fuseHeading()
|
|
{
|
|
// assign intermediate state variables
|
|
float q0 = _state.quat_nominal(0);
|
|
float q1 = _state.quat_nominal(1);
|
|
float q2 = _state.quat_nominal(2);
|
|
float q3 = _state.quat_nominal(3);
|
|
|
|
float magX = _mag_sample_delayed.mag(0);
|
|
float magY = _mag_sample_delayed.mag(1);
|
|
float magZ = _mag_sample_delayed.mag(2);
|
|
|
|
float R_mag = _params.mag_heading_noise;
|
|
|
|
float t2 = q0*q0;
|
|
float t3 = q1*q1;
|
|
float t4 = q2*q2;
|
|
float t5 = q3*q3;
|
|
float t6 = q0*q2*2.0f;
|
|
float t7 = q1*q3*2.0f;
|
|
float t8 = t6+t7;
|
|
float t9 = q0*q3*2.0f;
|
|
float t13 = q1*q2*2.0f;
|
|
float t10 = t9-t13;
|
|
float t11 = t2+t3-t4-t5;
|
|
float t12 = magX*t11;
|
|
float t14 = magZ*t8;
|
|
float t19 = magY*t10;
|
|
float t15 = t12+t14-t19;
|
|
float t16 = t2-t3+t4-t5;
|
|
float t17 = q0*q1*2.0f;
|
|
float t24 = q2*q3*2.0f;
|
|
float t18 = t17-t24;
|
|
float t20 = 1.0f/t15;
|
|
float t21 = magY*t16;
|
|
float t22 = t9+t13;
|
|
float t23 = magX*t22;
|
|
float t28 = magZ*t18;
|
|
float t25 = t21+t23-t28;
|
|
float t29 = t20*t25;
|
|
float t26 = tan(t29);
|
|
float t27 = 1.0f/(t15*t15);
|
|
float t30 = t26*t26;
|
|
float t31 = t30+1.0f;
|
|
|
|
float H_MAG[3] = {};
|
|
H_MAG[0] = -t31*(t20*(magZ*t16+magY*t18)+t25*t27*(magY*t8+magZ*t10));
|
|
H_MAG[1] = t31*(t20*(magX*t18+magZ*t22)+t25*t27*(magX*t8-magZ*t11));
|
|
H_MAG[2] = t31*(t20*(magX*t16-magY*t22)+t25*t27*(magX*t10+magY*t11));
|
|
|
|
// calculate innovation
|
|
matrix::Dcm<float> R_to_earth(_state.quat_nominal);
|
|
matrix::Vector3f mag_earth_pred = R_to_earth * _mag_sample_delayed.mag;
|
|
|
|
float innovation = atan2f(mag_earth_pred(1), mag_earth_pred(0)) - math::radians(_params.mag_declination_deg);
|
|
|
|
innovation = math::constrain(innovation, -0.5f, 0.5f);
|
|
|
|
float innovation_var = R_mag;
|
|
|
|
// calculate innovation variance
|
|
float PH[3] = {};
|
|
for (unsigned row = 0; row < 3; row++) {
|
|
for (unsigned column = 0; column < 3; column++) {
|
|
PH[row] += P[row][column]*H_MAG[column];
|
|
}
|
|
innovation_var += H_MAG[row] * PH[row];
|
|
}
|
|
|
|
if (innovation_var >= R_mag) {
|
|
// variance has increased, no failure
|
|
_fault_status.bad_mag_x = false;
|
|
_fault_status.bad_mag_y = false;
|
|
_fault_status.bad_mag_z = false;
|
|
} else {
|
|
// our innovation variance has decreased, our calculation is thus badly conditioned
|
|
_fault_status.bad_mag_x = true;
|
|
_fault_status.bad_mag_y = true;
|
|
_fault_status.bad_mag_z = true;
|
|
|
|
// we reinitialise the covariance matrix and abort this fusion step
|
|
initialiseCovariance();
|
|
return;
|
|
}
|
|
|
|
float innovation_var_inv = 1 / innovation_var;
|
|
|
|
// calculate kalman gain
|
|
float Kfusion[_k_num_states] = {};
|
|
for (unsigned row = 0; row < _k_num_states; row++) {
|
|
for (unsigned column = 0; column < 3; column++) {
|
|
Kfusion[row] += P[row][column] * H_MAG[column];
|
|
}
|
|
Kfusion[row] *= innovation_var_inv;
|
|
}
|
|
|
|
// innovation test ratio
|
|
float yaw_test_ratio = sq(innovation) / (sq(math::max(0.01f * (float)_params.heading_innov_gate, 1.0f)) * innovation_var);
|
|
|
|
// set the magnetometer unhealthy if the test fails
|
|
if (yaw_test_ratio > 1.0f) {
|
|
_mag_healthy = false;
|
|
|
|
// if we are in air we don't want to fuse the measurement
|
|
// we allow to use it when on the ground because the large innovation could be caused
|
|
// by interference or a large initial gyro bias
|
|
if (_in_air) {
|
|
return;
|
|
}
|
|
} else {
|
|
_mag_healthy = true;
|
|
}
|
|
|
|
_state.ang_error.setZero();
|
|
fuse(Kfusion, innovation);
|
|
|
|
// correct the nominal quaternion
|
|
Quaternion dq;
|
|
dq.from_axis_angle(_state.ang_error);
|
|
_state.quat_nominal = dq * _state.quat_nominal;
|
|
_state.quat_nominal.normalize();
|
|
|
|
float HP[_k_num_states] = {};
|
|
|
|
for (unsigned column = 0; column < _k_num_states; column++) {
|
|
for (unsigned row = 0; row < 3; row++) {
|
|
HP[column] += H_MAG[row] * P[row][column];
|
|
}
|
|
}
|
|
|
|
for (unsigned row = 0; row < _k_num_states; row++) {
|
|
for (unsigned column = 0; column < _k_num_states; column++) {
|
|
P[row][column] -= Kfusion[row] * HP[column];
|
|
}
|
|
}
|
|
|
|
makeSymmetrical();
|
|
limitCov();
|
|
} |