update credits
[librepilot.git] / flight / libraries / insgps14state.c
blob0a498d026d176bb874cc1798f40ecaa3566f9490
1 /**
2 ******************************************************************************
3 * @addtogroup Math
4 * @{
5 * @addtogroup INSGPS
6 * @{
7 * @brief INSGPS is a joint attitude and position estimation EKF
9 * @file insgps.c
10 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
11 * Tau Labs, http://github.com/TauLabs Copyright (C) 2012-2013.
12 * The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016.
13 * @brief An INS/GPS algorithm implemented with an EKF.
15 * @see The GNU Public License (GPL) Version 3
17 *****************************************************************************/
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 3 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
26 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 * for more details.
29 * You should have received a copy of the GNU General Public License along
30 * with this program; if not, write to the Free Software Foundation, Inc.,
31 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 #include "insgps.h"
35 #include <math.h>
36 #include <stdint.h>
37 #include <stdbool.h>
38 #include <pios_math.h>
39 #include <mathmisc.h>
40 #include <pios_constants.h>
42 // constants/macros/typdefs
43 #define NUMX 14 // number of states, X is the state vector
44 #define NUMW 10 // number of plant noise inputs, w is disturbance noise vector
45 #define NUMV 10 // number of measurements, v is the measurement noise vector
46 #define NUMU 6 // number of deterministic inputs, U is the input vector
47 #pragma GCC optimize "O3"
48 // Private functions
49 void CovariancePrediction(float F[NUMX][NUMX], float G[NUMX][NUMW],
50 float Q[NUMW], float dT, float P[NUMX][NUMX]);
51 static void SerialUpdate(float H[NUMV][NUMX], float R[NUMV], float Z[NUMV],
52 float Y[NUMV], float P[NUMX][NUMX], float X[NUMX],
53 uint16_t SensorsUsed);
54 static void RungeKutta(float X[NUMX], float U[NUMU], float dT);
55 static void StateEq(float X[NUMX], float U[NUMU], float Xdot[NUMX]);
56 static void LinearizeFG(float X[NUMX], float U[NUMU], float F[NUMX][NUMX],
57 float G[NUMX][NUMW]);
58 static void MeasurementEq(float X[NUMX], float Be[3], float Y[NUMV]);
59 static void LinearizeH(float X[NUMX], float Be[3], float H[NUMV][NUMX]);
61 // Private variables
63 // speed optimizations, describe matrix sparsity
64 // derived from state equations in
65 // LinearizeFG() and LinearizeH():
67 // usage F: usage G: usage H:
68 // -0123456789abcd 0123456789 0123456789abcd
69 // 0...X.......... .......... X.............
70 // 1....X......... .......... .X............
71 // 2.....X........ .......... ..X...........
72 // 3......XXXX...X ...XXX.... ...X..........
73 // 4......XXXX...X ...XXX.... ....X.........
74 // 5......XXXX...X ...XXX.... .....X........
75 // 6.....ooXXXXXX. XXX....... ......XXXX....
76 // 7.....oXoXXXXX. XXX....... ......XXXX....
77 // 8.....oXXoXXXX. XXX....... ......XXXX....
78 // 9.....oXXXoXXX. XXX....... ..X...........
79 // a.............. ..........
80 // b.............. ..........
81 // c.............. ..........
82 // d.............. ..........
84 static int8_t FrowMin[NUMX] = { 3, 4, 5, 6, 6, 6, 5, 5, 5, 5, 14, 14, 14, 14 };
85 static int8_t FrowMax[NUMX] = { 3, 4, 5, 13, 13, 13, 12, 12, 12, 12, -1, -1, -1, -1 };
87 static int8_t GrowMin[NUMX] = { 10, 10, 10, 3, 3, 3, 0, 0, 0, 0, 10, 10, 10, 10 };
88 static int8_t GrowMax[NUMX] = { -1, -1, -1, 5, 5, 5, 2, 2, 2, 2, -1, -1, -1, -1 };
90 static int8_t HrowMin[NUMV] = { 0, 1, 2, 3, 4, 5, 6, 6, 6, 2 };
91 static int8_t HrowMax[NUMV] = { 0, 1, 2, 3, 4, 5, 9, 9, 9, 2 };
93 static struct EKFData {
94 float F[NUMX][NUMX];
95 float G[NUMX][NUMW];
96 float H[NUMV][NUMX]; // linearized system matrices
97 // global to init to zero and maintain zero elements
98 float Be[3]; // local magnetic unit vector in NED frame
99 float P[NUMX][NUMX];
100 float X[NUMX]; // covariance matrix and state vector
101 float Q[NUMW];
102 float R[NUMV]; // input noise and measurement noise variances
103 float K[NUMX][NUMV]; // feedback gain matrix
104 } ekf;
106 // Global variables
107 struct NavStruct Nav;
109 // ************* Exposed Functions ****************
110 // *************************************************
112 uint16_t ins_get_num_states()
114 return NUMX;
117 void INSGPSInit()
119 ekf.Be[0] = 1.0f;
120 ekf.Be[1] = 0;
121 ekf.Be[2] = 0; // local magnetic unit vector
123 for (int i = 0; i < NUMX; i++) {
124 for (int j = 0; j < NUMX; j++) {
125 ekf.P[i][j] = 0.0f; // zero all terms
126 ekf.F[i][j] = 0.0f;
129 for (int j = 0; j < NUMW; j++) {
130 ekf.G[i][j] = 0.0f;
133 for (int j = 0; j < NUMV; j++) {
134 ekf.H[j][i] = 0.0f;
135 ekf.K[i][j] = 0.0f;
138 ekf.X[i] = 0.0f;
140 for (int i = 0; i < NUMW; i++) {
141 ekf.Q[i] = 0.0f;
143 for (int i = 0; i < NUMV; i++) {
144 ekf.R[i] = 0.0f;
147 ekf.P[0][0] = ekf.P[1][1] = ekf.P[2][2] = 25.0f; // initial position variance (m^2)
148 ekf.P[3][3] = ekf.P[4][4] = ekf.P[5][5] = 5.0f; // initial velocity variance (m/s)^2
149 ekf.P[6][6] = ekf.P[7][7] = ekf.P[8][8] = ekf.P[9][9] = 1e-5f; // initial quaternion variance
150 ekf.P[10][10] = ekf.P[11][11] = ekf.P[12][12] = 1e-6f; // initial gyro bias variance (rad/s)^2
151 ekf.P[13][13] = 1e-5f; // initial accel bias variance (deg/s)^2
153 ekf.X[0] = ekf.X[1] = ekf.X[2] = ekf.X[3] = ekf.X[4] = ekf.X[5] = 0.0f; // initial pos and vel (m)
154 ekf.X[6] = 1.0f;
155 ekf.X[7] = ekf.X[8] = ekf.X[9] = 0.0f; // initial quaternion (level and North) (m/s)
156 ekf.X[10] = ekf.X[11] = ekf.X[12] = 0.0f; // initial gyro bias (rad/s)
157 ekf.X[13] = 0.0f; // initial accel bias
159 ekf.Q[0] = ekf.Q[1] = ekf.Q[2] = 1e-5f; // gyro noise variance (rad/s)^2
160 ekf.Q[3] = ekf.Q[4] = ekf.Q[5] = 1e-5f; // accelerometer noise variance (m/s^2)^2
161 ekf.Q[6] = ekf.Q[7] = 1e-6f; // gyro x and y bias random walk variance (rad/s^2)^2
162 ekf.Q[8] = 1e-6f; // gyro z bias random walk variance (rad/s^2)^2
163 ekf.Q[9] = 5e-4f; // accel bias random walk variance (m/s^3)^2
165 ekf.R[0] = ekf.R[1] = 0.004f; // High freq GPS horizontal position noise variance (m^2)
166 ekf.R[2] = 0.036f; // High freq GPS vertical position noise variance (m^2)
167 ekf.R[3] = ekf.R[4] = 0.004f; // High freq GPS horizontal velocity noise variance (m/s)^2
168 ekf.R[5] = 0.004f; // High freq GPS vertical velocity noise variance (m/s)^2
169 ekf.R[6] = ekf.R[7] = ekf.R[8] = 0.005f; // magnetometer unit vector noise variance
170 ekf.R[9] = .05f; // High freq altimeter noise variance (m^2)
173 // ! Set the current flight state
174 void INSSetArmed(bool armed)
176 return;
178 // Speed up convergence of accel and gyro bias when not armed
179 if (armed) {
180 ekf.Q[9] = 1e-4f;
181 ekf.Q[8] = 2e-9f;
182 } else {
183 ekf.Q[9] = 1e-2f;
184 ekf.Q[8] = 2e-8f;
189 * Get the current state estimate (null input skips that get)
190 * @param[out] pos The position in NED space (m)
191 * @param[out] vel The velocity in NED (m/s)
192 * @param[out] attitude Quaternion representation of attitude
193 * @param[out] gyros_bias Estimate of gyro bias (rad/s)
194 * @param[out] accel_bias Estiamte of the accel bias (m/s^2)
196 void INSGetState(float *pos, float *vel, float *attitude, float *gyro_bias, float *accel_bias)
198 if (pos) {
199 pos[0] = ekf.X[0];
200 pos[1] = ekf.X[1];
201 pos[2] = ekf.X[2];
204 if (vel) {
205 vel[0] = ekf.X[3];
206 vel[1] = ekf.X[4];
207 vel[2] = ekf.X[5];
210 if (attitude) {
211 attitude[0] = ekf.X[6];
212 attitude[1] = ekf.X[7];
213 attitude[2] = ekf.X[8];
214 attitude[3] = ekf.X[9];
217 if (gyro_bias) {
218 gyro_bias[0] = ekf.X[10];
219 gyro_bias[1] = ekf.X[11];
220 gyro_bias[2] = ekf.X[12];
223 if (accel_bias) {
224 accel_bias[0] = 0.0f;
225 accel_bias[1] = 0.0f;
226 accel_bias[2] = ekf.X[13];
231 * Get the variance, for visualizing the filter performance
232 * @param[out var_out The variances
234 void INSGetVariance(float *var_out)
236 for (uint32_t i = 0; i < NUMX; i++) {
237 var_out[i] = ekf.P[i][i];
241 void INSResetP(const float *PDiag)
243 uint8_t i, j;
245 // if PDiag[i] nonzero then clear row and column and set diagonal element
246 for (i = 0; i < NUMX; i++) {
247 if (PDiag != 0) {
248 for (j = 0; j < NUMX; j++) {
249 ekf.P[i][j] = ekf.P[j][i] = 0.0f;
251 ekf.P[i][i] = PDiag[i];
256 void INSSetState(const float pos[3], const float vel[3], const float q[4], const float gyro_bias[3], const float accel_bias[3])
258 ekf.X[0] = pos[0];
259 ekf.X[1] = pos[1];
260 ekf.X[2] = pos[2];
261 ekf.X[3] = vel[0];
262 ekf.X[4] = vel[1];
263 ekf.X[5] = vel[2];
264 ekf.X[6] = q[0];
265 ekf.X[7] = q[1];
266 ekf.X[8] = q[2];
267 ekf.X[9] = q[3];
268 ekf.X[10] = gyro_bias[0];
269 ekf.X[11] = gyro_bias[1];
270 ekf.X[12] = gyro_bias[2];
271 ekf.X[13] = accel_bias[2];
274 void INSPosVelReset(const float pos[3], const float vel[3])
276 for (int i = 0; i < 6; i++) {
277 for (int j = i; j < NUMX; j++) {
278 ekf.P[i][j] = 0.0f; // zero the first 6 rows and columns
279 ekf.P[j][i] = 0.0f;
283 ekf.P[0][0] = ekf.P[1][1] = ekf.P[2][2] = 25.0f; // initial position variance (m^2)
284 ekf.P[3][3] = ekf.P[4][4] = ekf.P[5][5] = 5.0f; // initial velocity variance (m/s)^2
286 ekf.X[0] = pos[0];
287 ekf.X[1] = pos[1];
288 ekf.X[2] = pos[2];
289 ekf.X[3] = vel[0];
290 ekf.X[4] = vel[1];
291 ekf.X[5] = vel[2];
293 void INSSetPosVelVar(const float PosVar[3], const float VelVar[3])
295 ekf.R[0] = PosVar[0];
296 ekf.R[1] = PosVar[1];
297 ekf.R[2] = PosVar[2];
298 ekf.R[3] = VelVar[0];
299 ekf.R[4] = VelVar[1];
300 ekf.R[5] = VelVar[2]; // Don't change vertical velocity, not measured
303 void INSSetGyroBias(const float gyro_bias[3])
305 ekf.X[10] = gyro_bias[0];
306 ekf.X[11] = gyro_bias[1];
307 ekf.X[12] = gyro_bias[2];
310 void INSSetAccelBias(const float accel_bias[3])
312 ekf.X[13] = accel_bias[2];
315 void INSSetAccelVar(const float accel_var[3])
317 ekf.Q[3] = accel_var[0];
318 ekf.Q[4] = accel_var[1];
319 ekf.Q[5] = accel_var[2];
322 void INSSetGyroVar(const float gyro_var[3])
324 ekf.Q[0] = gyro_var[0];
325 ekf.Q[1] = gyro_var[1];
326 ekf.Q[2] = gyro_var[2];
329 void INSSetGyroBiasVar(const float gyro_bias_var[3])
331 ekf.Q[6] = gyro_bias_var[0];
332 ekf.Q[7] = gyro_bias_var[1];
333 ekf.Q[8] = gyro_bias_var[2];
336 void INSSetMagVar(const float scaled_mag_var[3])
338 ekf.R[6] = scaled_mag_var[0];
339 ekf.R[7] = scaled_mag_var[1];
340 ekf.R[8] = scaled_mag_var[2];
343 void INSSetBaroVar(const float baro_var)
345 ekf.R[9] = baro_var;
348 void INSSetMagNorth(const float B[3])
350 ekf.Be[0] = B[0];
351 ekf.Be[1] = B[1];
352 ekf.Be[2] = B[2];
355 void INSLimitBias()
357 // The Z accel bias should never wander too much. This helps ensure the filter
358 // remains stable.
359 if (ekf.X[13] > 0.1f) {
360 ekf.X[13] = 0.1f;
361 } else if (ekf.X[13] < -0.1f) {
362 ekf.X[13] = -0.1f;
365 // Make sure no gyro bias gets to more than 10 deg / s. This should be more than
366 // enough for well behaving sensors.
367 const float GYRO_BIAS_LIMIT = DEG2RAD(10);
368 for (int i = 10; i < 13; i++) {
369 if (ekf.X[i] < -GYRO_BIAS_LIMIT) {
370 ekf.X[i] = -GYRO_BIAS_LIMIT;
371 } else if (ekf.X[i] > GYRO_BIAS_LIMIT) {
372 ekf.X[i] = GYRO_BIAS_LIMIT;
377 void INSStatePrediction(const float gyro_data[3], const float accel_data[3], float dT)
379 float U[6];
380 float invqmag;
382 // rate gyro inputs in units of rad/s
383 U[0] = gyro_data[0];
384 U[1] = gyro_data[1];
385 U[2] = gyro_data[2];
387 // accelerometer inputs in units of m/s
388 U[3] = accel_data[0];
389 U[4] = accel_data[1];
390 U[5] = accel_data[2];
392 // EKF prediction step
393 LinearizeFG(ekf.X, U, ekf.F, ekf.G);
394 RungeKutta(ekf.X, U, dT);
395 invqmag = invsqrtf(ekf.X[6] * ekf.X[6] + ekf.X[7] * ekf.X[7] + ekf.X[8] * ekf.X[8] + ekf.X[9] * ekf.X[9]);
396 ekf.X[6] *= invqmag;
397 ekf.X[7] *= invqmag;
398 ekf.X[8] *= invqmag;
399 ekf.X[9] *= invqmag;
401 // Update Nav solution structure
402 Nav.Pos[0] = ekf.X[0];
403 Nav.Pos[1] = ekf.X[1];
404 Nav.Pos[2] = ekf.X[2];
405 Nav.Vel[0] = ekf.X[3];
406 Nav.Vel[1] = ekf.X[4];
407 Nav.Vel[2] = ekf.X[5];
408 Nav.q[0] = ekf.X[6];
409 Nav.q[1] = ekf.X[7];
410 Nav.q[2] = ekf.X[8];
411 Nav.q[3] = ekf.X[9];
412 Nav.gyro_bias[0] = ekf.X[10];
413 Nav.gyro_bias[1] = ekf.X[11];
414 Nav.gyro_bias[2] = ekf.X[12];
415 Nav.accel_bias[0] = 0.0f;
416 Nav.accel_bias[1] = 0.0f;
417 Nav.accel_bias[2] = ekf.X[13];
420 void INSCovariancePrediction(float dT)
422 CovariancePrediction(ekf.F, ekf.G, ekf.Q, dT, ekf.P);
425 void INSCorrection(const float mag_data[3], const float Pos[3], const float Vel[3],
426 const float BaroAlt, uint16_t SensorsUsed)
428 float Z[10], Y[10];
429 float invqmag;
431 // GPS Position in meters and in local NED frame
432 Z[0] = Pos[0];
433 Z[1] = Pos[1];
434 Z[2] = Pos[2];
436 // GPS Velocity in meters and in local NED frame
437 Z[3] = Vel[0];
438 Z[4] = Vel[1];
439 Z[5] = Vel[2];
441 if (SensorsUsed & MAG_SENSORS) {
442 // magnetometer data in any units (use unit vector) and in body frame
443 float Rbe_a[3][3];
444 float q0 = ekf.X[6];
445 float q1 = ekf.X[7];
446 float q2 = ekf.X[8];
447 float q3 = ekf.X[9];
448 float k1 = 1.0f / sqrtf(powf(q0 * q1 * 2.0f + q2 * q3 * 2.0f, 2.0f) + powf(q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3, 2.0f));
449 float k2 = sqrtf(-powf(q0 * q2 * 2.0f - q1 * q3 * 2.0f, 2.0f) + 1.0f);
451 Rbe_a[0][0] = k2;
452 Rbe_a[0][1] = 0.0f;
453 Rbe_a[0][2] = q0 * q2 * -2.0f + q1 * q3 * 2.0f;
454 Rbe_a[1][0] = k1 * (q0 * q1 * 2.0f + q2 * q3 * 2.0f) * (q0 * q2 * 2.0f - q1 * q3 * 2.0f);
455 Rbe_a[1][1] = k1 * (q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3);
456 Rbe_a[1][2] = k1 * sqrtf(-powf(q0 * q2 * 2.0f - q1 * q3 * 2.0f, 2.0f) + 1.0f) * (q0 * q1 * 2.0f + q2 * q3 * 2.0f);
457 Rbe_a[2][0] = k1 * (q0 * q2 * 2.0f - q1 * q3 * 2.0f) * (q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3);
458 Rbe_a[2][1] = -k1 * (q0 * q1 * 2.0f + q2 * q3 * 2.0f);
459 Rbe_a[2][2] = k1 * k2 * (q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3);
461 Z[6] = Rbe_a[0][0] * mag_data[0] + Rbe_a[1][0] * mag_data[1] + Rbe_a[2][0] * mag_data[2];
462 Z[7] = Rbe_a[0][1] * mag_data[0] + Rbe_a[1][1] * mag_data[1] + Rbe_a[2][1] * mag_data[2];
463 Z[8] = Rbe_a[0][2] * mag_data[0] + Rbe_a[1][2] * mag_data[1] + Rbe_a[2][2] * mag_data[2];
464 if (!(IS_REAL(Z[6]) && IS_REAL(Z[7]) && IS_REAL(Z[8]))) {
465 SensorsUsed = SensorsUsed & ~MAG_SENSORS;
469 // barometric altimeter in meters and in local NED frame
470 Z[9] = BaroAlt;
472 // EKF correction step
473 LinearizeH(ekf.X, ekf.Be, ekf.H);
474 MeasurementEq(ekf.X, ekf.Be, Y);
475 SerialUpdate(ekf.H, ekf.R, Z, Y, ekf.P, ekf.X, SensorsUsed);
476 invqmag = invsqrtf(ekf.X[6] * ekf.X[6] + ekf.X[7] * ekf.X[7] + ekf.X[8] * ekf.X[8] + ekf.X[9] * ekf.X[9]);
477 ekf.X[6] *= invqmag;
478 ekf.X[7] *= invqmag;
479 ekf.X[8] *= invqmag;
480 ekf.X[9] *= invqmag;
482 INSLimitBias();
485 // ************* CovariancePrediction *************
486 // Does the prediction step of the Kalman filter for the covariance matrix
487 // Output, Pnew, overwrites P, the input covariance
488 // Pnew = (I+F*T)*P*(I+F*T)' + T^2*G*Q*G'
489 // Q is the discrete time covariance of process noise
490 // Q is vector of the diagonal for a square matrix with
491 // dimensions equal to the number of disturbance noise variables
492 // The General Method is very inefficient,not taking advantage of the sparse F and G
493 // The first Method is very specific to this implementation
494 // ************************************************
496 void CovariancePrediction(float F[NUMX][NUMX], float G[NUMX][NUMW],
497 float Q[NUMW], float dT, float P[NUMX][NUMX])
499 // Pnew = (I+F*T)*P*(I+F*T)' + (T^2)*G*Q*G' = (T^2)[(P/T + F*P)*(I/T + F') + G*Q*G')]
501 const float dT1 = 1.0f / dT; // multiplication is faster than division on fpu.
502 const float dTsq = dT * dT;
504 float Dummy[NUMX][NUMX];
505 int8_t i;
506 int8_t k;
508 for (i = 0; i < NUMX; i++) { // Calculate Dummy = (P/T +F*P)
509 float *Firow = F[i];
510 float *Pirow = P[i];
511 float *Dirow = Dummy[i];
512 const int8_t Fistart = FrowMin[i];
513 const int8_t Fiend = FrowMax[i];
514 int8_t j;
516 for (j = 0; j < NUMX; j++) {
517 Dirow[j] = Pirow[j] * dT1; // Dummy = P / T ...
519 for (k = Fistart; k <= Fiend; k++) {
520 for (j = 0; j < NUMX; j++) {
521 Dirow[j] += Firow[k] * P[k][j]; // [] + F * P
525 for (i = 0; i < NUMX; i++) { // Calculate Pnew = (T^2) [Dummy/T + Dummy*F' + G*Qw*G']
526 float *Dirow = Dummy[i];
527 float *Girow = G[i];
528 float *Pirow = P[i];
529 const int8_t Gistart = GrowMin[i];
530 const int8_t Giend = GrowMax[i];
531 int8_t j;
534 for (j = i; j < NUMX; j++) { // Use symmetry, ie only find upper triangular
535 float Ptmp = Dirow[j] * dT1; // Pnew = Dummy / T ...
537 const float *Fjrow = F[j];
538 int8_t Fjstart = FrowMin[j];
539 int8_t Fjend = FrowMax[j];
540 k = Fjstart;
542 while (k <= Fjend - 3) {
543 Ptmp += Dirow[k] * Fjrow[k]; // [] + Dummy*F' ...
544 Ptmp += Dirow[k + 1] * Fjrow[k + 1];
545 Ptmp += Dirow[k + 2] * Fjrow[k + 2];
546 Ptmp += Dirow[k + 3] * Fjrow[k + 3];
547 k += 4;
549 while (k <= Fjend) {
550 Ptmp += Dirow[k] * Fjrow[k];
551 k++;
554 float *Gjrow = G[j];
555 const int8_t Gjstart = MAX(Gistart, GrowMin[j]);
556 const int8_t Gjend = MIN(Giend, GrowMax[j]);
557 k = Gjstart;
558 while (k <= Gjend - 2) {
559 Ptmp += Q[k] * Girow[k] * Gjrow[k]; // [] + G*Q*G' ...
560 Ptmp += Q[k + 1] * Girow[k + 1] * Gjrow[k + 1];
561 Ptmp += Q[k + 2] * Girow[k + 2] * Gjrow[k + 2];
562 k += 3;
564 if (k <= Gjend) {
565 Ptmp += Q[k] * Girow[k] * Gjrow[k];
566 if (k <= Gjend - 1) {
567 Ptmp += Q[k + 1] * Girow[k + 1] * Gjrow[k + 1];
571 P[j][i] = Pirow[j] = Ptmp * dTsq; // [] * (T^2)
576 // ************* SerialUpdate *******************
577 // Does the update step of the Kalman filter for the covariance and estimate
578 // Outputs are Xnew & Pnew, and are written over P and X
579 // Z is actual measurement, Y is predicted measurement
580 // Xnew = X + K*(Z-Y), Pnew=(I-K*H)*P,
581 // where K=P*H'*inv[H*P*H'+R]
582 // NOTE the algorithm assumes R (measurement covariance matrix) is diagonal
583 // i.e. the measurment noises are uncorrelated.
584 // It therefore uses a serial update that requires no matrix inversion by
585 // processing the measurements one at a time.
586 // Algorithm - see Grewal and Andrews, "Kalman Filtering,2nd Ed" p.121 & p.253
587 // - or see Simon, "Optimal State Estimation," 1st Ed, p.150
588 // The SensorsUsed variable is a bitwise mask indicating which sensors
589 // should be used in the update.
590 // ************************************************
591 void SerialUpdate(float H[NUMV][NUMX], float R[NUMV], float Z[NUMV],
592 float Y[NUMV], float P[NUMX][NUMX], float X[NUMX],
593 uint16_t SensorsUsed)
595 float HP[NUMX], HPHR, Error;
596 uint8_t i, j, k, m;
597 float Km[NUMX];
599 // Iterate through all the possible measurements and apply the
600 // appropriate corrections
601 for (m = 0; m < NUMV; m++) {
602 if (SensorsUsed & (0x01 << m)) { // use this sensor for update
603 for (j = 0; j < NUMX; j++) { // Find Hp = H*P
604 HP[j] = 0;
607 for (k = HrowMin[m]; k <= HrowMax[m]; k++) {
608 for (j = 0; j < NUMX; j++) { // Find Hp = H*P
609 HP[j] += H[m][k] * P[k][j];
612 HPHR = R[m]; // Find HPHR = H*P*H' + R
613 for (k = HrowMin[m]; k <= HrowMax[m]; k++) {
614 HPHR += HP[k] * H[m][k];
616 float invHPHR = 1.0f / HPHR;
617 for (k = 0; k < NUMX; k++) {
618 Km[k] = HP[k] * invHPHR; // find K = HP/HPHR
620 for (i = 0; i < NUMX; i++) { // Find P(m)= P(m-1) + K*HP
621 for (j = i; j < NUMX; j++) {
622 P[i][j] = P[j][i] = P[i][j] - Km[i] * HP[j];
626 Error = Z[m] - Y[m];
627 for (i = 0; i < NUMX; i++) { // Find X(m)= X(m-1) + K*Error
628 X[i] = X[i] + Km[i] * Error;
634 // ************* RungeKutta **********************
635 // Does a 4th order Runge Kutta numerical integration step
636 // Output, Xnew, is written over X
637 // NOTE the algorithm assumes time invariant state equations and
638 // constant inputs over integration step
639 // ************************************************
641 void RungeKutta(float X[NUMX], float U[NUMU], float dT)
643 const float dT2 = dT / 2.0f;
644 float K1[NUMX], K2[NUMX], K3[NUMX], K4[NUMX], Xlast[NUMX];
645 uint8_t i;
647 for (i = 0; i < NUMX; i++) {
648 Xlast[i] = X[i]; // make a working copy
650 StateEq(X, U, K1); // k1 = f(x,u)
651 for (i = 0; i < NUMX; i++) {
652 X[i] = Xlast[i] + dT2 * K1[i];
654 StateEq(X, U, K2); // k2 = f(x+0.5*dT*k1,u)
655 for (i = 0; i < NUMX; i++) {
656 X[i] = Xlast[i] + dT2 * K2[i];
658 StateEq(X, U, K3); // k3 = f(x+0.5*dT*k2,u)
659 for (i = 0; i < NUMX; i++) {
660 X[i] = Xlast[i] + dT * K3[i];
662 StateEq(X, U, K4); // k4 = f(x+dT*k3,u)
664 // Xnew = X + dT*(k1+2*k2+2*k3+k4)/6
665 for (i = 0; i < NUMX; i++) {
666 X[i] =
667 Xlast[i] + dT * (K1[i] + 2.0f * K2[i] + 2.0f * K3[i] +
668 K4[i]) * (1.0f / 6.0f);
672 // ************* Model Specific Stuff ***************************
673 // *** StateEq, MeasurementEq, LinerizeFG, and LinearizeH ********
675 // State Variables = [Pos Vel Quaternion GyroBias AccelBias]
676 // Deterministic Inputs = [AngularVel Accel]
677 // Disturbance Noise = [GyroNoise AccelNoise GyroRandomWalkNoise AccelRandomWalkNoise]
679 // Measurement Variables = [Pos Vel BodyFrameMagField Altimeter]
680 // Inputs to Measurement = [EarthFrameMagField]
682 // Notes: Pos and Vel in earth frame
683 // AngularVel and Accel in body frame
684 // MagFields are unit vectors
685 // Xdot is output of StateEq()
686 // F and G are outputs of LinearizeFG(), all elements not set should be zero
687 // y is output of OutputEq()
688 // H is output of LinearizeH(), all elements not set should be zero
689 // ************************************************
691 void StateEq(float X[NUMX], float U[NUMU], float Xdot[NUMX])
693 const float wx = U[0] - X[10];
694 const float wy = U[1] - X[11];
695 const float wz = U[2] - X[12]; // subtract the biases on gyros
696 const float ax = U[3];
697 const float ay = U[4];
698 const float az = U[5] - X[13]; // subtract the biases on accels
699 const float q0 = X[6];
700 const float q1 = X[7];
701 const float q2 = X[8];
702 const float q3 = X[9];
704 // Pdot = V
705 Xdot[0] = X[3];
706 Xdot[1] = X[4];
707 Xdot[2] = X[5];
709 // Vdot = Reb*a
710 Xdot[3] =
711 (q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3) * ax + 2.0f * (q1 * q2 -
712 q0 * q3) *
713 ay + 2.0f * (q1 * q3 + q0 * q2) * az;
714 Xdot[4] =
715 2.0f * (q1 * q2 + q0 * q3) * ax + (q0 * q0 - q1 * q1 + q2 * q2 -
716 q3 * q3) * ay + 2.0f * (q2 * q3 -
717 q0 * q1) *
719 Xdot[5] =
720 2.0f * (q1 * q3 - q0 * q2) * ax + 2.0f * (q2 * q3 + q0 * q1) * ay +
721 (q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3) * az + PIOS_CONST_MKS_GRAV_ACCEL_F;
723 // qdot = Q*w
724 Xdot[6] = (-q1 * wx - q2 * wy - q3 * wz) / 2.0f;
725 Xdot[7] = (q0 * wx - q3 * wy + q2 * wz) / 2.0f;
726 Xdot[8] = (q3 * wx + q0 * wy - q1 * wz) / 2.0f;
727 Xdot[9] = (-q2 * wx + q1 * wy + q0 * wz) / 2.0f;
729 // best guess is that bias stays constant
730 Xdot[10] = Xdot[11] = Xdot[12] = 0;
732 // For accels to make sure things stay stable, assume bias always walks weakly
733 // towards zero for the horizontal axis. This prevents drifting around an
734 // unobservable manifold of possible attitudes and gyro biases. The z-axis
735 // we assume no drift because this is the one we want to estimate most accurately.
736 Xdot[13] = 0.0f;
740 * Linearize the state equations around the current state estimate.
741 * @param[in] X the current state estimate
742 * @param[in] U the control inputs
743 * @param[out] F the linearized natural dynamics
744 * @param[out] G the linearized influence of disturbance model
746 * so the prediction of the next state is
747 * Xdot = F * X + G * U
748 * where X is the current state and U is the current input
750 * For reference the state order (in F) is pos, vel, attitude, gyro bias, accel bias
751 * and the input order is gyro, bias
753 void LinearizeFG(float X[NUMX], float U[NUMU], float F[NUMX][NUMX],
754 float G[NUMX][NUMW])
756 const float wx = U[0] - X[10];
757 const float wy = U[1] - X[11];
758 const float wz = U[2] - X[12]; // subtract the biases on gyros
759 const float ax = U[3];
760 const float ay = U[4];
761 const float az = U[5] - X[13]; // subtract the biases on accels
762 const float q0 = X[6];
763 const float q1 = X[7];
764 const float q2 = X[8];
765 const float q3 = X[9];
767 // Pdot = V
768 F[0][3] = F[1][4] = F[2][5] = 1.0f;
770 // dVdot/dq
771 F[3][6] = 2.0f * (q0 * ax - q3 * ay + q2 * az);
772 F[3][7] = 2.0f * (q1 * ax + q2 * ay + q3 * az);
773 F[3][8] = 2.0f * (-q2 * ax + q1 * ay + q0 * az);
774 F[3][9] = 2.0f * (-q3 * ax - q0 * ay + q1 * az);
775 F[4][6] = 2.0f * (q3 * ax + q0 * ay - q1 * az);
776 F[4][7] = 2.0f * (q2 * ax - q1 * ay - q0 * az);
777 F[4][8] = 2.0f * (q1 * ax + q2 * ay + q3 * az);
778 F[4][9] = 2.0f * (q0 * ax - q3 * ay + q2 * az);
779 F[5][6] = 2.0f * (-q2 * ax + q1 * ay + q0 * az);
780 F[5][7] = 2.0f * (q3 * ax + q0 * ay - q1 * az);
781 F[5][8] = 2.0f * (-q0 * ax + q3 * ay - q2 * az);
782 F[5][9] = 2.0f * (q1 * ax + q2 * ay + q3 * az);
784 // dVdot/dabias & dVdot/dna - the equations for how the accel input and accel bias influence velocity are the same
785 F[3][13] = G[3][5] = -2.0f * (q1 * q3 + q0 * q2);
786 F[4][13] = G[4][5] = 2.0f * (-q2 * q3 + q0 * q1);
787 F[5][13] = G[5][5] = -q0 * q0 + q1 * q1 + q2 * q2 - q3 * q3;
789 // dqdot/dq
790 F[6][6] = 0;
791 F[6][7] = -wx / 2.0f;
792 F[6][8] = -wy / 2.0f;
793 F[6][9] = -wz / 2.0f;
794 F[7][6] = wx / 2.0f;
795 F[7][7] = 0;
796 F[7][8] = wz / 2.0f;
797 F[7][9] = -wy / 2.0f;
798 F[8][6] = wy / 2.0f;
799 F[8][7] = -wz / 2.0f;
800 F[8][8] = 0;
801 F[8][9] = wx / 2.0f;
802 F[9][6] = wz / 2.0f;
803 F[9][7] = wy / 2.0f;
804 F[9][8] = -wx / 2.0f;
805 F[9][9] = 0;
807 // dqdot/dwbias
808 F[6][10] = q1 / 2.0f;
809 F[6][11] = q2 / 2.0f;
810 F[6][12] = q3 / 2.0f;
811 F[7][10] = -q0 / 2.0f;
812 F[7][11] = q3 / 2.0f;
813 F[7][12] = -q2 / 2.0f;
814 F[8][10] = -q3 / 2.0f;
815 F[8][11] = -q0 / 2.0f;
816 F[8][12] = q1 / 2.0f;
817 F[9][10] = q2 / 2.0f;
818 F[9][11] = -q1 / 2.0f;
819 F[9][12] = -q0 / 2.0f;
821 // dVdot/dna
822 G[3][3] = -q0 * q0 - q1 * q1 + q2 * q2 + q3 * q3;
823 G[3][4] = 2 * (-q1 * q2 + q0 * q3);
824 // G[3][5] = -2 * (q1 * q3 + q0 * q2); // already assigned above
825 G[4][3] = -2 * (q1 * q2 + q0 * q3);
826 G[4][4] = -q0 * q0 + q1 * q1 - q2 * q2 + q3 * q3;
827 // G[4][5] = 2 * (-q2 * q3 + q0 * q1); // already assigned above
828 G[5][3] = 2 * (-q1 * q3 + q0 * q2);
829 G[5][4] = -2 * (q2 * q3 + q0 * q1);
830 // G[5][5] = -q0 * q0 + q1 * q1 + q2 * q2 - q3 * q3; // already assigned above
832 // dqdot/dnw
833 G[6][0] = q1 / 2.0f;
834 G[6][1] = q2 / 2.0f;
835 G[6][2] = q3 / 2.0f;
836 G[7][0] = -q0 / 2.0f;
837 G[7][1] = q3 / 2.0f;
838 G[7][2] = -q2 / 2.0f;
839 G[8][0] = -q3 / 2.0f;
840 G[8][1] = -q0 / 2.0f;
841 G[8][2] = q1 / 2.0f;
842 G[9][0] = q2 / 2.0f;
843 G[9][1] = -q1 / 2.0f;
844 G[9][2] = -q0 / 2.0f;
848 * Predicts the measurements from the current state. Note
849 * that this is very similar to @ref LinearizeH except this
850 * directly computes the outputs instead of a matrix that
851 * you transform the state by
853 void MeasurementEq(float X[NUMX], float Be[3], float Y[NUMV])
855 const float q0 = X[6];
856 const float q1 = X[7];
857 const float q2 = X[8];
858 const float q3 = X[9];
860 // first six outputs are P and V
861 Y[0] = X[0];
862 Y[1] = X[1];
863 Y[2] = X[2];
864 Y[3] = X[3];
865 Y[4] = X[4];
866 Y[5] = X[5];
868 // Rotate Be by only the yaw heading
869 const float a1 = 2 * q0 * q3 + 2 * q1 * q2;
870 const float a2 = q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3;
871 const float r = sqrtf(a1 * a1 + a2 * a2);
872 const float cP = a2 / r;
873 const float sP = a1 / r;
874 Y[6] = Be[0] * cP + Be[1] * sP;
875 Y[7] = -Be[0] * sP + Be[1] * cP;
876 Y[8] = 0; // don't care
878 // Alt = -Pz
879 Y[9] = X[2] * -1.0f;
883 * Linearize the measurement around the current state estiamte
884 * so the predicted measurements are
885 * Z = H * X
887 void LinearizeH(float X[NUMX], float Be[3], float H[NUMV][NUMX])
889 const float q0 = X[6];
890 const float q1 = X[7];
891 const float q2 = X[8];
892 const float q3 = X[9];
894 // dP/dP=I; (expect position to measure the position)
895 H[0][0] = H[1][1] = H[2][2] = 1.0f;
896 // dV/dV=I; (expect velocity to measure the velocity)
897 H[3][3] = H[4][4] = H[5][5] = 1.0f;
899 // dBb/dq (expected magnetometer readings in the horizontal plane)
900 // these equations were generated by Rhb(q)*Be which is the matrix that
901 // rotates the earth magnetic field into the horizontal plane, and then
902 // taking the partial derivative wrt each term in q. Maniuplated in
903 // matlab symbolic toolbox
904 const float Be_0 = Be[0];
905 const float Be_1 = Be[1];
906 const float a1 = q0 * q3 * 2.0f + q1 * q2 * 2.0f;
907 const float a1s = a1 * a1;
908 const float a2 = q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3;
909 const float a2s = a2 * a2;
910 const float a3 = 1.0f / powf(a1s + a2s, 3.0f / 2.0f) * (1.0f / 2.0f);
912 const float k1 = 1.0f / sqrtf(a1s + a2s);
913 const float k3 = a3 * a2;
914 const float k4 = a2 * 4.0f;
915 const float k5 = a1 * 4.0f;
916 const float k6 = a3 * a1;
918 H[6][6] = Be_0 * q0 * k1 * 2.0f + Be_1 * q3 * k1 * 2.0f - Be_0 * (q0 * k4 + q3 * k5) * k3 - Be_1 * (q0 * k4 + q3 * k5) * k6;
919 H[6][7] = Be_0 * q1 * k1 * 2.0f + Be_1 * q2 * k1 * 2.0f - Be_0 * (q1 * k4 + q2 * k5) * k3 - Be_1 * (q1 * k4 + q2 * k5) * k6;
920 H[6][8] = Be_0 * q2 * k1 * -2.0f + Be_1 * q1 * k1 * 2.0f + Be_0 * (q2 * k4 - q1 * k5) * k3 + Be_1 * (q2 * k4 - q1 * k5) * k6;
921 H[6][9] = Be_1 * q0 * k1 * 2.0f - Be_0 * q3 * k1 * 2.0f + Be_0 * (q3 * k4 - q0 * k5) * k3 + Be_1 * (q3 * k4 - q0 * k5) * k6;
922 H[7][6] = Be_1 * q0 * k1 * 2.0f - Be_0 * q3 * k1 * 2.0f - Be_1 * (q0 * k4 + q3 * k5) * k3 + Be_0 * (q0 * k4 + q3 * k5) * k6;
923 H[7][7] = Be_0 * q2 * k1 * -2.0f + Be_1 * q1 * k1 * 2.0f - Be_1 * (q1 * k4 + q2 * k5) * k3 + Be_0 * (q1 * k4 + q2 * k5) * k6;
924 H[7][8] = Be_0 * q1 * k1 * -2.0f - Be_1 * q2 * k1 * 2.0f + Be_1 * (q2 * k4 - q1 * k5) * k3 - Be_0 * (q2 * k4 - q1 * k5) * k6;
925 H[7][9] = Be_0 * q0 * k1 * -2.0f - Be_1 * q3 * k1 * 2.0f + Be_1 * (q3 * k4 - q0 * k5) * k3 - Be_0 * (q3 * k4 - q0 * k5) * k6;
926 H[8][6] = 0.0f;
927 H[8][7] = 0.0f;
928 H[8][9] = 0.0f;
930 // dAlt/dPz = -1 (expected baro readings)
931 H[9][2] = -1.0f;
935 * @}
936 * @}