PageRenderTime 29ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/UDB3.X/AHRS/AHRS.c

https://bitbucket.org/mituav/udb3_quadrotor_control
C | 283 lines | 155 code | 57 blank | 71 comment | 19 complexity | 34e5faac4224c340c4c09ce72d504bea MD5 | raw file
  1. #include "AHRS.h"
  2. #define CALIBRATE_ACC
  3. /* globals */
  4. extern volatile tLoopFlags loop;
  5. extern volatile tSensorCal SensorCal;
  6. extern volatile tSensorData SensorData;
  7. extern volatile tAHRSdata AHRSdata;
  8. extern _Q16 num0p998, num0p0001, num1p0, num0p5, num1p1;
  9. // Initialize
  10. void AHRS_init(void){
  11. // Setup calibration struct
  12. SensorCal.biasCountGyro = 0;
  13. SensorCal.biasTotalGyro = 2000;
  14. SensorCal.blankReadsGyro = 200;
  15. SensorCal.biasCountAcc = 0;
  16. SensorCal.biasTotalAcc = 2000;
  17. SensorCal.blankReadsAcc = 200;
  18. SensorCal.pBias = _Q16ftoi(0.0);
  19. SensorCal.qBias = _Q16ftoi(0.0);
  20. SensorCal.rBias = _Q16ftoi(0.0);
  21. // Initialize attitude
  22. AHRSdata.q_est.o = _Q16ftoi(1.0);
  23. AHRSdata.q_est.x = _Q16ftoi(0.0);
  24. AHRSdata.q_est.y = _Q16ftoi(0.0);
  25. AHRSdata.q_est.z = _Q16ftoi(0.0);
  26. AHRSdata.q_meas.o = _Q16ftoi(1.0);
  27. AHRSdata.q_meas.x = _Q16ftoi(0.0);
  28. AHRSdata.q_meas.y = _Q16ftoi(0.0);
  29. AHRSdata.q_meas.z = _Q16ftoi(0.0);
  30. // Parameters
  31. SensorCal.gyroRawBias = 512; // Mid-range value for 10-bit A2D
  32. SensorCal.accelRawBias= 500; // Mid-range value for 10-bit A2D (adjusted manually)
  33. SensorCal.gyroScale = _Q16ftoi(PI/180.0 / 1024.0 * 3.3 / 0.0033 * 1.5 ); // Based on 10-bit A2D and gyro sensitivity of 3.3mV / deg/s with fudge factor
  34. SensorCal.gyroPropDT = _Q16ftoi(0.001 / 2.0);
  35. SensorCal.accelScale = _Q16ftoi( 3.3 / 1024.0 / 0.200 ); // at 6g resolution, sensitivity is 200mV/g
  36. SensorCal.acc_window_min = _Q16ftoi(0.5);
  37. SensorCal.acc_window_max = _Q16ftoi(1.5);
  38. SensorCal.axBias = _Q16ftoi(0.0);
  39. SensorCal.ayBias = _Q16ftoi(0.0);
  40. SensorCal.azBias = _Q16ftoi(0.0);
  41. SensorCal.K_AttFilter = _Q16ftoi(0.025); // Default value, may be overwritten by SensorPacket
  42. }
  43. void AHRS_GyroProp(void){
  44. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  45. // Read raw gyro values from A2D registers, A2D automatically scans inputs at 5KHz
  46. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  47. SensorData.gyroX = xgyro; SensorData.gyroX -= SensorCal.gyroRawBias;
  48. SensorData.gyroY = ygyro; SensorData.gyroY -= SensorCal.gyroRawBias;
  49. SensorData.gyroZ = zgyro; SensorData.gyroZ -= SensorCal.gyroRawBias;
  50. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  51. // Gyro scaling, to rad/sec
  52. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  53. int16toQ16(&AHRSdata.p, &SensorData.gyroX);
  54. AHRSdata.p = -mult( AHRSdata.p, SensorCal.gyroScale);
  55. int16toQ16(&AHRSdata.q, &SensorData.gyroY);
  56. AHRSdata.q = mult( AHRSdata.q, SensorCal.gyroScale );
  57. int16toQ16(&AHRSdata.r, &SensorData.gyroZ);
  58. AHRSdata.r = mult( AHRSdata.r, SensorCal.gyroScale );
  59. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  60. // Initial gyro bias calculation
  61. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  62. if( SensorCal.biasCountGyro < SensorCal.biasTotalGyro){
  63. // Do some blank reads to clear any garbage in the initial transient
  64. if(--SensorCal.blankReadsGyro > 0)
  65. return;
  66. SensorCal.pBias += AHRSdata.p;
  67. SensorCal.qBias += AHRSdata.q;
  68. SensorCal.rBias += AHRSdata.r;
  69. led_on(LED_RED);
  70. if( ++SensorCal.biasCountGyro == SensorCal.biasTotalGyro ){
  71. _Q16 tmp = _Q16ftoi(1.0 / ((float)SensorCal.biasTotalGyro ));
  72. SensorCal.pBias = mult( SensorCal.pBias, tmp);
  73. SensorCal.qBias = mult( SensorCal.qBias, tmp);
  74. SensorCal.rBias = mult( SensorCal.rBias, tmp);
  75. led_off(LED_RED);
  76. led_off(LED_GREEN);
  77. }
  78. // TODO: Initialize q_est to q_meas
  79. return;
  80. }
  81. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  82. // Gyro bias correction
  83. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  84. AHRSdata.p -= SensorCal.pBias;
  85. AHRSdata.q -= SensorCal.qBias;
  86. AHRSdata.r -= SensorCal.rBias;
  87. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  88. // Gyro propagation
  89. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  90. AHRSdata.q_est.o -= mult( mult(AHRSdata.q_est.x,AHRSdata.p) + mult(AHRSdata.q_est.y,AHRSdata.q) + mult(AHRSdata.q_est.z,AHRSdata.r) , SensorCal.gyroPropDT);
  91. AHRSdata.q_est.x += mult( mult(AHRSdata.q_est.o,AHRSdata.p) - mult(AHRSdata.q_est.z,AHRSdata.q) + mult(AHRSdata.q_est.y,AHRSdata.r) , SensorCal.gyroPropDT);
  92. AHRSdata.q_est.y += mult( mult(AHRSdata.q_est.z,AHRSdata.p) + mult(AHRSdata.q_est.o,AHRSdata.q) - mult(AHRSdata.q_est.x,AHRSdata.r) , SensorCal.gyroPropDT);
  93. AHRSdata.q_est.z += mult( mult(AHRSdata.q_est.x,AHRSdata.q) - mult(AHRSdata.q_est.y,AHRSdata.p) + mult(AHRSdata.q_est.o,AHRSdata.r) , SensorCal.gyroPropDT);
  94. // Run the attitude control after propogating gyros
  95. loop.AttCtl = 1;
  96. }
  97. void AHRS_AccMagCorrect(void)
  98. {
  99. // Quit if the biases are still being calculated
  100. if( SensorCal.biasCountGyro < SensorCal.biasTotalGyro)
  101. return;
  102. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  103. // Read raw accel values from A2D registers, A2D automatically scans inputs at 5KHz
  104. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  105. SensorData.accX = xaccel; SensorData.accX -= SensorCal.accelRawBias;
  106. SensorData.accY = yaccel; SensorData.accY -= SensorCal.accelRawBias;
  107. SensorData.accZ = zaccel; SensorData.accZ -= SensorCal.accelRawBias;
  108. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  109. // Accel scaling, to g
  110. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  111. int16toQ16(&AHRSdata.ax, &SensorData.accX);
  112. AHRSdata.ax = -mult( AHRSdata.ax, SensorCal.accelScale);
  113. int16toQ16(&AHRSdata.ay, &SensorData.accY);
  114. AHRSdata.ay = mult( AHRSdata.ay, SensorCal.accelScale );
  115. int16toQ16(&AHRSdata.az, &SensorData.accZ);
  116. AHRSdata.az = -mult( AHRSdata.az, SensorCal.accelScale );
  117. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  118. // Initial acc bias calculation
  119. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  120. if( SensorCal.biasCountAcc < SensorCal.biasTotalAcc){
  121. // Do some blank reads to clear any garbage in the initial transient
  122. if(--SensorCal.blankReadsAcc > 0)
  123. return;
  124. SensorCal.axBias += AHRSdata.ax;
  125. SensorCal.ayBias += AHRSdata.ay;
  126. SensorCal.azBias += AHRSdata.az;
  127. led_on(LED_RED);
  128. if( ++SensorCal.biasCountAcc == SensorCal.biasTotalAcc ){
  129. _Q16 tmp = _Q16ftoi(1.0 / ((float)SensorCal.biasTotalAcc ));
  130. SensorCal.axBias = mult( SensorCal.axBias, tmp);
  131. SensorCal.ayBias = mult( SensorCal.ayBias, tmp);
  132. SensorCal.azBias = mult( SensorCal.azBias, tmp);
  133. led_off(LED_RED);
  134. led_off(LED_GREEN);
  135. }
  136. return;
  137. }
  138. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  139. // Acc bias correction
  140. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  141. AHRSdata.ax = AHRSdata.ax - SensorCal.axBias;
  142. AHRSdata.ay = AHRSdata.ay - SensorCal.ayBias;
  143. AHRSdata.az = AHRSdata.az - SensorCal.azBias;
  144. _Q16 ax = AHRSdata.ax;
  145. _Q16 ay = AHRSdata.ay;
  146. _Q16 az = AHRSdata.az;
  147. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  148. // Maneuver detector, do not use accels during fast movement
  149. // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  150. // TODO: Check angular rates
  151. // Roll and pitch calculation, assumes accelerometer units are 10000*g
  152. // Normalize the acceleration vector to length 1
  153. _Q16 root = _Q16sqrt( mult(ax,ax) + mult(ay,ay) + mult(az,az));
  154. // TODO: Make sure we're around 1g
  155. if( root < SensorCal.acc_window_min || root > SensorCal.acc_window_max){
  156. return;
  157. }
  158. // Normalize
  159. ax = _IQ16div(ax, root);
  160. ay = _IQ16div(ay, root);
  161. az = _IQ16div(az, root);
  162. // Too close to singularity (due to numerical precision limits)
  163. if( ax > num0p998 || -ax > num0p998 )
  164. return;
  165. root = _Q16sqrt( mult(ay,ay) + mult(az,az));
  166. if(root < num0p0001 )
  167. root = num0p0001;
  168. // Calculate sin/cos of roll and pitch
  169. _Q16 sinR = - _IQ16div(ay,root);
  170. _Q16 cosR = - _IQ16div(az,root);
  171. _Q16 sinP = ax;
  172. _Q16 cosP = -( mult(ay,sinR) + mult(az,cosR) );
  173. // Calculate half-angles
  174. _Q16 cosR2 = _Q16sqrt( mult( num1p0 + cosR , num0p5 ));
  175. if(cosR2 < num0p0001 )
  176. cosR2 = num0p0001;
  177. _Q16 sinR2 = mult(_IQ16div( sinR , cosR2) , num0p5 ); // WARNING: This step is numerically ill-behaved!
  178. _Q16 cosP2 = _Q16sqrt( mult( num1p0 + cosP , num0p5 ));
  179. if(cosP2 < num0p0001 )
  180. cosP2 = num0p0001;
  181. _Q16 sinP2 = mult(_IQ16div( sinP , cosP2) , num0p5 ); // WARNING: This step is numerically ill-behaved!
  182. // Too close to singularity (due to numerical precision limits)
  183. if( mult(cosR2,cosR2) + mult(sinR2,sinR2) > num1p1 || mult(cosP2,cosP2) + mult(sinP2,sinP2) > num1p1 )
  184. return;
  185. // Yaw calculation
  186. // Normalize the magnetometer vector to length 1
  187. /* magx = (float)AHRSdata.magY;
  188. magy = -(float)AHRSdata.magX;
  189. magz = (float)AHRSdata.magZ;
  190. // Todo: magx*magx can be done in fixed pt
  191. root = sqrt( magx*magx + magy*magy + magz*magz );
  192. magx /= root;
  193. magy /= root;
  194. magz /= root;
  195. yaw = atan2(-cosR*magy - sinR*magz , cosP*magx+sinP*sinR*magy-sinP*cosR*magz);
  196. yaw += PI;
  197. if(yaw > PI){
  198. yaw -= 2*PI;
  199. }
  200. sinY2 = sin(yaw/2.0);
  201. cosY2 = cos(yaw/2.0);
  202. */
  203. _Q16 cosY2 = _Q16ftoi(1.0);
  204. _Q16 sinY2 = 0;
  205. // Finally get quaternion
  206. tQuaternion qroll,qpitch,qyaw;
  207. qyaw.o = cosY2; qyaw.x = 0; qyaw.y = 0; qyaw.z = sinY2;
  208. qpitch.o = cosP2; qpitch.x = 0; qpitch.y = sinP2; qpitch.z = 0;
  209. qroll.o = cosR2; qroll.x = sinR2; qroll.y = 0; qroll.z = 0;
  210. AHRSdata.q_meas = qprod(qyaw,qpitch);
  211. AHRSdata.q_meas = qprod(AHRSdata.q_meas, qroll);
  212. // Check if flipped from last measurement
  213. if( mult(AHRSdata.q_meas.x,AHRSdata.q_est.x) + mult(AHRSdata.q_meas.y,AHRSdata.q_est.y) + mult(AHRSdata.q_meas.z,AHRSdata.q_est.z) + mult(AHRSdata.q_meas.o,AHRSdata.q_est.o) < 0 )
  214. {
  215. AHRSdata.q_meas.o = - AHRSdata.q_meas.o;
  216. AHRSdata.q_meas.x = - AHRSdata.q_meas.x;
  217. AHRSdata.q_meas.y = - AHRSdata.q_meas.y;
  218. AHRSdata.q_meas.z = - AHRSdata.q_meas.z;
  219. }
  220. // Gyro bias estimation
  221. // Make the correction
  222. AHRSdata.q_est.o -= mult(AHRSdata.q_est.o-AHRSdata.q_meas.o, SensorCal.K_AttFilter);
  223. AHRSdata.q_est.x -= mult(AHRSdata.q_est.x-AHRSdata.q_meas.x, SensorCal.K_AttFilter);
  224. AHRSdata.q_est.y -= mult(AHRSdata.q_est.y-AHRSdata.q_meas.y, SensorCal.K_AttFilter);
  225. AHRSdata.q_est.z -= mult(AHRSdata.q_est.z-AHRSdata.q_meas.z, SensorCal.K_AttFilter);
  226. }