Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
attitude.c
00001 // =============================================================================================== 00002 // = UAVXArm Quadrocopter Controller = 00003 // = Copyright (c) 2008 by Prof. Greg Egan = 00004 // = Original V3.15 Copyright (c) 2007 Ing. Wolfgang Mahringer = 00005 // = http://code.google.com/p/uavp-mods/ = 00006 // =============================================================================================== 00007 00008 // This is part of UAVXArm. 00009 00010 // UAVXArm is free software: you can redistribute it and/or modify it under the terms of the GNU 00011 // General Public License as published by the Free Software Foundation, either version 3 of the 00012 // License, or (at your option) any later version. 00013 00014 // UAVXArm is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without 00015 // even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00016 // See the GNU General Public License for more details. 00017 00018 // You should have received a copy of the GNU General Public License along with this program. 00019 // If not, see http://www.gnu.org/licenses/ 00020 00021 #include "UAVXArm.h" 00022 00023 // Reference frame is positive X forward, Y left, Z down, Roll right, Pitch up, Yaw CW. 00024 // CAUTION: Because of the coordinate frame the LR Acc sense must be negated for roll compensation. 00025 00026 void AdaptiveYawLPFreq(void); 00027 void DoLegacyYawComp(uint8); 00028 void NormaliseAccelerations(void); 00029 void AttitudeTest(void); 00030 void InitAttitude(void); 00031 00032 real32 AccMagnitude; 00033 real32 EstAngle[3][MaxAttitudeScheme]; 00034 real32 EstRate[3][MaxAttitudeScheme]; 00035 real32 Correction[2]; 00036 real32 YawFilterLPFreq; 00037 real32 dT, dTOn2, dTR, dTmS; 00038 uint32 uSp; 00039 00040 uint8 AttitudeMethod = Wolferl; //Integrator, Wolferl MadgwickIMU PremerlaniDCM MadgwickAHRS, MultiWii; 00041 00042 void AdaptiveYawLPFreq(void) { // Filter LP freq is decreased with reduced yaw stick deflection 00043 00044 YawFilterLPFreq = ( MAX_YAW_FREQ*abs(DesiredYaw) / RC_NEUTRAL ); 00045 YawFilterLPFreq = Limit(YawFilterLPFreq, 0.5, MAX_YAW_FREQ); 00046 00047 } // AdaptiveYawFilterA 00048 00049 real32 HE; 00050 00051 void DoLegacyYawComp(uint8 S) { 00052 00053 #define COMPASS_MIDDLE 10 // yaw stick neutral dead zone 00054 #define DRIFT_COMP_YAW_RATE QUARTERPI // Radians/Sec 00055 00056 static int16 Temp; 00057 00058 // Yaw Angle here is meant to be interpreted as the Heading Error 00059 00060 Rate[Yaw] = Gyro[Yaw]; 00061 00062 Temp = DesiredYaw - Trim[Yaw]; 00063 if ( F.CompassValid ) // CW+ 00064 if ( abs(Temp) > COMPASS_MIDDLE ) { 00065 DesiredHeading = Heading; // acquire new heading 00066 Angle[Yaw] = 0.0; 00067 } else { 00068 HE = MinimumTurn(DesiredHeading - Heading); 00069 HE = Limit1(HE, SIXTHPI); // 30 deg limit 00070 HE = HE*K[CompassKp]; 00071 Rate[Yaw] = -Limit1(HE, DRIFT_COMP_YAW_RATE); 00072 } 00073 else { 00074 DesiredHeading = Heading; 00075 Angle[Yaw] = 0.0; 00076 } 00077 00078 Angle[Yaw] += Rate[Yaw]*dT; 00079 // Angle[Yaw] = Limit1(Angle[Yaw], K[YawIntLimit]); 00080 00081 } // DoLegacyYawComp 00082 00083 void NormaliseAccelerations(void) { 00084 00085 const real32 MIN_ACC_MAGNITUDE = 0.7; // below this the accelerometers are deemed unreliable - falling? 00086 00087 static real32 ReNorm; 00088 00089 AccMagnitude = sqrt(Sqr(Acc[BF]) + Sqr(Acc[LR]) + Sqr(Acc[UD])); 00090 F.AccMagnitudeOK = AccMagnitude > MIN_ACC_MAGNITUDE; 00091 if ( F.AccMagnitudeOK ) { 00092 ReNorm = 1.0 / AccMagnitude; 00093 Acc[BF] *= ReNorm; 00094 Acc[LR] *= ReNorm; 00095 Acc[UD] *= ReNorm; 00096 } else { 00097 Acc[LR] = Acc[BF] = 0.0; 00098 Acc[UD] = 1.0; 00099 } 00100 } // NormaliseAccelerations 00101 00102 void GetAttitude(void) { 00103 00104 static uint32 Now; 00105 static uint8 i; 00106 00107 if ( GyroType == IRSensors ) 00108 GetIRAttitude(); 00109 else { 00110 GetGyroRates(); 00111 GetAccelerations(); 00112 } 00113 00114 Now = uSClock(); 00115 dT = ( Now - uSp)*0.000001; 00116 dTOn2 = 0.5 * dT; 00117 dTR = 1.0 / dT; 00118 uSp = Now; 00119 00120 GetHeading(); // only updated every 50mS but read continuously anyway 00121 00122 if ( GyroType == IRSensors ) { 00123 00124 for ( i = 0; i < (uint8)2; i++ ) { 00125 Rate[i] = ( Angle[i] - Anglep[i] )*dT; 00126 Anglep[i] = Angle[i]; 00127 } 00128 00129 Rate[Yaw] = 0.0; // need Yaw gyro! 00130 00131 } else { 00132 DebugPin = true; 00133 00134 NormaliseAccelerations(); // rudimentary check for free fall etc 00135 00136 // Wolferl 00137 DoWolferl(); 00138 00139 #ifdef INC_ALL_SCHEMES 00140 00141 // Complementary 00142 DoCF(); 00143 00144 // Kalman 00145 DoKalman(); 00146 00147 //Premerlani DCM 00148 DoDCM(); 00149 00150 // MultiWii 00151 DoMultiWii(); 00152 00153 // Madgwick IMU 00154 // DoMadgwickIMU(Gyro[Roll], Gyro[Pitch], Gyro[Yaw], Acc[BF], -Acc[LR], -Acc[UD]); 00155 00156 //#define INC_IMU2 00157 00158 #ifdef INC_IMU2 00159 DoMadgwickIMU2(Gyro[Roll], Gyro[Pitch], Gyro[Yaw], Acc[BF], -Acc[LR], -Acc[UD]); 00160 #else 00161 Madgwick IMU April 30, 2010 Paper Version 00162 #endif 00163 // Madgwick AHRS BROKEN 00164 DoMadgwickAHRS(Gyro[Roll], Gyro[Pitch], Gyro[Yaw], Acc[BF], -Acc[LR], -Acc[UD], Mag[BF].V, Mag[LR].V, -Mag[UD].V); 00165 00166 // Integrator - REFERENCE/FALLBACK 00167 DoIntegrator(); 00168 00169 #endif // INC_ALL_SCHEMES 00170 00171 Angle[Roll] = EstAngle[Roll][AttitudeMethod]; 00172 Angle[Pitch] = EstAngle[Pitch][AttitudeMethod]; 00173 00174 DebugPin = false; 00175 } 00176 00177 F.NearLevel = Max(fabs(Angle[Roll]), fabs(Angle[Pitch])) < NAV_RTH_LOCKOUT; 00178 00179 } // GetAttitude 00180 00181 //____________________________________________________________________________________________ 00182 00183 // Integrator 00184 00185 void DoIntegrator(void) { 00186 00187 static uint8 g; 00188 00189 for ( g = 0; g < (uint8)3; g++ ) { 00190 EstRate[g][Integrator] = Gyro[g]; 00191 EstAngle[g][Integrator] += EstRate[g][Integrator]*dT; 00192 // EstAngle[g][Integrator] = DecayX(EstAngle[g][Integrator], 0.0001*dT); 00193 } 00194 00195 } // DoIntegrator 00196 00197 //____________________________________________________________________________________________ 00198 00199 // Original simple accelerometer compensation of gyros developed for UAVP by Wolfgang Mahringer 00200 // and adapted for UAVXArm 00201 00202 void DoWolferl(void) { // NO YAW ESTIMATE 00203 00204 const real32 WKp = 0.13; // 0.1 00205 00206 static real32 Grav[2], Dyn[2]; 00207 static real32 CompStep; 00208 00209 Rate[Roll] = Gyro[Roll]; 00210 Rate[Pitch] = Gyro[Pitch]; 00211 00212 if ( F.AccMagnitudeOK ) { 00213 00214 CompStep = WKp*dT; 00215 00216 // Roll 00217 00218 Grav[LR] = -sin(EstAngle[Roll][Wolferl]); // original used approximation for small angles 00219 Dyn[LR] = 0.0; //Rate[Roll]; // lateral acceleration due to rate - do later:). 00220 00221 Correction[LR] = -Acc[LR] + Grav[LR] + Dyn[LR]; // Acc is reversed 00222 Correction[LR] = Limit1(Correction[LR], CompStep); 00223 00224 EstAngle[Roll][Wolferl] += Rate[Roll]*dT; 00225 EstAngle[Roll][Wolferl] += Correction[LR]; 00226 00227 // Pitch 00228 00229 Grav[BF] = -sin(EstAngle[Pitch][Wolferl]); 00230 Dyn[BF] = 0.0; // Rate[Pitch]; 00231 00232 Correction[BF] = Acc[BF] + Grav[BF] + Dyn[BF]; 00233 Correction[BF] = Limit1(Correction[BF], CompStep); 00234 00235 EstAngle[Pitch][Wolferl] += Rate[Pitch]*dT; 00236 EstAngle[Pitch][Wolferl] += Correction[BF]; 00237 00238 } else { 00239 00240 EstAngle[Roll][Wolferl] += Rate[Roll]*dT; 00241 EstAngle[Pitch][Wolferl] += Rate[Pitch]*dT; 00242 00243 } 00244 00245 } // DoWolferl 00246 00247 //_________________________________________________________________________________ 00248 00249 // Complementary Filter originally authored by RoyLB 00250 // http://www.rcgroups.com/forums/showpost.php?p=12082524&postcount=1286 00251 00252 const real32 TauCF = 1.1; 00253 00254 real32 AngleCF[2] = {0,0}; 00255 real32 F0[2] = {0,0}; 00256 real32 F1[2] = {0,0}; 00257 real32 F2[2] = {0,0}; 00258 00259 real32 CF(uint8 a, real32 NewAngle, real32 NewRate) { 00260 00261 if ( F.AccMagnitudeOK ) { 00262 F0[a] = (NewAngle - AngleCF[a])*Sqr(TauCF); 00263 F2[a] += F0[a]*dT; 00264 F1[a] = F2[a] + (NewAngle - AngleCF[a])*2.0*TauCF + NewRate; 00265 AngleCF[a] = (F1[a]*dT) + AngleCF[a]; 00266 } else 00267 AngleCF[a] += NewRate*dT; 00268 00269 return ( AngleCF[a] ); // This is actually the current angle, but is stored for the next iteration 00270 } // CF 00271 00272 void DoCF(void) { // NO YAW ANGLE ESTIMATE 00273 00274 EstAngle[Roll][Complementary] = CF(Roll, asin(-Acc[LR]), Gyro[Roll]); 00275 EstAngle[Pitch][Complementary] = CF(Pitch, asin(-Acc[BF]), Gyro[Pitch]); // zzz minus??? 00276 EstRate[Roll][Complementary] = Gyro[Roll]; 00277 EstRate[Pitch][Complementary] = Gyro[Pitch]; 00278 00279 } // DoCF 00280 00281 //____________________________________________________________________________________________ 00282 00283 // The DCM formulation used here is due to W. Premerlani and P. Bizard in a paper entitled: 00284 // Direction Cosine Matrix IMU: Theory, Draft 17 June 2009. This paper draws upon the original 00285 // work by R. Mahony et al. - Thanks Rob! 00286 00287 // SEEMS TO BE A FAIRLY LARGE PHASE DELAY OF 2 SAMPLE INTERVALS 00288 00289 void DCMNormalise(void); 00290 void DCMDriftCorrection(void); 00291 void DCMMotionCompensation(void); 00292 void DCMUpdate(void); 00293 void DCMEulerAngles(void); 00294 00295 real32 RollPitchError[3] = {0,0,0}; 00296 real32 OmegaV[3] = {0,0,0}; // corrected gyro data 00297 real32 OmegaP[3] = {0,0,0}; // proportional correction 00298 real32 OmegaI[3] = {0,0,0}; // integral correction 00299 real32 Omega[3] = {0,0,0}; 00300 real32 DCM[3][3] = {{1,0,0 },{0,1,0} ,{0,0,1}}; 00301 real32 U[3][3] = {{0,1,2},{ 3,4,5} ,{6,7,8}}; 00302 real32 TempM[3][3] = {{0,0,0},{0,0,0},{ 0,0,0}}; 00303 real32 AccV[3]; 00304 00305 void DCMNormalise(void) { 00306 00307 static real32 Error = 0; 00308 static real32 Renorm = 0.0; 00309 static boolean Problem; 00310 static uint8 r; 00311 00312 Error = -VDot(&DCM[0][0], &DCM[1][0])*0.5; //eq.19 00313 00314 VScale(&TempM[0][0], &DCM[1][0], Error); //eq.19 00315 VScale(&TempM[1][0], &DCM[0][0], Error); //eq.19 00316 00317 VAdd(&TempM[0][0], &TempM[0][0], &DCM[0][0]); //eq.19 00318 VAdd(&TempM[1][0], &TempM[1][0], &DCM[1][0]); //eq.19 00319 00320 VCross(&TempM[2][0],&TempM[0][0], &TempM[1][0]); // c= a*b eq.20 00321 00322 Problem = false; 00323 for ( r = 0; r < (uint8)3; r++ ) { 00324 Renorm = VDot(&TempM[r][0], &TempM[r][0]); 00325 if ( (Renorm < 1.5625) && (Renorm > 0.64) ) 00326 Renorm = 0.5*(3.0 - Renorm); //eq.21 00327 else 00328 if ( (Renorm < 100.0) && (Renorm > 0.01) ) 00329 Renorm = 1.0 / sqrt( Renorm ); 00330 else 00331 Problem = true; 00332 00333 VScale(&DCM[r][0], &TempM[r][0], Renorm); 00334 } 00335 00336 if ( Problem ) { // Divergent - force to initial conditions and hope! 00337 DCM[0][0] = 1.0; 00338 DCM[0][1] = 0.0; 00339 DCM[0][2] = 0.0; 00340 DCM[1][0] = 0.0; 00341 DCM[1][1] = 1.0; 00342 DCM[1][2] = 0.0; 00343 DCM[2][0] = 0.0; 00344 DCM[2][1] = 0.0; 00345 DCM[2][2] = 1.0; 00346 } 00347 00348 } // DCMNormalise 00349 00350 void DCMMotionCompensation(void) { 00351 // compensation for rate of change of velocity LR/BF needs GPS velocity but 00352 // updates probably too slow? 00353 AccV[LR ] += 0.0; 00354 AccV[BF] += 0.0; 00355 } // DCMMotionCompensation 00356 00357 void DCMDriftCorrection(void) { 00358 00359 static real32 ScaledOmegaI[3]; 00360 00361 //DON'T USE #define USE_DCM_YAW_COMP 00362 #ifdef USE_DCM_YAW_COMP 00363 static real32 ScaledOmegaP[3]; 00364 static real32 YawError[3]; 00365 static real32 ErrorCourse; 00366 #endif // USE_DCM_YAW_COMP 00367 00368 VCross(&RollPitchError[0], &AccV[0], &DCM[2][0]); //adjust the reference ground 00369 VScale(&OmegaP[0], &RollPitchError[0], Kp_RollPitch); 00370 00371 VScale(&ScaledOmegaI[0], &RollPitchError[0], Ki_RollPitch); 00372 VAdd(&OmegaI[0], &OmegaI[0], &ScaledOmegaI[0]); 00373 00374 #ifdef USE_DCM_YAW_COMP 00375 // Yaw - drift correction based on compass/magnetometer heading 00376 HeadingCos = cos(Heading); 00377 HeadingSin = sin(Heading); 00378 ErrorCourse = ( U[0][0]*HeadingSin ) - ( U[1][0]*HeadingCos ); 00379 VScale(YawError, &U[2][0], ErrorCourse ); 00380 00381 VScale(&ScaledOmegaP[0], &YawError[0], Kp_Yaw ); 00382 VAdd(&OmegaP[0], &OmegaP[0], &ScaledOmegaP[0]); 00383 00384 VScale(&ScaledOmegaI[0], &YawError[0], Ki_Yaw ); 00385 VAdd(&OmegaI[0], &OmegaI[0], &ScaledOmegaI[0]); 00386 #endif // USE_DCM_YAW_COMP 00387 00388 } // DCMDriftCorrection 00389 00390 void DCMUpdate(void) { 00391 00392 static uint8 i, j, k; 00393 static real32 op[3]; 00394 00395 AccV[BF] = Acc[BF]; 00396 AccV[LR] = -Acc[LR]; 00397 AccV[UD] = -Acc[UD]; 00398 00399 VAdd(&Omega[0], &Gyro[0], &OmegaI[0]); 00400 VAdd(&OmegaV[0], &Omega[0], &OmegaP[0]); 00401 00402 // MotionCompensation(); 00403 00404 U[0][0] = 0.0; 00405 U[0][1] = -dT*OmegaV[2]; //-z 00406 U[0][2] = dT*OmegaV[1]; // y 00407 U[1][0] = dT*OmegaV[2]; // z 00408 U[1][1] = 0.0; 00409 U[1][2] = -dT*OmegaV[0]; //-x 00410 U[2][0] = -dT*OmegaV[1]; //-y 00411 U[2][1] = dT*OmegaV[0]; // x 00412 U[2][2] = 0.0; 00413 00414 for ( i = 0; i < (uint8)3; i++ ) 00415 for ( j = 0; j < (uint8)3; j++ ) { 00416 for ( k = 0; k < (uint8)3; k++ ) 00417 op[k] = DCM[i][k]*U[k][j]; 00418 00419 TempM[i][j] = op[0] + op[1] + op[2]; 00420 } 00421 00422 for ( i = 0; i < (uint8)3; i++ ) 00423 for (j = 0; j < (uint8)3; j++ ) 00424 DCM[i][j] += TempM[i][j]; 00425 00426 } // DCMUpdate 00427 00428 void DCMEulerAngles(void) { 00429 00430 static uint8 g; 00431 00432 for ( g = 0; g < (uint8)3; g++ ) 00433 Rate[g] = Gyro[g]; 00434 00435 EstAngle[Roll][PremerlaniDCM]= atan2(DCM[2][1], DCM[2][2]); 00436 EstAngle[Pitch][PremerlaniDCM] = -asin(DCM[2][0]); 00437 EstAngle[Yaw][PremerlaniDCM] = atan2(DCM[1][0], DCM[0][0]); 00438 00439 // Est. Rates ??? 00440 00441 } // DCMEulerAngles 00442 00443 void DoDCM(void) { 00444 DCMUpdate(); 00445 DCMNormalise(); 00446 DCMDriftCorrection(); 00447 DCMEulerAngles(); 00448 } // DoDCM 00449 00450 //___________________________________________________________________________________ 00451 00452 // IMU.c 00453 // S.O.H. Madgwick 00454 // 25th September 2010 00455 00456 // Description: 00457 00458 // Quaternion implementation of the 'DCM filter' [Mahony et al.]. 00459 00460 // Global variables 'q0', 'q1', 'q2', 'q3' are the quaternion elements representing the estimated 00461 // orientation. See my report for an overview of the use of quaternions in this application. 00462 00463 // User must call 'IMUupdate()' every sample period and parse calibrated gyroscope ('gx', 'gy', 'gz') 00464 // and accelerometer ('ax', 'ay', 'az') data. Gyroscope units are radians/second, accelerometer 00465 // units are irrelevant as the vector is normalised. 00466 00467 const real32 MKp = 2.0; // proportional gain governs rate of convergence to accelerometer/magnetometer 00468 const real32 MKi = 1.0; // integral gain governs rate of convergence of gyroscope biases // 0.005 00469 00470 real32 exInt = 0.0, eyInt = 0.0, ezInt = 0.0; // scaled integral error 00471 real32 q0 = 1.0, q1 = 0.0, q2 = 0.0, q3 = 0.0; // quaternion elements representing the estimated orientation 00472 00473 void DoMadgwickIMU(real32 gx, real32 gy, real32 gz, real32 ax, real32 ay, real32 az) { 00474 00475 static uint8 g; 00476 static real32 ReNorm; 00477 static real32 vx, vy, vz; 00478 static real32 ex, ey, ez; 00479 00480 if ( F.AccMagnitudeOK ) { 00481 00482 // estimated direction of gravity 00483 vx = 2.0*(q1*q3 - q0*q2); 00484 vy = 2.0*(q0*q1 + q2*q3); 00485 vz = Sqr(q0) - Sqr(q1) - Sqr(q2) + Sqr(q3); 00486 00487 // error is sum of cross product between reference direction of field and direction measured by sensor 00488 ex = (ay*vz - az*vy); 00489 ey = (az*vx - ax*vz); 00490 ez = (ax*vy - ay*vx); 00491 00492 // integral error scaled integral gain 00493 exInt += ex*MKi*dT; 00494 eyInt += ey*MKi*dT; 00495 ezInt += ez*MKi*dT; 00496 00497 // adjusted gyroscope measurements 00498 gx += MKp*ex + exInt; 00499 gy += MKp*ey + eyInt; 00500 gz += MKp*ez + ezInt; 00501 00502 // integrate quaternion rate and normalise 00503 q0 += (-q1*gx - q2*gy - q3*gz)*dTOn2; 00504 q1 += (q0*gx + q2*gz - q3*gy)*dTOn2; 00505 q2 += (q0*gy - q1*gz + q3*gx)*dTOn2; 00506 q3 += (q0*gz + q1*gy - q2*gx)*dTOn2; 00507 00508 // normalise quaternion 00509 ReNorm = 1.0 /sqrt(Sqr(q0) + Sqr(q1) + Sqr(q2) + Sqr(q3)); 00510 q0 *= ReNorm; 00511 q1 *= ReNorm; 00512 q2 *= ReNorm; 00513 q3 *= ReNorm; 00514 00515 MadgwickEulerAngles(MadgwickIMU); 00516 00517 } else 00518 for ( g = 0; g <(uint8)3; g++) { 00519 EstRate[g][MadgwickIMU] = Gyro[g]; 00520 EstAngle[g][MadgwickIMU] += EstRate[g][MadgwickIMU]*dT; 00521 } 00522 00523 } // DoMadgwickIMU 00524 00525 //_________________________________________________________________________________ 00526 00527 // IMU.c 00528 // S.O.H. Madgwick, 00529 // 'An Efficient Orientation Filter for Inertial and Inertial/Magnetic Sensor Arrays', 00530 // April 30, 2010 00531 00532 #ifdef INC_IMU2 00533 00534 boolean FirstIMU2 = true; 00535 real32 BetaIMU2 = 0.033; 00536 // const real32 BetaAHRS = 0.041; 00537 00538 //Quaternion orientation of earth frame relative to auxiliary frame. 00539 real32 AEq_1; 00540 real32 AEq_2; 00541 real32 AEq_3; 00542 real32 AEq_4; 00543 00544 //Estimated orientation quaternion elements with initial conditions. 00545 real32 SEq_1; 00546 real32 SEq_2; 00547 real32 SEq_3; 00548 real32 SEq_4; 00549 00550 void DoMadgwickIMU2(real32 w_x, real32 w_y, real32 w_z, real32 a_x, real32 a_y, real32 a_z) { 00551 00552 static uint8 g; 00553 00554 //Vector norm. 00555 static real32 Renorm; 00556 //Quaternion rate from gyroscope elements. 00557 static real32 SEqDot_omega_1, SEqDot_omega_2, SEqDot_omega_3, SEqDot_omega_4; 00558 //Objective function elements. 00559 static real32 f_1, f_2, f_3; 00560 //Objective function Jacobian elements. 00561 static real32 J_11or24, J_12or23, J_13or22, J_14or21, J_32, J_33; 00562 //Objective function gradient elements. 00563 static real32 SEqHatDot_1, SEqHatDot_2, SEqHatDot_3, SEqHatDot_4; 00564 00565 //Auxiliary variables to avoid reapeated calcualtions. 00566 static real32 halfSEq_1, halfSEq_2, halfSEq_3, halfSEq_4; 00567 static real32 twoSEq_1, twoSEq_2, twoSEq_3; 00568 00569 if ( F.AccMagnitudeOK ) { 00570 00571 halfSEq_1 = 0.5*SEq_1; 00572 halfSEq_2 = 0.5*SEq_2; 00573 halfSEq_3 = 0.5*SEq_3; 00574 halfSEq_4 = 0.5*SEq_4; 00575 twoSEq_1 = 2.0*SEq_1; 00576 twoSEq_2 = 2.0*SEq_2; 00577 twoSEq_3 = 2.0*SEq_3; 00578 00579 //Compute the quaternion rate measured by gyroscopes. 00580 SEqDot_omega_1 = -halfSEq_2*w_x - halfSEq_3*w_y - halfSEq_4*w_z; 00581 SEqDot_omega_2 = halfSEq_1*w_x + halfSEq_3*w_z - halfSEq_4*w_y; 00582 SEqDot_omega_3 = halfSEq_1*w_y - halfSEq_2*w_z + halfSEq_4*w_x; 00583 SEqDot_omega_4 = halfSEq_1*w_z + halfSEq_2*w_y - halfSEq_3*w_x; 00584 00585 /* 00586 //Normalise the accelerometer measurement. 00587 Renorm = 1.0 / sqrt(Sqr(a_x) + Sqr(a_y) + Sqr(a_z)); 00588 a_x *= Renorm; 00589 a_y *= Renorm; 00590 a_z *= Renorm; 00591 */ 00592 00593 //Compute the objective function and Jacobian. 00594 f_1 = twoSEq_2*SEq_4 - twoSEq_1*SEq_3 - a_x; 00595 f_2 = twoSEq_1*SEq_2 + twoSEq_3*SEq_4 - a_y; 00596 f_3 = 1.0 - twoSEq_2*SEq_2 - twoSEq_3*SEq_3 - a_z; 00597 //J_11 negated in matrix multiplication. 00598 J_11or24 = twoSEq_3; 00599 J_12or23 = 2.0*SEq_4; 00600 //J_12 negated in matrix multiplication 00601 J_13or22 = twoSEq_1; 00602 J_14or21 = twoSEq_2; 00603 //Negated in matrix multiplication. 00604 J_32 = 2.0*J_14or21; 00605 //Negated in matrix multiplication. 00606 J_33 = 2.0*J_11or24; 00607 00608 //Compute the gradient (matrix multiplication). 00609 SEqHatDot_1 = J_14or21*f_2 - J_11or24*f_1; 00610 SEqHatDot_2 = J_12or23*f_1 + J_13or22*f_2 - J_32*f_3; 00611 SEqHatDot_3 = J_12or23*f_2 - J_33*f_3 - J_13or22*f_1; 00612 SEqHatDot_4 = J_14or21*f_1 + J_11or24*f_2; 00613 00614 //Normalise the gradient. 00615 Renorm = 1.0 / sqrt(Sqr(SEqHatDot_1) + Sqr(SEqHatDot_2) + Sqr(SEqHatDot_3) + Sqr(SEqHatDot_4)); 00616 SEqHatDot_1 *= Renorm; 00617 SEqHatDot_2 *= Renorm; 00618 SEqHatDot_3 *= Renorm; 00619 SEqHatDot_4 *= Renorm; 00620 00621 //Compute then integrate the estimated quaternion rate. 00622 SEq_1 += (SEqDot_omega_1 - (BetaIMU2*SEqHatDot_1))*dT; 00623 SEq_2 += (SEqDot_omega_2 - (BetaIMU2*SEqHatDot_2))*dT; 00624 SEq_3 += (SEqDot_omega_3 - (BetaIMU2*SEqHatDot_3))*dT; 00625 SEq_4 += (SEqDot_omega_4 - (BetaIMU2*SEqHatDot_4))*dT; 00626 00627 //Normalise quaternion 00628 Renorm = 1.0 / sqrt(Sqr(SEq_1) + Sqr(SEq_2) + Sqr(SEq_3) + Sqr(SEq_4)); 00629 SEq_1 *= Renorm; 00630 SEq_2 *= Renorm; 00631 SEq_3 *= Renorm; 00632 SEq_4 *= Renorm; 00633 00634 if ( FirstIMU2 ) { 00635 //Store orientation of auxiliary frame. 00636 AEq_1 = SEq_1; 00637 AEq_2 = SEq_2; 00638 AEq_3 = SEq_3; 00639 AEq_4 = SEq_4; 00640 FirstIMU2 = false; 00641 } 00642 00643 MadgwickEulerAngles(MadgwickIMU2); 00644 00645 } else 00646 for ( g = 0; g <(uint8)3; g++) { 00647 EstRate[g][MadgwickIMU2] = Gyro[g]; 00648 EstAngle[g][MadgwickIMU2] += EstRate[g][MadgwickIMU2]*dT; 00649 } 00650 00651 } // DoMadgwickIMU2 00652 00653 #endif // INC_IMU2 00654 00655 //_________________________________________________________________________________ 00656 00657 // AHRS.c 00658 // S.O.H. Madgwick 00659 // 25th August 2010 00660 00661 // Description: 00662 00663 // Quaternion implementation of the 'DCM filter' [Mahoney et al]. Incorporates the magnetic distortion 00664 // compensation algorithms from my filter [Madgwick] which eliminates the need for a reference 00665 // direction of flux (bx bz) to be predefined and limits the effect of magnetic distortions to yaw 00666 // a only. 00667 00668 // User must define 'dTOn2' as the (sample period / 2), and the filter gains 'MKp' and 'MKi'. 00669 00670 // Global variables 'q0', 'q1', 'q2', 'q3' are the quaternion elements representing the estimated 00671 // orientation. See my report for an overview of the use of quaternions in this application. 00672 00673 // User must call 'AHRSupdate()' every sample period and parse calibrated gyroscope ('gx', 'gy', 'gz'), 00674 // accelerometer ('ax', 'ay', 'az') and magnetometer ('mx', 'my', 'mz') data. Gyroscope units are 00675 // radians/second, accelerometer and magnetometer units are irrelevant as the vector is normalised. 00676 00677 void DoMadgwickAHRS(real32 gx, real32 gy, real32 gz, real32 ax, real32 ay, real32 az, real32 mx, real32 my, real32 mz) { 00678 00679 static uint8 g; 00680 static real32 ReNorm; 00681 static real32 hx, hy, hz, bx2, bz2, mx2, my2, mz2; 00682 static real32 vx, vy, vz, wx, wy, wz; 00683 static real32 ex, ey, ez; 00684 static real32 q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; 00685 00686 if ( F.AccMagnitudeOK ) { 00687 00688 // auxiliary variables to reduce number of repeated operations 00689 q0q0 = q0*q0; 00690 q0q1 = q0*q1; 00691 q0q2 = q0*q2; 00692 q0q3 = q0*q3; 00693 q1q1 = q1*q1; 00694 q1q2 = q1*q2; 00695 q1q3 = q1*q3; 00696 q2q2 = q2*q2; 00697 q2q3 = q2*q3; 00698 q3q3 = q3*q3; 00699 00700 ReNorm = 1.0 / sqrt( Sqr( mx ) + Sqr( my ) + Sqr( mz ) ); 00701 mx *= ReNorm; 00702 my *= ReNorm; 00703 mz *= ReNorm; 00704 mx2 = 2.0*mx; 00705 my2 = 2.0*my; 00706 mz2 = 2.0*mz; 00707 00708 // compute reference direction of flux 00709 hx = mx2*(0.5 - q2q2 - q3q3) + my2*(q1q2 - q0q3) + mz2*(q1q3 + q0q2); 00710 hy = mx2*(q1q2 + q0q3) + my2*( 0.5 - q1q1 - q3q3) + mz2*(q2q3 - q0q1); 00711 hz = mx2*(q1q3 - q0q2) + my2*(q2q3 + q0q1) + mz2*( 0.5 - q1q1 - q2q2 ); 00712 bx2 = 2.0*sqrt( Sqr( hx ) + Sqr( hy ) ); 00713 bz2 = 2.0*hz; 00714 00715 // estimated direction of gravity and flux (v and w) 00716 vx = 2.0*(q1q3 - q0q2); 00717 vy = 2.0*(q0q1 + q2q3); 00718 vz = q0q0 - q1q1 - q2q2 + q3q3; 00719 00720 wx = bx2*(0.5 - q2q2 - q3q3) + bz2*(q1q3 - q0q2); 00721 wy = bx2*(q1q2 - q0q3) + bz2*( q0q1 + q2q3 ); 00722 wz = bx2*(q0q2 + q1q3) + bz2*( 0.5 - q1q1 - q2q2 ); 00723 00724 // error is sum of cross product between reference direction of fields and direction measured by sensors 00725 ex = (ay*vz - az*vy) + (my*wz - mz*wy); 00726 ey = (az*vx - ax*vz) + (mz*wx - mx*wz); 00727 ez = (ax*vy - ay*vx) + (mx*wy - my*wx); 00728 00729 // integral error scaled integral gain 00730 exInt += ex*MKi*dT; 00731 eyInt += ey*MKi*dT; 00732 ezInt += ez*MKi*dT; 00733 00734 // adjusted gyroscope measurements 00735 gx += MKp*ex + exInt; 00736 gy += MKp*ey + eyInt; 00737 gz += MKp*ez + ezInt; 00738 00739 // integrate quaternion rate and normalise 00740 q0 += (-q1*gx - q2*gy - q3*gz)*dTOn2; 00741 q1 += (q0*gx + q2*gz - q3*gy)*dTOn2; 00742 q2 += (q0*gy - q1*gz + q3*gx)*dTOn2; 00743 q3 += (q0*gz + q1*gy - q2*gx)*dTOn2; 00744 00745 // normalise quaternion 00746 ReNorm = 1.0 / sqrt(Sqr(q0) + Sqr(q1) + Sqr(q2) + Sqr(q3)); 00747 q0 *= ReNorm; 00748 q1 *= ReNorm; 00749 q2 *= ReNorm; 00750 q3 *= ReNorm; 00751 00752 MadgwickEulerAngles(MadgwickAHRS); 00753 00754 } else 00755 for ( g = 0; g <(uint8)3; g++) { 00756 EstRate[g][MadgwickAHRS] = Gyro[g]; 00757 EstAngle[g][MadgwickAHRS] += EstRate[g][MadgwickAHRS]*dT; 00758 } 00759 00760 } // DoMadgwickAHRS 00761 00762 void MadgwickEulerAngles(uint8 S) { 00763 00764 EstAngle[Roll][S] = atan2(2.0*q2*q3 - 2.0*q0*q1, 2.0*Sqr(q0) + 2.0*Sqr(q3) - 1.0); 00765 EstAngle[Pitch][S] = asin(2.0*q1*q2 - 2.0*q0*q2); 00766 EstAngle[Yaw][S] = atan2(2.0*q1*q2 - 2.0*q0*q3, 2.0*Sqr(q0) + 2.0*Sqr(q1) - 1.0); 00767 00768 } // MadgwickEulerAngles 00769 00770 //_________________________________________________________________________________ 00771 00772 // Kalman Filter originally authored by Tom Pycke 00773 // http://tom.pycke.be/mav/71/kalman-filtering-of-imu-data 00774 00775 real32 AngleKF[2] = {0,0}; 00776 real32 BiasKF[2] = {0,0}; 00777 real32 P00[2] = {0,0}; 00778 real32 P01[2] = {0,0}; 00779 real32 P10[2] = {0,0}; 00780 real32 P11[2] = {0,0}; 00781 00782 real32 KalmanFilter(uint8 a, real32 NewAngle, real32 NewRate) { 00783 00784 // Q is a 2x2 matrix that represents the process covariance noise. 00785 // In this case, it indicates how much we trust the accelerometer 00786 // relative to the gyros. 00787 const real32 AngleQ = 0.003; 00788 const real32 GyroQ = 0.009; 00789 00790 // R represents the measurement covariance noise. In this case, 00791 // it is a 1x1 matrix that says that we expect AngleR rad jitter 00792 // from the accelerometer. 00793 const real32 AngleR = GYRO_PROP_NOISE; 00794 00795 static real32 y, S; 00796 static real32 K0, K1; 00797 00798 AngleKF[a] += (NewRate - BiasKF[a])*dT; 00799 P00[a] -= (( P10[a] + P01[a] ) + AngleQ )*dT; 00800 P01[a] -= P11[a]*dT; 00801 P10[a] -= P11[a]*dT; 00802 P11[a] += GyroQ*dT; 00803 00804 y = NewAngle - AngleKF[a]; 00805 S = 1.0 / ( P00[a] + AngleR ); 00806 K0 = P00[a]*S; 00807 K1 = P10[a]*S; 00808 00809 AngleKF[a] += K0*y; 00810 BiasKF[a] += K1*y; 00811 P00[a] -= K0*P00[a]; 00812 P01[a] -= K0*P01[a]; 00813 P10[a] -= K1*P00[a]; 00814 P11[a] -= K1*P01[a]; 00815 00816 return ( AngleKF[a] ); 00817 00818 } // KalmanFilter 00819 00820 void DoKalman(void) { // NO YAW ANGLE ESTIMATE 00821 EstAngle[Roll][Kalman] = KalmanFilter(Roll, asin(-Acc[LR]), Gyro[Roll]); 00822 EstAngle[Pitch][Kalman] = KalmanFilter(Pitch, asin(Acc[BF]), Gyro[Pitch]); 00823 EstRate[Roll][Kalman] = Gyro[Roll]; 00824 EstRate[Pitch][Kalman] = Gyro[Pitch]; 00825 } // DoKalman 00826 00827 00828 //_________________________________________________________________________________ 00829 00830 // MultWii 00831 // Original code by Alex at: http://radio-commande.com/international/triwiicopter-design/ 00832 // simplified IMU based on Kalman Filter 00833 // inspired from http://starlino.com/imu_guide.html 00834 // and http://www.starlino.com/imu_kalman_arduino.html 00835 // with this algorithm, we can get absolute angles for a stable mode integration 00836 00837 real32 AccMW[3] = {0.0, 0.0, 1.0}; // init acc in stable mode 00838 real32 GyroMW[3] = {0.0, 0.0, 0.0}; // R obtained from last estimated value and gyro movement 00839 real32 Axz, Ayz; // angles between projection of R on XZ/YZ plane and Z axis 00840 00841 void DoMultiWii(void) { // V1.6 NO YAW ANGLE ESTIMATE 00842 00843 const real32 GyroWt = 50.0; // gyro weight/smoothing factor 00844 const real32 GyroWtR = 1.0 / GyroWt; 00845 00846 if ( Acc[UD] < 0.0 ) { // check not inverted 00847 00848 if ( F.AccMagnitudeOK ) { 00849 Ayz = atan2( AccMW[LR], AccMW[UD] ) + Gyro[Roll]*dT; 00850 Axz = atan2( AccMW[BF], AccMW[UD] ) + Gyro[Pitch]*dT; 00851 } else { 00852 Ayz += Gyro[Roll]*dT; 00853 Axz += Gyro[Pitch]*dT; 00854 } 00855 00856 // reverse calculation of GyroMW from Awz angles, 00857 // for formulae deduction see http://starlino.com/imu_guide.html 00858 GyroMW[Roll] = sin( Ayz ) / sqrt( 1.0 + Sqr( cos( Ayz ) )*Sqr( tan( Axz ) ) ); 00859 GyroMW[Pitch] = sin( Axz ) / sqrt( 1.0 + Sqr( cos( Axz ) )*Sqr( tan( Ayz ) ) ); 00860 GyroMW[Yaw] = sqrt( fabs( 1.0 - Sqr( GyroMW[Roll] ) - Sqr( GyroMW[Pitch] ) ) ); 00861 00862 //combine accelerometer and gyro readings 00863 AccMW[LR] = ( -Acc[LR] + GyroWt*GyroMW[Roll] )*GyroWtR; 00864 AccMW[BF] = ( Acc[BF] + GyroWt*GyroMW[Pitch] )*GyroWtR; 00865 AccMW[UD] = ( -Acc[UD] + GyroWt*GyroMW[Yaw] )*GyroWtR; 00866 } 00867 00868 EstAngle[Roll][MultiWii] = Ayz; 00869 EstAngle[Pitch][MultiWii] = Axz; 00870 00871 // EstRate[Roll][MultiWii] = GyroMW[Roll]; 00872 // EstRate[Pitch][MultiWii] = GyroMW[Pitch]; 00873 00874 EstRate[Roll][MultiWii] = Gyro[Roll]; 00875 EstRate[Pitch][MultiWii] = Gyro[Pitch]; 00876 00877 } // DoMultiWii 00878 00879 //_________________________________________________________________________________ 00880 00881 void AttitudeTest(void) { 00882 00883 TxString("\r\nAttitude Test\r\n"); 00884 00885 GetAttitude(); 00886 00887 TxString("\r\ndT \t"); 00888 TxVal32(dT*1000.0, 3, 0); 00889 TxString(" Sec.\r\n\r\n"); 00890 00891 if ( GyroType == IRSensors ) { 00892 00893 TxString("IR Sensors:\r\n"); 00894 TxString("\tRoll \t"); 00895 TxVal32(IR[Roll]*100.0, 2, HT); 00896 TxNextLine(); 00897 TxString("\tPitch\t"); 00898 TxVal32(IR[Pitch]*100.0, 2, HT); 00899 TxNextLine(); 00900 TxString("\tZ \t"); 00901 TxVal32(IR[Yaw]*100.0, 2, HT); 00902 TxNextLine(); 00903 TxString("\tMin/Max\t"); 00904 TxVal32(IRMin*100.0, 2, HT); 00905 TxVal32(IRMax*100.0, 2, HT); 00906 TxString("\tSwing\t"); 00907 TxVal32(IRSwing*100.0, 2, HT); 00908 TxNextLine(); 00909 00910 } else { 00911 00912 TxString("Gyro, Compensated, Max Delta(Deg./Sec.):\r\n"); 00913 TxString("\tRoll \t"); 00914 TxVal32(Gyro[Roll]*MILLIANGLE, 3, HT); 00915 TxVal32(Rate[Roll]*MILLIANGLE, 3, HT); 00916 TxVal32(GyroNoise[Roll]*MILLIANGLE,3, 0); 00917 TxNextLine(); 00918 TxString("\tPitch\t"); 00919 TxVal32(Gyro[Pitch]*MILLIANGLE, 3, HT); 00920 TxVal32(Rate[Pitch]*MILLIANGLE, 3, HT); 00921 TxVal32(GyroNoise[Pitch]*MILLIANGLE,3, 0); 00922 TxNextLine(); 00923 TxString("\tYaw \t"); 00924 TxVal32(Gyro[Yaw]*MILLIANGLE, 3, HT); 00925 TxVal32(Rate[Yaw]*MILLIANGLE, 3, HT); 00926 TxVal32(GyroNoise[Yaw]*MILLIANGLE, 3, 0); 00927 TxNextLine(); 00928 00929 TxString("Accelerations , peak change(G):\r\n"); 00930 TxString("\tB->F \t"); 00931 TxVal32(Acc[BF]*1000.0, 3, HT); 00932 TxVal32( AccNoise[BF]*1000.0, 3, 0); 00933 TxNextLine(); 00934 TxString("\tL->R \t"); 00935 TxVal32(Acc[LR]*1000.0, 3, HT); 00936 TxVal32( AccNoise[LR]*1000.0, 3, 0); 00937 TxNextLine(); 00938 TxString("\tU->D \t"); 00939 TxVal32(Acc[UD]*1000.0, 3, HT); 00940 TxVal32( AccNoise[UD]*1000.0, 3, 0); 00941 TxNextLine(); 00942 } 00943 00944 if ( CompassType != HMC6352 ) { 00945 TxString("Magnetic:\r\n"); 00946 TxString("\tX \t"); 00947 TxVal32(Mag[Roll].V, 0, 0); 00948 TxNextLine(); 00949 TxString("\tY \t"); 00950 TxVal32(Mag[Pitch].V, 0, 0); 00951 TxNextLine(); 00952 TxString("\tZ \t"); 00953 TxVal32(Mag[Yaw].V, 0, 0); 00954 TxNextLine(); 00955 } 00956 00957 TxString("Heading: \t"); 00958 TxVal32(Make2Pi(Heading)*MILLIANGLE, 3, 0); 00959 TxNextLine(); 00960 00961 } // AttitudeTest 00962 00963 void InitAttitude(void) { 00964 00965 static uint8 a, s; 00966 00967 #ifdef INC_IMU2 00968 00969 FirstIMU2 = true; 00970 BetaIMU2 = sqrt(0.75) * GyroNoiseRadian[GyroType]; 00971 00972 //Quaternion orientation of earth frame relative to auxiliary frame. 00973 AEq_1 = 1.0; 00974 AEq_2 = 0.0; 00975 AEq_3 = 0.0; 00976 AEq_4 = 0.0; 00977 00978 //Estimated orientation quaternion elements with initial conditions. 00979 SEq_1 = 1.0; 00980 SEq_2 = 0.0; 00981 SEq_3 = 0.0; 00982 SEq_4 = 0.0; 00983 00984 #endif // INC_IMU2 00985 00986 for ( a = 0; a < (uint8)2; a++ ) 00987 AngleCF[a] = AngleKF[a] = BiasKF[a] = F0[a] = F1[a] = F2[a] = 0.0; 00988 00989 for ( a = 0; a < (uint8)3; a++ ) 00990 for ( s = 0; s < MaxAttitudeScheme; s++ ) 00991 EstAngle[a][s] = EstRate[a][s] = 0.0; 00992 00993 } // InitAttitude 00994
Generated on Wed Jul 13 2022 01:50:20 by
