/** ****************************************************************************** * @addtogroup AHRS * @{ * @addtogroup INSGPS * @{ * @brief INSGPS is a joint attitude and position estimation EKF * * @file insgps.c * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. * @brief An INS/GPS algorithm implemented with an EKF. * * @see The GNU Public License (GPL) Version 3 * *****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "insgps.h" #include #include // constants/macros/typdefs #define NUMX 13 // number of states, X is the state vector #define NUMW 9 // number of plant noise inputs, w is disturbance noise vector #define NUMV 10 // number of measurements, v is the measurement noise vector #define NUMU 6 // number of deterministic inputs, U is the input vector // Private functions void INSCorrection(float mag_data[3], float Pos[3], float Vel[3], float BaroAlt, uint16_t SensorsUsed); void CovariancePrediction(float F[NUMX][NUMX], float G[NUMX][NUMW], float Q[NUMW], float dT, float P[NUMX][NUMX]); void SerialUpdate(float H[NUMV][NUMX], float R[NUMV], float Z[NUMV], float Y[NUMV], float P[NUMX][NUMX], float X[NUMX], uint16_t SensorsUsed); void RungeKutta(float X[NUMX],float U[NUMU], float dT); void StateEq(float X[NUMX],float U[NUMU],float Xdot[NUMX]); void LinearizeFG(float X[NUMX],float U[NUMU], float F[NUMX][NUMX], float G[NUMX][NUMW]); void MeasurementEq(float X[NUMX], float Be[3], float Y[NUMV]); void LinearizeH(float X[NUMX], float Be[3], float H[NUMV][NUMX]); // Private variables float F[NUMX][NUMX], G[NUMX][NUMW], H[NUMV][NUMX]; // linearized system matrices // global to init to zero and maintain zero elements float Be[3]; // local magnetic unit vector in NED frame float P[NUMX][NUMX], X[NUMX]; // covariance matrix and state vector float Q[NUMW], R[NUMV]; // input noise and measurement noise variances // ************* Exposed Functions **************** // ************************************************* void INSGPSInit() //pretty much just a place holder for now { Be[0]=1; Be[1]=0; Be[2]=0; // local magnetic unit vector P[0][0]=P[1][1]=P[2][2]=25; // initial position variance (m^2) P[3][3]=P[4][4]=P[5][5]=5; // initial velocity variance (m/s)^2 P[6][6]=P[7][7]=P[8][8]=P[9][9]=1e-5; // initial quaternion variance P[10][10]=P[11][11]=P[12][12]=1e-5; // initial gyro bias variance (rad/s)^2 X[0]=X[1]=X[2]=X[3]=X[4]=X[5]=0; // initial pos and vel (m) X[6]=1; X[7]=X[8]=X[9]=0; // initial quaternion (level and North) (m/s) X[10]=X[11]=X[12]=0; // initial gyro bias (rad/s) Q[0]=Q[1]=Q[2]=50e-8; // gyro noise variance (rad/s)^2 Q[3]=Q[4]=Q[5]=0.01; // accelerometer noise variance (m/s^2)^2 Q[6]=Q[7]=Q[8]=2e-7; // gyro bias random walk variance (rad/s^2)^2 R[0]=R[1]=0.004; // High freq GPS horizontal position noise variance (m^2) R[2]=0.036; // High freq GPS vertical position noise variance (m^2) R[3]=R[4]=0.004; // High freq GPS horizontal velocity noise variance (m/s)^2 R[5]=100; // High freq GPS vertical velocity noise variance (m/s)^2 R[6]=R[7]=R[8]=0.005; // magnetometer unit vector noise variance R[9]=1; // High freq altimeter noise variance (m^2) } void INSSetPosVelVar(float PosVar) { R[0] = PosVar; R[1] = PosVar; R[2] = PosVar; R[3] = PosVar; R[4] = PosVar; // R[5] = PosVar; // Don't change vertical velocity, not measured } void INSSetGyroBias(float gyro_bias[3]) { X[10] = gyro_bias[0]; X[11] = gyro_bias[1]; X[12] = gyro_bias[2]; } void INSSetAccelVar(float accel_var[3]) { Q[3] = accel_var[0]; Q[4] = accel_var[1]; Q[5] = accel_var[2]; } void INSSetGyroVar(float gyro_var[3]) { Q[0] = gyro_var[0]; Q[1] = gyro_var[1]; Q[2] = gyro_var[2]; } void INSSetMagVar(float scaled_mag_var[3]) { R[6] = scaled_mag_var[0]; R[7] = scaled_mag_var[1]; R[8] = scaled_mag_var[2]; } void INSSetMagNorth(float B[3]) { Be[0] = B[0]; Be[1] = B[1]; Be[2] = B[2]; } void INSPrediction(float gyro_data[3], float accel_data[3], float dT) { float U[6]; float qmag; // rate gyro inputs in units of rad/s U[0]=gyro_data[0]; U[1]=gyro_data[1]; U[2]=gyro_data[2]; // accelerometer inputs in units of m/s U[3]=accel_data[0]; U[4]=accel_data[1]; U[5]=accel_data[2]; // EKF prediction step LinearizeFG(X,U,F,G); RungeKutta(X,U,dT); qmag=sqrt(X[6]*X[6] + X[7]*X[7] + X[8]*X[8] + X[9]*X[9]); X[6] /= qmag; X[7] /= qmag; X[8] /= qmag; X[9] /= qmag; CovariancePrediction(F,G,Q,dT,P); // Update Nav solution structure Nav.Pos[0] = X[0]; Nav.Pos[1] = X[1]; Nav.Pos[2] = X[2]; Nav.Vel[0] = X[3]; Nav.Vel[1] = X[4]; Nav.Vel[2] = X[5]; Nav.q[0] = X[6]; Nav.q[1] = X[7]; Nav.q[2] = X[8]; Nav.q[3] = X[9]; } float zeros[3] = {0,0,0}; void MagCorrection(float mag_data[3]) { INSCorrection(mag_data, zeros, zeros, zeros[0], MAG_SENSORS); } void FullCorrection(float mag_data[3], float Pos[3], float Vel[3], float BaroAlt) { INSCorrection(mag_data, Pos, Vel, BaroAlt, FULL_SENSORS); } void GpsMagCorrection(float mag_data[3], float Pos[3], float Vel[3]) { INSCorrection(mag_data, Pos, Vel, zeros[0], POS_SENSORS | HORIZ_SENSORS | MAG_SENSORS); } void VelBaroCorrection(float Vel[3], float BaroAlt) { INSCorrection(zeros, zeros, Vel, BaroAlt, HORIZ_SENSORS | VERT_SENSORS | BARO_SENSOR); } void INSCorrection(float mag_data[3], float Pos[3], float Vel[3], float BaroAlt, uint16_t SensorsUsed) { float Z[10], Y[10]; float Bmag, qmag; // GPS Position in meters and in local NED frame Z[0]=Pos[0]; Z[1]=Pos[1]; Z[2]=Pos[2]; // GPS Velocity in meters and in local NED frame Z[3]=Vel[0]; Z[4]=Vel[1]; Z[5]=Vel[2]; // magnetometer data in any units (use unit vector) and in body frame Bmag = sqrt(mag_data[0]*mag_data[0] + mag_data[1]*mag_data[1] + mag_data[2]*mag_data[2]); Z[6] = mag_data[0]/Bmag; Z[7] = mag_data[1]/Bmag; Z[8] = mag_data[2]/Bmag; // barometric altimeter in meters and in local NED frame Z[9] = BaroAlt; // EKF correction step LinearizeH(X,Be,H); MeasurementEq(X,Be,Y); SerialUpdate(H,R,Z,Y,P,X,SensorsUsed); qmag=sqrt(X[6]*X[6] + X[7]*X[7] + X[8]*X[8] + X[9]*X[9]); X[6] /= qmag; X[7] /= qmag; X[8] /= qmag; X[9] /= qmag; // Update Nav solution structure Nav.Pos[0] = X[0]; Nav.Pos[1] = X[1]; Nav.Pos[2] = X[2]; Nav.Vel[0] = X[3]; Nav.Vel[1] = X[4]; Nav.Vel[2] = X[5]; Nav.q[0] = X[6]; Nav.q[1] = X[7]; Nav.q[2] = X[8]; Nav.q[3] = X[9]; } // ************* CovariancePrediction ************* // Does the prediction step of the Kalman filter for the covariance matrix // Output, Pnew, overwrites P, the input covariance // Pnew = (I+F*T)*P*(I+F*T)' + T^2*G*Q*G' // Q is the discrete time covariance of process noise // Q is vector of the diagonal for a square matrix with // dimensions equal to the number of disturbance noise variables // Could be much more efficient using the sparse, block structure of F and G // ************************************************ void CovariancePrediction(float F[NUMX][NUMX], float G[NUMX][NUMW], float Q[NUMW], float dT, float P[NUMX][NUMX]){ float Dummy[NUMX][NUMX], dTsq; uint8_t i,j,k; // 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')] dTsq = dT*dT; for (i=0;i