2016-03-07 05:12:06 -04:00
|
|
|
/****************************************************************************
|
|
|
|
*
|
|
|
|
* 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 vel_pos_fusion.cpp
|
|
|
|
* Function for fusing gps and baro measurements/
|
|
|
|
*
|
|
|
|
* @author Paul Riseborough <p_riseborough@live.com.au>
|
|
|
|
* @author Siddharth Bharat Purohit <siddharthbharatpurohit@gmail.com>
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ekf.h"
|
|
|
|
#include "mathlib.h"
|
|
|
|
|
|
|
|
void Ekf::fuseOptFlow()
|
|
|
|
{
|
|
|
|
float gndclearance = fmaxf(_params.rng_gnd_clearance, 0.1f);
|
|
|
|
float optflow_test_ratio[2] = {0};
|
|
|
|
|
|
|
|
// get latest estimated orientation
|
|
|
|
float q0 = _state.quat_nominal(0);
|
|
|
|
float q1 = _state.quat_nominal(1);
|
|
|
|
float q2 = _state.quat_nominal(2);
|
|
|
|
float q3 = _state.quat_nominal(3);
|
|
|
|
|
|
|
|
// get latest velocity in earth frame
|
|
|
|
float vn = _state.vel(0);
|
|
|
|
float ve = _state.vel(1);
|
|
|
|
float vd = _state.vel(2);
|
|
|
|
|
|
|
|
// calculate the observation noise variance - scaling noise linearly across flow quality range
|
|
|
|
float R_LOS_best = fmaxf(_params.flow_noise, 0.05f);
|
|
|
|
float R_LOS_worst = fmaxf(_params.flow_noise_qual_min, 0.05f);
|
|
|
|
|
|
|
|
// calculate a weighting that varies between 1 when flow quality is best and 0 when flow quality is worst
|
|
|
|
float weighting = (255.0f - (float)_params.flow_qual_min);
|
|
|
|
|
|
|
|
if (weighting >= 1.0f) {
|
|
|
|
weighting = math::constrain(((float)_flow_sample_delayed.quality - (float)_params.flow_qual_min) / weighting, 0.0f,
|
|
|
|
1.0f);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
weighting = 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
// take the weighted average of the observation noie for the best and wort flow quality
|
|
|
|
float R_LOS = sq(R_LOS_best * weighting + R_LOS_worst * (1.0f - weighting));
|
|
|
|
|
2016-03-09 18:39:45 -04:00
|
|
|
float H_LOS[2][24] = {}; // Optical flow observation Jacobians
|
|
|
|
float Kfusion[24][2] = {}; // Optical flow Kalman gains
|
2016-03-07 05:12:06 -04:00
|
|
|
|
|
|
|
// constrain height above ground to be above minimum height when sitting on ground
|
|
|
|
float heightAboveGndEst = math::max((_terrain_vpos - _state.pos(2)), gndclearance);
|
|
|
|
|
|
|
|
// get rotation nmatrix from earth to body
|
|
|
|
matrix::Dcm<float> earth_to_body(_state.quat_nominal);
|
|
|
|
earth_to_body = earth_to_body.transpose();
|
|
|
|
|
2016-04-09 13:22:49 -03:00
|
|
|
// calculate the sensor position relative to the IMU
|
|
|
|
Vector3f pos_offset_body = _params.flow_pos_body - _params.imu_pos_body;
|
|
|
|
|
|
|
|
// calculate the velocity of the sensor reelative to the imu in body frame
|
|
|
|
Vector3f vel_rel_imu_body = cross_product(_flow_sample_delayed.gyroXYZ , pos_offset_body);
|
|
|
|
|
|
|
|
// calculate the velocity of the sensor in the earth frame
|
|
|
|
Vector3f vel_rel_earth = _state.vel + _R_prev.transpose() * vel_rel_imu_body;
|
|
|
|
|
|
|
|
// rotate into body frame
|
|
|
|
Vector3f vel_body = earth_to_body * vel_rel_earth;
|
2016-03-07 05:12:06 -04:00
|
|
|
|
|
|
|
// calculate range from focal point to centre of image
|
|
|
|
float range = heightAboveGndEst / earth_to_body(2, 2); // absolute distance to the frame region in view
|
|
|
|
|
|
|
|
// calculate optical LOS rates using optical flow rates that have had the body angular rate contribution removed
|
|
|
|
// correct for gyro bias errors in the data used to do the motion compensation
|
|
|
|
// Note the sign convention used: A positive LOS rate is a RH rotaton of the scene about that axis.
|
|
|
|
Vector2f opt_flow_rate;
|
|
|
|
opt_flow_rate(0) = _flow_sample_delayed.flowRadXYcomp(0) / _flow_sample_delayed.dt + _flow_gyro_bias(0);
|
|
|
|
opt_flow_rate(1) = _flow_sample_delayed.flowRadXYcomp(1) / _flow_sample_delayed.dt + _flow_gyro_bias(1);
|
|
|
|
|
|
|
|
if (opt_flow_rate.norm() < _params.flow_rate_max) {
|
|
|
|
_flow_innov[0] = vel_body(1) / range - opt_flow_rate(0); // flow around the X axis
|
|
|
|
_flow_innov[1] = -vel_body(0) / range - opt_flow_rate(1); // flow around the Y axis
|
|
|
|
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Fuse X and Y axis measurements sequentially assuming observation errors are uncorrelated
|
|
|
|
// Calculate Obser ation Jacobians and Kalman gans for each measurement axis
|
|
|
|
for (uint8_t obs_index = 0; obs_index <= 1; obs_index++) {
|
|
|
|
|
|
|
|
if (obs_index == 0) {
|
|
|
|
|
|
|
|
// calculate X axis observation Jacobian
|
|
|
|
float t2 = 1.0f / range;
|
|
|
|
float t3 = q0 * q0;
|
|
|
|
float t4 = q1 * q1;
|
|
|
|
float t5 = q2 * q2;
|
|
|
|
float t6 = q3 * q3;
|
|
|
|
float t7 = q0 * q2 * 2.0f;
|
|
|
|
float t8 = q1 * q3 * 2.0f;
|
|
|
|
float t9 = q0 * q3 * 2.0f;
|
|
|
|
float t10 = q1 * q2 * 2.0f;
|
|
|
|
float t11 = q0 * q1 * 2.0f;
|
|
|
|
H_LOS[0][0] = t2 * (vn * (t7 + t8) + vd * (t3 - t4 - t5 + t6) - ve * (t11 - q2 * q3 * 2.0f));
|
|
|
|
H_LOS[0][2] = -t2 * (ve * (t9 + t10) - vd * (t7 - t8) + vn * (t3 + t4 - t5 - t6));
|
|
|
|
H_LOS[0][3] = -t2 * (t9 - t10);
|
|
|
|
H_LOS[0][4] = t2 * (t3 - t4 + t5 - t6);
|
|
|
|
H_LOS[0][5] = t2 * (t11 + q2 * q3 * 2.0f);
|
|
|
|
|
|
|
|
// calculate intermediate variables for the X observaton innovatoin variance and klmna gains
|
|
|
|
t2 = 1.0f / range;
|
|
|
|
t3 = q0 * q1 * 2.0f;
|
|
|
|
t4 = q2 * q3 * 2.0f;
|
|
|
|
t5 = q0 * q0;
|
|
|
|
t6 = q1 * q1;
|
|
|
|
t7 = q2 * q2;
|
|
|
|
t8 = q3 * q3;
|
|
|
|
t9 = q0 * q2 * 2.0f;
|
|
|
|
t10 = q1 * q3 * 2.0f;
|
|
|
|
t11 = q0 * q3 * 2.0f;
|
|
|
|
float t12 = q1 * q2 * 2.0f;
|
|
|
|
float t13 = t11 - t12;
|
|
|
|
float t14 = t3 + t4;
|
|
|
|
float t15 = t5 - t6 - t7 + t8;
|
|
|
|
float t16 = t15 * vd;
|
|
|
|
float t17 = t3 - t4;
|
|
|
|
float t18 = t9 + t10;
|
|
|
|
float t19 = t18 * vn;
|
|
|
|
float t28 = t17 * ve;
|
|
|
|
float t20 = t16 + t19 - t28;
|
|
|
|
float t21 = t5 + t6 - t7 - t8;
|
|
|
|
float t22 = t21 * vn;
|
|
|
|
float t23 = t9 - t10;
|
|
|
|
float t24 = t11 + t12;
|
|
|
|
float t25 = t24 * ve;
|
|
|
|
float t29 = t23 * vd;
|
|
|
|
float t26 = t22 + t25 - t29;
|
|
|
|
float t27 = t5 - t6 + t7 - t8;
|
|
|
|
float t30 = P[0][0] * t2 * t20;
|
|
|
|
float t31 = P[5][3] * t2 * t14;
|
|
|
|
float t32 = P[0][3] * t2 * t20;
|
|
|
|
float t33 = P[4][3] * t2 * t27;
|
|
|
|
float t56 = P[3][3] * t2 * t13;
|
|
|
|
float t57 = P[2][3] * t2 * t26;
|
|
|
|
float t34 = t31 + t32 + t33 - t56 - t57;
|
|
|
|
float t35 = P[5][5] * t2 * t14;
|
|
|
|
float t36 = P[0][5] * t2 * t20;
|
|
|
|
float t37 = P[4][5] * t2 * t27;
|
|
|
|
float t59 = P[3][5] * t2 * t13;
|
|
|
|
float t60 = P[2][5] * t2 * t26;
|
|
|
|
float t38 = t35 + t36 + t37 - t59 - t60;
|
|
|
|
float t39 = t2 * t14 * t38;
|
|
|
|
float t40 = P[5][0] * t2 * t14;
|
|
|
|
float t41 = P[4][0] * t2 * t27;
|
|
|
|
float t61 = P[3][0] * t2 * t13;
|
|
|
|
float t62 = P[2][0] * t2 * t26;
|
|
|
|
float t42 = t30 + t40 + t41 - t61 - t62;
|
|
|
|
float t43 = t2 * t20 * t42;
|
|
|
|
float t44 = P[5][2] * t2 * t14;
|
|
|
|
float t45 = P[0][2] * t2 * t20;
|
|
|
|
float t46 = P[4][2] * t2 * t27;
|
|
|
|
float t55 = P[2][2] * t2 * t26;
|
|
|
|
float t63 = P[3][2] * t2 * t13;
|
|
|
|
float t47 = t44 + t45 + t46 - t55 - t63;
|
|
|
|
float t48 = P[5][4] * t2 * t14;
|
|
|
|
float t49 = P[0][4] * t2 * t20;
|
|
|
|
float t50 = P[4][4] * t2 * t27;
|
|
|
|
float t65 = P[3][4] * t2 * t13;
|
|
|
|
float t66 = P[2][4] * t2 * t26;
|
|
|
|
float t51 = t48 + t49 + t50 - t65 - t66;
|
|
|
|
float t52 = t2 * t27 * t51;
|
|
|
|
float t58 = t2 * t13 * t34;
|
|
|
|
float t64 = t2 * t26 * t47;
|
|
|
|
float t53 = R_LOS + t39 + t43 + t52 - t58 - t64;
|
|
|
|
float t54;
|
|
|
|
|
|
|
|
// calculate innovation variance for X axis observation and protect against a badly conditioned calculation
|
|
|
|
if (t53 >= R_LOS) {
|
|
|
|
t54 = 1.0f / t53;
|
|
|
|
_flow_innov_var[0] = t53;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// we need to reinitialise the covariance matrix and abort this fusion step
|
|
|
|
initialiseCovariance();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// calculate Kalman gains for X-axis observation
|
|
|
|
Kfusion[0][0] = t54 * (t30 - P[0][3] * t2 * (t11 - q1 * q2 * 2.0f) + P[0][5] * t2 * t14 - P[0][2] * t2 * t26 + P[0][4] *
|
|
|
|
t2 * t27);
|
|
|
|
Kfusion[1][0] = t54 * (-P[1][3] * t2 * t13 + P[1][5] * t2 * t14 + P[1][0] * t2 * t20 - P[1][2] * t2 * t26 + P[1][4] * t2
|
|
|
|
* t27);
|
|
|
|
Kfusion[2][0] = t54 * (-t55 - P[2][3] * t2 * t13 + P[2][5] * t2 * t14 + P[2][0] * t2 * t20 + P[2][4] * t2 * t27);
|
|
|
|
Kfusion[3][0] = t54 * (-t56 + P[3][5] * t2 * t14 + P[3][0] * t2 * t20 - P[3][2] * t2 * t26 + P[3][4] * t2 * t27);
|
|
|
|
Kfusion[4][0] = t54 * (t50 - P[4][3] * t2 * t13 + P[4][5] * t2 * t14 + P[4][0] * t2 * t20 - P[4][2] * t2 * t26);
|
|
|
|
Kfusion[5][0] = t54 * (t35 - P[5][3] * t2 * t13 + P[5][0] * t2 * t20 - P[5][2] * t2 * t26 + P[5][4] * t2 * t27);
|
|
|
|
Kfusion[6][0] = t54 * (-P[6][3] * t2 * t13 + P[6][5] * t2 * t14 + P[6][0] * t2 * t20 - P[6][2] * t2 * t26 + P[6][4] * t2
|
|
|
|
* t27);
|
|
|
|
Kfusion[7][0] = t54 * (-P[7][3] * t2 * t13 + P[7][5] * t2 * t14 + P[7][0] * t2 * t20 - P[7][2] * t2 * t26 + P[7][4] * t2
|
|
|
|
* t27);
|
|
|
|
Kfusion[8][0] = t54 * (-P[8][3] * t2 * t13 + P[8][5] * t2 * t14 + P[8][0] * t2 * t20 - P[8][2] * t2 * t26 + P[8][4] * t2
|
|
|
|
* t27);
|
|
|
|
Kfusion[9][0] = t54 * (-P[9][3] * t2 * t13 + P[9][5] * t2 * t14 + P[9][0] * t2 * t20 - P[9][2] * t2 * t26 + P[9][4] * t2
|
|
|
|
* t27);
|
|
|
|
Kfusion[10][0] = t54 * (-P[10][3] * t2 * t13 + P[10][5] * t2 * t14 + P[10][0] * t2 * t20 - P[10][2] * t2 * t26 +
|
|
|
|
P[10][4] * t2 * t27);
|
|
|
|
Kfusion[11][0] = t54 * (-P[11][3] * t2 * t13 + P[11][5] * t2 * t14 + P[11][0] * t2 * t20 - P[11][2] * t2 * t26 +
|
|
|
|
P[11][4] * t2 * t27);
|
|
|
|
Kfusion[12][0] = t54 * (-P[12][3] * t2 * t13 + P[12][5] * t2 * t14 + P[12][0] * t2 * t20 - P[12][2] * t2 * t26 +
|
|
|
|
P[12][4] * t2 * t27);
|
|
|
|
Kfusion[13][0] = t54 * (-P[13][3] * t2 * t13 + P[13][5] * t2 * t14 + P[13][0] * t2 * t20 - P[13][2] * t2 * t26 +
|
|
|
|
P[13][4] * t2 * t27);
|
|
|
|
Kfusion[14][0] = t54 * (-P[14][3] * t2 * t13 + P[14][5] * t2 * t14 + P[14][0] * t2 * t20 - P[14][2] * t2 * t26 +
|
|
|
|
P[14][4] * t2 * t27);
|
|
|
|
Kfusion[15][0] = t54 * (-P[15][3] * t2 * t13 + P[15][5] * t2 * t14 + P[15][0] * t2 * t20 - P[15][2] * t2 * t26 +
|
|
|
|
P[15][4] * t2 * t27);
|
|
|
|
|
|
|
|
if (_control_status.flags.mag_3D) {
|
|
|
|
Kfusion[16][0] = t54 * (-P[16][3] * t2 * t13 + P[16][5] * t2 * t14 + P[16][0] * t2 * t20 - P[16][2] * t2 * t26 +
|
|
|
|
P[16][4] * t2 * t27);
|
|
|
|
Kfusion[17][0] = t54 * (-P[17][3] * t2 * t13 + P[17][5] * t2 * t14 + P[17][0] * t2 * t20 - P[17][2] * t2 * t26 +
|
|
|
|
P[17][4] * t2 * t27);
|
|
|
|
Kfusion[18][0] = t54 * (-P[18][3] * t2 * t13 + P[18][5] * t2 * t14 + P[18][0] * t2 * t20 - P[18][2] * t2 * t26 +
|
|
|
|
P[18][4] * t2 * t27);
|
|
|
|
Kfusion[19][0] = t54 * (-P[19][3] * t2 * t13 + P[19][5] * t2 * t14 + P[19][0] * t2 * t20 - P[19][2] * t2 * t26 +
|
|
|
|
P[19][4] * t2 * t27);
|
|
|
|
Kfusion[20][0] = t54 * (-P[20][3] * t2 * t13 + P[20][5] * t2 * t14 + P[20][0] * t2 * t20 - P[20][2] * t2 * t26 +
|
|
|
|
P[20][4] * t2 * t27);
|
|
|
|
Kfusion[21][0] = t54 * (-P[21][3] * t2 * t13 + P[21][5] * t2 * t14 + P[21][0] * t2 * t20 - P[21][2] * t2 * t26 +
|
|
|
|
P[21][4] * t2 * t27);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_control_status.flags.wind) {
|
|
|
|
Kfusion[22][0] = t54 * (-P[22][3] * t2 * t13 + P[22][5] * t2 * t14 + P[22][0] * t2 * t20 - P[22][2] * t2 * t26 +
|
|
|
|
P[22][4] * t2 * t27);
|
|
|
|
Kfusion[23][0] = t54 * (-P[23][3] * t2 * t13 + P[23][5] * t2 * t14 + P[23][0] * t2 * t20 - P[23][2] * t2 * t26 +
|
|
|
|
P[23][4] * t2 * t27);
|
|
|
|
}
|
|
|
|
|
|
|
|
// run innovation consistency checks
|
|
|
|
optflow_test_ratio[0] = sq(_flow_innov[0]) / (sq(math::max(_params.flow_innov_gate, 1.0f)) * _flow_innov_var[0]);
|
|
|
|
|
|
|
|
} else if (obs_index == 1) {
|
|
|
|
|
|
|
|
// calculate Y axis observation Jacobian
|
|
|
|
float t2 = 1.0f / range;
|
|
|
|
float t3 = q0 * q0;
|
|
|
|
float t4 = q1 * q1;
|
|
|
|
float t5 = q2 * q2;
|
|
|
|
float t6 = q3 * q3;
|
|
|
|
float t7 = q0 * q1 * 2.0f;
|
|
|
|
float t8 = q0 * q3 * 2.0f;
|
|
|
|
float t9 = q0 * q2 * 2.0f;
|
|
|
|
float t10 = q1 * q3 * 2.0f;
|
|
|
|
H_LOS[1][1] = t2 * (vn * (t9 + t10) + vd * (t3 - t4 - t5 + t6) - ve * (t7 - q2 * q3 * 2.0f));
|
|
|
|
H_LOS[1][2] = -t2 * (ve * (t3 - t4 + t5 - t6) + vd * (t7 + q2 * q3 * 2.0f) - vn * (t8 - q1 * q2 * 2.0f));
|
|
|
|
H_LOS[1][3] = -t2 * (t3 + t4 - t5 - t6);
|
|
|
|
H_LOS[1][4] = -t2 * (t8 + q1 * q2 * 2.0f);
|
|
|
|
H_LOS[1][5] = t2 * (t9 - t10);
|
|
|
|
|
|
|
|
// calculate intermediate variables for the X observaton innovatoin variance and klmna gains
|
|
|
|
t2 = 1.0f / range;
|
|
|
|
t3 = q0 * q2 * 2.0f;
|
|
|
|
t4 = q0 * q0;
|
|
|
|
t5 = q1 * q1;
|
|
|
|
t6 = q2 * q2;
|
|
|
|
t7 = q3 * q3;
|
|
|
|
t8 = q0 * q1 * 2.0f;
|
|
|
|
t9 = q0 * q3 * 2.0f;
|
|
|
|
t10 = q1 * q2 * 2.0f;
|
|
|
|
float t11 = t9 + t10;
|
|
|
|
float t12 = q1 * q3 * 2.0f;
|
|
|
|
float t13 = t4 - t5 - t6 + t7;
|
|
|
|
float t14 = t13 * vd;
|
|
|
|
float t15 = q2 * q3 * 2.0f;
|
|
|
|
float t16 = t3 + t12;
|
|
|
|
float t17 = t16 * vn;
|
|
|
|
float t18 = t4 - t5 + t6 - t7;
|
|
|
|
float t19 = t18 * ve;
|
|
|
|
float t20 = t8 + t15;
|
|
|
|
float t21 = t20 * vd;
|
|
|
|
float t22 = t9 - t10;
|
|
|
|
float t28 = t22 * vn;
|
|
|
|
float t23 = t19 + t21 - t28;
|
|
|
|
float t24 = t4 + t5 - t6 - t7;
|
|
|
|
float t25 = t3 - t12;
|
|
|
|
float t26 = t8 - t15;
|
|
|
|
float t29 = t26 * ve;
|
|
|
|
float t27 = t14 + t17 - t29;
|
|
|
|
float t30 = P[4][4] * t2 * t11;
|
|
|
|
float t31 = P[2][4] * t2 * t23;
|
|
|
|
float t32 = P[3][4] * t2 * t24;
|
|
|
|
float t56 = P[5][4] * t2 * t25;
|
|
|
|
float t57 = P[1][4] * t2 * t27;
|
|
|
|
float t33 = t30 + t31 + t32 - t56 - t57;
|
|
|
|
float t34 = t2 * t11 * t33;
|
|
|
|
float t35 = P[4][5] * t2 * t11;
|
|
|
|
float t36 = P[2][5] * t2 * t23;
|
|
|
|
float t37 = P[3][5] * t2 * t24;
|
|
|
|
float t58 = P[5][5] * t2 * t25;
|
|
|
|
float t59 = P[1][5] * t2 * t27;
|
|
|
|
float t38 = t35 + t36 + t37 - t58 - t59;
|
|
|
|
float t39 = P[4][1] * t2 * t11;
|
|
|
|
float t40 = P[2][1] * t2 * t23;
|
|
|
|
float t41 = P[3][1] * t2 * t24;
|
|
|
|
float t55 = P[1][1] * t2 * t27;
|
|
|
|
float t61 = P[5][1] * t2 * t25;
|
|
|
|
float t42 = t39 + t40 + t41 - t55 - t61;
|
|
|
|
float t43 = P[4][2] * t2 * t11;
|
|
|
|
float t44 = P[2][2] * t2 * t23;
|
|
|
|
float t45 = P[3][2] * t2 * t24;
|
|
|
|
float t63 = P[5][2] * t2 * t25;
|
|
|
|
float t64 = P[1][2] * t2 * t27;
|
|
|
|
float t46 = t43 + t44 + t45 - t63 - t64;
|
|
|
|
float t47 = t2 * t23 * t46;
|
|
|
|
float t48 = P[4][3] * t2 * t11;
|
|
|
|
float t49 = P[2][3] * t2 * t23;
|
|
|
|
float t50 = P[3][3] * t2 * t24;
|
|
|
|
float t65 = P[5][3] * t2 * t25;
|
|
|
|
float t66 = P[1][3] * t2 * t27;
|
|
|
|
float t51 = t48 + t49 + t50 - t65 - t66;
|
|
|
|
float t52 = t2 * t24 * t51;
|
|
|
|
float t60 = t2 * t25 * t38;
|
|
|
|
float t62 = t2 * t27 * t42;
|
|
|
|
float t53 = R_LOS + t34 + t47 + t52 - t60 - t62;
|
|
|
|
float t54;
|
|
|
|
|
|
|
|
// calculate innovation variance for X axis observation and protect against a badly conditioned calculation
|
|
|
|
if (t53 >= R_LOS) {
|
|
|
|
t54 = 1.0f / t53;
|
|
|
|
_flow_innov_var[1] = t53;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// we need to reinitialise the covariance matrix and abort this fusion step
|
|
|
|
initialiseCovariance();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// calculate Kalman gains for X-axis observation
|
|
|
|
Kfusion[0][1] = -t54 * (P[0][4] * t2 * t11 + P[0][2] * t2 * t23 + P[0][3] * t2 * t24 - P[0][1] * t2 * t27 - P[0][5] * t2
|
|
|
|
* t25);
|
|
|
|
Kfusion[1][1] = -t54 * (-t55 + P[1][4] * t2 * t11 + P[1][2] * t2 * t23 + P[1][3] * t2 * t24 - P[1][5] * t2 * t25);
|
|
|
|
Kfusion[2][1] = -t54 * (t44 + P[2][4] * t2 * t11 + P[2][3] * t2 * t24 - P[2][1] * t2 * t27 - P[2][5] * t2 * t25);
|
|
|
|
Kfusion[3][1] = -t54 * (t50 + P[3][4] * t2 * t11 + P[3][2] * t2 * t23 - P[3][1] * t2 * t27 - P[3][5] * t2 * t25);
|
|
|
|
Kfusion[4][1] = -t54 * (t30 + P[4][2] * t2 * t23 + P[4][3] * t2 * t24 - P[4][1] * t2 * t27 - P[4][5] * t2 * t25);
|
|
|
|
Kfusion[5][1] = -t54 * (-t58 + P[5][4] * t2 * t11 + P[5][2] * t2 * t23 + P[5][3] * t2 * t24 - P[5][1] * t2 * t27);
|
|
|
|
Kfusion[6][1] = -t54 * (P[6][4] * t2 * t11 + P[6][2] * t2 * t23 + P[6][3] * t2 * t24 - P[6][1] * t2 * t27 - P[6][5] * t2
|
|
|
|
* t25);
|
|
|
|
Kfusion[7][1] = -t54 * (P[7][4] * t2 * t11 + P[7][2] * t2 * t23 + P[7][3] * t2 * t24 - P[7][1] * t2 * t27 - P[7][5] * t2
|
|
|
|
* t25);
|
|
|
|
Kfusion[8][1] = -t54 * (P[8][4] * t2 * t11 + P[8][2] * t2 * t23 + P[8][3] * t2 * t24 - P[8][1] * t2 * t27 - P[8][5] * t2
|
|
|
|
* t25);
|
|
|
|
Kfusion[9][1] = -t54 * (P[9][4] * t2 * t11 + P[9][2] * t2 * t23 + P[9][3] * t2 * t24 - P[9][1] * t2 * t27 - P[9][5] * t2
|
|
|
|
* t25);
|
|
|
|
Kfusion[10][1] = -t54 * (P[10][4] * t2 * t11 + P[10][2] * t2 * t23 + P[10][3] * t2 * t24 - P[10][1] * t2 * t27 -
|
|
|
|
P[10][5] * t2 * t25);
|
|
|
|
Kfusion[11][1] = -t54 * (P[11][4] * t2 * t11 + P[11][2] * t2 * t23 + P[11][3] * t2 * t24 - P[11][1] * t2 * t27 -
|
|
|
|
P[11][5] * t2 * t25);
|
|
|
|
Kfusion[12][1] = -t54 * (P[12][4] * t2 * t11 + P[12][2] * t2 * t23 + P[12][3] * t2 * t24 - P[12][1] * t2 * t27 -
|
|
|
|
P[12][5] * t2 * t25);
|
|
|
|
Kfusion[13][1] = -t54 * (P[13][4] * t2 * t11 + P[13][2] * t2 * t23 + P[13][3] * t2 * t24 - P[13][1] * t2 * t27 -
|
|
|
|
P[13][5] * t2 * t25);
|
|
|
|
Kfusion[14][1] = -t54 * (P[14][4] * t2 * t11 + P[14][2] * t2 * t23 + P[14][3] * t2 * t24 - P[14][1] * t2 * t27 -
|
|
|
|
P[14][5] * t2 * t25);
|
|
|
|
Kfusion[15][1] = -t54 * (P[15][4] * t2 * t11 + P[15][2] * t2 * t23 + P[15][3] * t2 * t24 - P[15][1] * t2 * t27 -
|
|
|
|
P[15][5] * t2 * t25);
|
|
|
|
|
|
|
|
if (_control_status.flags.mag_3D) {
|
|
|
|
Kfusion[16][1] = -t54 * (P[16][4] * t2 * t11 + P[16][2] * t2 * t23 + P[16][3] * t2 * t24 - P[16][1] * t2 * t27 -
|
|
|
|
P[16][5] * t2 * t25);
|
|
|
|
Kfusion[17][1] = -t54 * (P[17][4] * t2 * t11 + P[17][2] * t2 * t23 + P[17][3] * t2 * t24 - P[17][1] * t2 * t27 -
|
|
|
|
P[17][5] * t2 * t25);
|
|
|
|
Kfusion[18][1] = -t54 * (P[18][4] * t2 * t11 + P[18][2] * t2 * t23 + P[18][3] * t2 * t24 - P[18][1] * t2 * t27 -
|
|
|
|
P[18][5] * t2 * t25);
|
|
|
|
Kfusion[19][1] = -t54 * (P[19][4] * t2 * t11 + P[19][2] * t2 * t23 + P[19][3] * t2 * t24 - P[19][1] * t2 * t27 -
|
|
|
|
P[19][5] * t2 * t25);
|
|
|
|
Kfusion[20][1] = -t54 * (P[20][4] * t2 * t11 + P[20][2] * t2 * t23 + P[20][3] * t2 * t24 - P[20][1] * t2 * t27 -
|
|
|
|
P[20][5] * t2 * t25);
|
|
|
|
Kfusion[21][1] = -t54 * (P[21][4] * t2 * t11 + P[21][2] * t2 * t23 + P[21][3] * t2 * t24 - P[21][1] * t2 * t27 -
|
|
|
|
P[21][5] * t2 * t25);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_control_status.flags.wind) {
|
|
|
|
Kfusion[22][1] = -t54 * (P[22][4] * t2 * t11 + P[22][2] * t2 * t23 + P[22][3] * t2 * t24 - P[22][1] * t2 * t27 -
|
|
|
|
P[22][5] * t2 * t25);
|
|
|
|
Kfusion[23][1] = -t54 * (P[23][4] * t2 * t11 + P[23][2] * t2 * t23 + P[23][3] * t2 * t24 - P[23][1] * t2 * t27 -
|
|
|
|
P[23][5] * t2 * t25);
|
|
|
|
}
|
|
|
|
|
|
|
|
// run innovation consistency check
|
|
|
|
optflow_test_ratio[1] = sq(_flow_innov[1]) / (sq(math::max(_params.flow_innov_gate, 1.0f)) * _flow_innov_var[1]);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if either axis fails, we fail the sensor
|
|
|
|
if (optflow_test_ratio[0] > 1.0f || optflow_test_ratio[1] > 1.0f) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint8_t obs_index = 0; obs_index <= 1; obs_index++) {
|
|
|
|
|
|
|
|
// by definition our error state is zero at the time of fusion
|
|
|
|
_state.ang_error.setZero();
|
|
|
|
|
|
|
|
// copy the Kalman gain vector for the axis we are fusing
|
|
|
|
float gain[24];
|
|
|
|
|
|
|
|
for (unsigned row = 0; row <= 23; row++) {
|
|
|
|
gain[row] = Kfusion[row][obs_index];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the state vector
|
|
|
|
fuse(gain, _flow_innov[obs_index]);
|
|
|
|
|
|
|
|
// correct the quaternion using the attitude error state
|
|
|
|
Quaternion q_correction;
|
|
|
|
q_correction.from_axis_angle(_state.ang_error);
|
|
|
|
_state.quat_nominal = q_correction * _state.quat_nominal;
|
|
|
|
_state.quat_nominal.normalize();
|
|
|
|
|
|
|
|
// reset attitude error to zero after the correction has been applied
|
|
|
|
_state.ang_error.setZero();
|
|
|
|
|
|
|
|
// apply covariance correction via P_new = (I -K*H)*P
|
|
|
|
// first calculate expression for KHP
|
|
|
|
// then calculate P - KHP
|
|
|
|
for (unsigned row = 0; row < _k_num_states; row++) {
|
|
|
|
for (unsigned column = 0; column <= 5; column++) {
|
|
|
|
KH[row][column] = gain[row] * H_LOS[obs_index][column];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned row = 0; row < _k_num_states; row++) {
|
|
|
|
for (unsigned column = 0; column < _k_num_states; column++) {
|
|
|
|
float tmp = KH[row][0] * P[0][column];
|
|
|
|
tmp += KH[row][1] * P[1][column];
|
|
|
|
tmp += KH[row][2] * P[2][column];
|
|
|
|
tmp += KH[row][3] * P[3][column];
|
|
|
|
tmp += KH[row][4] * P[4][column];
|
|
|
|
tmp += KH[row][5] * P[5][column];
|
|
|
|
KHP[row][column] = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned row = 0; row < _k_num_states; row++) {
|
|
|
|
for (unsigned column = 0; column < _k_num_states; column++) {
|
|
|
|
P[row][column] -= KHP[row][column];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_time_last_of_fuse = _time_last_imu;
|
|
|
|
_gps_check_fail_status.value = 0;
|
|
|
|
makeSymmetrical();
|
|
|
|
limitCov();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Ekf::get_flow_innov(float flow_innov[2])
|
|
|
|
{
|
|
|
|
memcpy(flow_innov, _flow_innov, sizeof(_flow_innov));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Ekf::get_flow_innov_var(float flow_innov_var[2])
|
|
|
|
{
|
|
|
|
memcpy(flow_innov_var, _flow_innov_var, sizeof(_flow_innov_var));
|
|
|
|
}
|
|
|
|
|
|
|
|
// calculate optical flow gyro bias errors
|
|
|
|
void Ekf::calcOptFlowBias()
|
|
|
|
{
|
|
|
|
// accumulate the bias corrected delta angles from the navigation sensor and lapsed time
|
2016-04-09 13:22:49 -03:00
|
|
|
_imu_del_ang_of += _imu_sample_delayed.delta_ang;
|
2016-03-07 05:12:06 -04:00
|
|
|
_delta_time_of += _imu_sample_delayed.delta_ang_dt;
|
|
|
|
|
|
|
|
// reset the accumulators if the time interval is too large
|
|
|
|
if (_delta_time_of > 1.0f) {
|
|
|
|
_imu_del_ang_of.setZero();
|
|
|
|
_delta_time_of = 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if accumulation time differences are not excessive and accumulation time is adequate
|
|
|
|
// compare the optical flow and and navigation rate data and calculate a bias error
|
|
|
|
if (_fuse_flow) {
|
|
|
|
if ((fabsf(_delta_time_of - _flow_sample_delayed.dt) < 0.05f) && (_delta_time_of > 0.01f)
|
|
|
|
&& (_flow_sample_delayed.dt > 0.01f)) {
|
|
|
|
// calculate a reference angular rate
|
2016-04-09 13:22:49 -03:00
|
|
|
Vector3f reference_body_rate;
|
|
|
|
reference_body_rate = _imu_del_ang_of * (1.0f / _delta_time_of);
|
2016-03-07 05:12:06 -04:00
|
|
|
|
|
|
|
// calculate the optical flow sensor measured body rate
|
2016-04-09 13:22:49 -03:00
|
|
|
Vector3f of_body_rate;
|
|
|
|
of_body_rate = _flow_sample_delayed.gyroXYZ * (1.0f / _flow_sample_delayed.dt);
|
2016-03-07 05:12:06 -04:00
|
|
|
|
|
|
|
// calculate the bias estimate using a combined LPF and spike filter
|
|
|
|
_flow_gyro_bias(0) = 0.99f * _flow_gyro_bias(0) + 0.01f * math::constrain((of_body_rate(0) - reference_body_rate(0)),
|
|
|
|
-0.1f, 0.1f);
|
|
|
|
_flow_gyro_bias(1) = 0.99f * _flow_gyro_bias(1) + 0.01f * math::constrain((of_body_rate(1) - reference_body_rate(1)),
|
|
|
|
-0.1f, 0.1f);
|
2016-04-09 13:22:49 -03:00
|
|
|
_flow_gyro_bias(2) = 0.99f * _flow_gyro_bias(2) + 0.01f * math::constrain((of_body_rate(2) - reference_body_rate(2)),
|
|
|
|
-0.1f, 0.1f);
|
2016-03-07 05:12:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// reset the accumulators
|
|
|
|
_imu_del_ang_of.setZero();
|
|
|
|
_delta_time_of = 0.0f;
|
|
|
|
}
|
|
|
|
}
|