// Fuse the SmallEKF velocity estimates - this enables alevel reference to be maintained during constant turns void SmallEKF::fuseVelocity(bool yawInit) { float R_OBS = 0.25f; float innovation[3]; float varInnov[3]; Vector3f angErrVec; uint8_t stateIndex; float K[9]; // Fuse measurements sequentially for (uint8_t obsIndex=0;obsIndex<=2;obsIndex++) { stateIndex = 3 + obsIndex; // Calculate the velocity measurement innovation using the SmallEKF estimate as the observation // if heading isn't aligned, use zero velocity (static assumption) if (yawInit) { Vector3f measVelNED; _main_ekf.getVelNED(measVelNED); innovation[obsIndex] = state.velocity[obsIndex] - measVelNED[obsIndex]; } else { innovation[obsIndex] = state.velocity[obsIndex]; } // Zero the attitude error states - they represent the incremental error so must be zero before corrections are applied state.angErr.zero(); // Calculate the innovation variance varInnov[obsIndex] = Cov[stateIndex][stateIndex] + R_OBS; // Calculate the Kalman gain and correct states, taking advantage of direct state observation for (uint8_t rowIndex=0;rowIndex<=8;rowIndex++) { K[rowIndex] = Cov[rowIndex][stateIndex]/varInnov[obsIndex]; states[rowIndex] -= K[rowIndex] * innovation[obsIndex]; } // Store tilt error estimate for external monitoring angErrVec = angErrVec + state.angErr; // the first 3 states represent the angular misalignment vector. This is // is used to correct the estimated quaternion // Convert the error rotation vector to its equivalent quaternion // truth = estimate + error float rotationMag = state.angErr.length(); if (rotationMag > 1e-6f) { Quaternion deltaQuat; float temp = sinf(0.5f*rotationMag) / rotationMag; deltaQuat[0] = cosf(0.5f*rotationMag); deltaQuat[1] = state.angErr.x*temp; deltaQuat[2] = state.angErr.y*temp; deltaQuat[3] = state.angErr.z*temp; // Update the quaternion states by rotating from the previous attitude through the error quaternion state.quat *= deltaQuat; // re-normalise the quaternion state.quat.normalize(); } // Update the covariance for (uint8_t rowIndex=0;rowIndex<=8;rowIndex++) { for (uint8_t colIndex=0;colIndex<=8;colIndex++) { Cov[rowIndex][colIndex] = Cov[rowIndex][colIndex] - K[rowIndex]*Cov[stateIndex][colIndex]; } } // force symmetry and constrain diagonals to be non-negative fixCovariance(); } // calculate tilt component of angle correction TiltCorrection = sqrtf(sq(angErrVec.x) + sq(angErrVec.y)); }
// Fuse compass measurements from autopilot void SmallEKF::fuseCompass() { float q0 = state.quat[0]; float q1 = state.quat[1]; float q2 = state.quat[2]; float q3 = state.quat[3]; float magX = magData.x; float magY = magData.y; float magZ = magData.z; const float R_MAG = 3e-2f; // Calculate observation Jacobian float t5695 = sq(q0); float t5696 = sq(q1); float t5697 = sq(q2); float t5698 = sq(q3); float t5699 = t5695+t5696-t5697-t5698; float t5702 = q0*q2*2.0f; float t5703 = q1*q3*2.0f; float t5704 = t5702+t5703; float t5705 = q0*q3*2.0f; float t5707 = q1*q2*2.0f; float t5706 = t5705-t5707; float t5708 = cosTheta*sinPsi; float t5709 = sinPhi*sinTheta*cosPsi; float t5710 = t5708+t5709; float t5711 = t5705+t5707; float t5712 = sinTheta*sinPsi; float t5730 = cosTheta*sinPhi*cosPsi; float t5713 = t5712-t5730; float t5714 = q0*q1*2.0f; float t5720 = q2*q3*2.0f; float t5715 = t5714-t5720; float t5716 = t5695-t5696+t5697-t5698; float t5717 = sinTheta*cosPsi; float t5718 = cosTheta*sinPhi*sinPsi; float t5719 = t5717+t5718; float t5721 = cosTheta*cosPsi; float t5735 = sinPhi*sinTheta*sinPsi; float t5722 = t5721-t5735; float t5724 = sinPhi*t5706; float t5725 = cosPhi*sinTheta*t5699; float t5726 = cosPhi*cosTheta*t5704; float t5727 = t5724+t5725-t5726; float t5728 = magZ*t5727; float t5729 = t5699*t5710; float t5731 = t5704*t5713; float t5732 = cosPhi*cosPsi*t5706; float t5733 = t5729+t5731-t5732; float t5734 = magY*t5733; float t5736 = t5699*t5722; float t5737 = t5704*t5719; float t5738 = cosPhi*sinPsi*t5706; float t5739 = t5736+t5737+t5738; float t5740 = magX*t5739; float t5741 = -t5728+t5734+t5740; float t5742 = 1.0f/t5741; float t5743 = sinPhi*t5716; float t5744 = cosPhi*cosTheta*t5715; float t5745 = cosPhi*sinTheta*t5711; float t5746 = -t5743+t5744+t5745; float t5747 = magZ*t5746; float t5748 = t5710*t5711; float t5749 = t5713*t5715; float t5750 = cosPhi*cosPsi*t5716; float t5751 = t5748-t5749+t5750; float t5752 = magY*t5751; float t5753 = t5715*t5719; float t5754 = t5711*t5722; float t5755 = cosPhi*sinPsi*t5716; float t5756 = t5753-t5754+t5755; float t5757 = magX*t5756; float t5758 = t5747-t5752+t5757; float t5759 = t5742*t5758; float t5723 = tan(t5759); float t5760 = sq(t5723); float t5761 = t5760+1.0f; float t5762 = 1.0f/sq(t5741); float H_MAG[3]; H_MAG[0] = -t5761*(t5742*(magZ*(sinPhi*t5715+cosPhi*cosTheta*t5716)+magY*(t5713*t5716+cosPhi*cosPsi*t5715)+magX*(t5716*t5719-cosPhi*sinPsi*t5715))-t5758*t5762*(magZ*(sinPhi*t5704+cosPhi*cosTheta*t5706)+magY*(t5706*t5713+cosPhi*cosPsi*t5704)+magX*(t5706*t5719-cosPhi*sinPsi*t5704))); H_MAG[1] = t5761*(t5742*(magZ*(cosPhi*cosTheta*t5711-cosPhi*sinTheta*t5715)+magY*(t5711*t5713+t5710*t5715)+magX*(t5711*t5719+t5715*t5722))+t5758*t5762*(magZ*(cosPhi*cosTheta*t5699+cosPhi*sinTheta*t5704)+magY*(t5699*t5713-t5704*t5710)+magX*(t5699*t5719-t5704*t5722))); H_MAG[2] = t5761*(t5742*(-magZ*(sinPhi*t5711+cosPhi*sinTheta*t5716)+magY*(t5710*t5716-cosPhi*cosPsi*t5711)+magX*(t5716*t5722+cosPhi*sinPsi*t5711))-t5758*t5762*(magZ*(sinPhi*t5699-cosPhi*sinTheta*t5706)+magY*(t5706*t5710+cosPhi*t5699*cosPsi)+magX*(t5706*t5722-cosPhi*t5699*sinPsi))); // Calculate innovation variance and Kalman gains, taking advantage of the fact that only the first 3 elements in H are non zero float PH[3]; float varInnov = R_MAG; for (uint8_t rowIndex=0;rowIndex<=2;rowIndex++) { PH[rowIndex] = 0.0f; for (uint8_t colIndex=0;colIndex<=2;colIndex++) { PH[rowIndex] += Cov[rowIndex][colIndex]*H_MAG[colIndex]; } varInnov += H_MAG[rowIndex]*PH[rowIndex]; } float K_MAG[9]; float varInnovInv = 1.0f / varInnov; for (uint8_t rowIndex=0;rowIndex<=8;rowIndex++) { K_MAG[rowIndex] = 0.0f; for (uint8_t colIndex=0;colIndex<=2;colIndex++) { K_MAG[rowIndex] += Cov[rowIndex][colIndex]*H_MAG[colIndex]; } K_MAG[rowIndex] *= varInnovInv; } // Calculate the innovation float innovation = calcMagHeadingInnov(); // limit the innovation so that initial corrections are not too large if (innovation > 0.5f) { innovation = 0.5f; } else if (innovation < -0.5f) { innovation = -0.5f; } // correct the state vector state.angErr.zero(); for (uint8_t i=0;i<=8;i++) { states[i] -= K_MAG[i] * innovation; } // the first 3 states represent the angular error vector where truth = estimate + error. This is is used to correct the estimated quaternion float rotationMag = state.angErr.length(); if (rotationMag > 1e-6f) { // Convert the error rotation vector to its equivalent quaternion Quaternion deltaQuat; float temp = sinf(0.5f*rotationMag) / rotationMag; deltaQuat[0] = cosf(0.5f*rotationMag); deltaQuat[1] = state.angErr.x*temp; deltaQuat[2] = state.angErr.y*temp; deltaQuat[3] = state.angErr.z*temp; // Bring the quaternion state estimate back to 'truth' by adding the error state.quat *= deltaQuat; // re-normalise the quaternion state.quat.normalize(); } // correct the covariance using P = P - K*H*P taking advantage of the fact that only the first 3 elements in H are non zero float HP[9]; for (uint8_t colIndex=0;colIndex<=8;colIndex++) { HP[colIndex] = 0.0f; for (uint8_t rowIndex=0;rowIndex<=2;rowIndex++) { HP[colIndex] += H_MAG[rowIndex]*Cov[rowIndex][colIndex]; } } for (uint8_t rowIndex=0;rowIndex<=8;rowIndex++) { for (uint8_t colIndex=0;colIndex<=8;colIndex++) { Cov[rowIndex][colIndex] -= K_MAG[rowIndex] * HP[colIndex]; } } // force symmetry and constrain diagonals to be non-negative fixCovariance(); }
// Fuse the SoloGimbalEKF velocity estimates - this enables alevel reference to be maintained during constant turns void SoloGimbalEKF::fuseVelocity() { if (!_ahrs.have_inertial_nav()) { return; } float R_OBS = 0.25f; float innovation[3]; float varInnov[3]; Vector3f angErrVec; uint8_t stateIndex; float K[9]; // Fuse measurements sequentially for (uint8_t obsIndex=0;obsIndex<=2;obsIndex++) { stateIndex = 3 + obsIndex; // Calculate the velocity measurement innovation using the SoloGimbalEKF estimate as the observation // if heading isn't aligned, use zero velocity (static assumption) if (YawAligned) { Vector3f measVelNED = Vector3f(0,0,0); nav_filter_status main_ekf_status; if (_ahrs.get_filter_status(main_ekf_status)) { if (main_ekf_status.flags.horiz_vel) { _ahrs.get_velocity_NED(measVelNED); } } innovation[obsIndex] = state.velocity[obsIndex] - measVelNED[obsIndex]; } else { innovation[obsIndex] = state.velocity[obsIndex]; } // Zero the attitude error states - they represent the incremental error so must be zero before corrections are applied state.angErr.zero(); // Calculate the innovation variance varInnov[obsIndex] = Cov[stateIndex][stateIndex] + R_OBS; // Calculate the Kalman gain and correct states, taking advantage of direct state observation for (uint8_t rowIndex=0;rowIndex<=8;rowIndex++) { K[rowIndex] = Cov[rowIndex][stateIndex]/varInnov[obsIndex]; states[rowIndex] -= K[rowIndex] * innovation[obsIndex]; } // Store tilt error estimate for external monitoring angErrVec = angErrVec + state.angErr; // the first 3 states represent the angular error vector where truth = estimate + error. This is is used to correct the estimated quaternion // Bring the quaternion state estimate back to 'truth' by adding the error state.quat.rotate(state.angErr); // re-normalise the quaternion state.quat.normalize(); // Update the covariance for (uint8_t rowIndex=0;rowIndex<=8;rowIndex++) { for (uint8_t colIndex=0;colIndex<=8;colIndex++) { Cov[rowIndex][colIndex] = Cov[rowIndex][colIndex] - K[rowIndex]*Cov[stateIndex][colIndex]; } } // force symmetry and constrain diagonals to be non-negative fixCovariance(); } // calculate tilt component of angle correction TiltCorrection = sqrtf(sq(angErrVec.x) + sq(angErrVec.y)); }