MahonyAHRS.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. //=============================================================================================
  2. // MahonyAHRS.c
  3. //=============================================================================================
  4. //
  5. // Madgwick's implementation of Mayhony's AHRS algorithm.
  6. // See: http://www.x-io.co.uk/open-source-imu-and-ahrs-algorithms/
  7. //
  8. // From the x-io website "Open-source resources available on this website are
  9. // provided under the GNU General Public Licence unless an alternative licence
  10. // is provided in source."
  11. //
  12. // Date Author Notes
  13. // 29/09/2011 SOH Madgwick Initial release
  14. // 02/10/2011 SOH Madgwick Optimised for reduced CPU load
  15. //
  16. // Algorithm paper:
  17. // http://ieeexplore.ieee.org/xpl/login.jsp?tp=&arnumber=4608934&url=http%3A%2F%2Fieeexplore.ieee.org%2Fstamp%2Fstamp.jsp%3Ftp%3D%26arnumber%3D4608934
  18. //
  19. //=============================================================================================
  20. //-------------------------------------------------------------------------------------------
  21. // Header files
  22. #include "MahonyAHRS.h"
  23. #include <math.h>
  24. //-------------------------------------------------------------------------------------------
  25. // Definitions
  26. #define sampleFreq 100
  27. #define twoKpDef (10.0f * 0.5f) // 2 * proportional gain
  28. #define twoKiDef (2.0f * 0.0f) // 2 * integral gain
  29. float twoKi = twoKiDef; // 2 * integral gain (Ki)
  30. float twoKp = twoKpDef; // 2 * integral gain (Ki)
  31. float invSampleFreq = 1.0f/sampleFreq;
  32. float q0=1.0f, q1=0.0f, q2=0.0f, q3=0.0f; // quaternion of sensor frame relative to auxiliary frame
  33. float integralFBx=0, integralFBy=0, integralFBz=0; // integral error terms scaled by Ki
  34. float roll, pitch, yaw;
  35. char anglesComputed;
  36. float Mahony_invSqrt(float x)
  37. {
  38. float halfx = 0.5f * x;
  39. float y = x;
  40. long i = *(long*)&y;
  41. i = 0x5f3759df - (i>>1);
  42. y = *(float*)&i;
  43. y = y * (1.5f - (halfx * y * y));
  44. y = y * (1.5f - (halfx * y * y));
  45. return y;
  46. }
  47. float invSqrt(float x)
  48. {
  49. float halfx = 0.5f * x;
  50. float y = x;
  51. long i = *(long*)&y;
  52. i = 0x5f3759df - (i>>1);
  53. y = *(float*)&i;
  54. y = y * (1.5f - (halfx * y * y));
  55. return y;
  56. }
  57. #define samplePeriod 0.01f
  58. //float twoKp=(5.0f * 0.5f);
  59. //float twoKi=(2.0f * 0.5f);
  60. //float integralFBx = 0.0f, integralFBy = 0.0f, integralFBz = 0.0f; // integral error terms scaled by Ki
  61. //¹²éîËÄÔªÊý
  62. void quatconj(const float *Quat,float *out)
  63. {
  64. out[0]= Quat[0];
  65. out[1]=-Quat[1];
  66. out[2]=-Quat[2];
  67. out[3]=-Quat[3];
  68. }
  69. float quatdianc(const float* Q,const float* P)
  70. {
  71. float result;
  72. result=P[0]*Q[0] + P[1]*Q[1] + P[2]*Q[2] + P[3]*Q[3];
  73. return result;
  74. }
  75. void quatmultiply(const float * Q,const float * P,float *QP)
  76. {
  77. QP[0]=P[0]*Q[0] - P[1]*Q[1] - P[2]*Q[2] - P[3]*Q[3];
  78. QP[1]=P[0]*Q[1] + P[1]*Q[0] + P[2]*Q[3] - P[3]*Q[2];
  79. QP[2]=P[0]*Q[2] + P[2]*Q[0] + P[3]*Q[1] - P[1]*Q[3];
  80. QP[3]=P[0]*Q[3] + P[3]*Q[0] + P[1]*Q[2] - P[2]*Q[1];
  81. }
  82. void quatinv(float* Q,float *quatinvQ)
  83. {
  84. float mod;
  85. float temp[4];
  86. quatconj(Q,temp);
  87. mod=quatdianc(temp,temp);
  88. quatinvQ[0]=temp[0]/mod;
  89. quatinvQ[1]=temp[1]/mod;
  90. quatinvQ[2]=temp[2]/mod;
  91. quatinvQ[3]=temp[3]/mod;
  92. }
  93. void quatrotate(float* sour_pion,float* Q,float *out_poin)
  94. {
  95. float Quaternion_p[4];
  96. float temp[4];
  97. float temp1[4];
  98. float temp2[4];
  99. Quaternion_p[0]=0;
  100. Quaternion_p[1]=sour_pion[0];
  101. Quaternion_p[2]=sour_pion[1];
  102. Quaternion_p[3]=sour_pion[2];
  103. quatmultiply(Q,Quaternion_p,temp);
  104. quatinv(Q,temp2);
  105. quatmultiply(temp,temp2,temp1);
  106. out_poin[0]=temp1[1];
  107. out_poin[1]=temp1[2];
  108. out_poin[2]=temp1[3];
  109. }
  110. void accel_BConvertToN(float accel_s[3], float accel_d[3], float q[4])
  111. {
  112. float temp[4];
  113. quatconj(q,temp);
  114. quatrotate(accel_s,temp,accel_d);
  115. // accel_d[2] -= 1;
  116. }
  117. void updateIMU(float gx, float gy, float gz, float ax, float ay, float az)
  118. {
  119. float recipNorm;
  120. float halfvx, halfvy, halfvz;
  121. float halfex, halfey, halfez;
  122. float qa, qb, qc;
  123. // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
  124. if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f)))
  125. {
  126. // Normalise accelerometer measurement
  127. recipNorm = invSqrt(ax * ax + ay * ay + az * az);
  128. ax *= recipNorm;
  129. ay *= recipNorm;
  130. az *= recipNorm;
  131. // Estimated direction of gravity and vector perpendicular to magnetic flux
  132. halfvx = q1 * q3 - q0 * q2;
  133. halfvy = q0 * q1 + q2 * q3;
  134. halfvz = q0 * q0 - 0.5f + q3 * q3;
  135. // Error is sum of cross product between estimated and measured direction of gravity
  136. halfex = (ay * halfvz - az * halfvy);
  137. halfey = (az * halfvx - ax * halfvz);
  138. halfez = (ax * halfvy - ay * halfvx);
  139. // Compute and apply integral feedback if enabled
  140. if(twoKi > 0.0f)
  141. {
  142. integralFBx += twoKi * halfex * samplePeriod; // integral error scaled by Ki
  143. integralFBy += twoKi * halfey * samplePeriod;
  144. integralFBz += twoKi * halfez * samplePeriod;
  145. gx += integralFBx; // apply integral feedback
  146. gy += integralFBy;
  147. gz += integralFBz;
  148. }
  149. else
  150. {
  151. integralFBx = 0.0f; // prevent integral windup
  152. integralFBy = 0.0f;
  153. integralFBz = 0.0f;
  154. }
  155. // Apply proportional feedback
  156. gx += twoKp * halfex;
  157. gy += twoKp * halfey;
  158. gz += twoKp * halfez;
  159. }
  160. // Integrate rate of change of quaternion
  161. gx *= (0.5f * samplePeriod); // pre-multiply common factors
  162. gy *= (0.5f * samplePeriod);
  163. gz *= (0.5f * samplePeriod);
  164. qa = q0;
  165. qb = q1;
  166. qc = q2;
  167. q0 += (-qb * gx - qc * gy - q3 * gz);
  168. q1 += (qa * gx + qc * gz - q3 * gy);
  169. q2 += (qa * gy - qb * gz + q3 * gx);
  170. q3 += (qa * gz + qb * gy - qc * gx);
  171. // Normalise quaternion
  172. recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
  173. q0 *= recipNorm;
  174. q1 *= recipNorm;
  175. q2 *= recipNorm;
  176. q3 *= recipNorm;
  177. }
  178. void Mahony_update(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz)
  179. {
  180. float recipNorm;
  181. float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3;
  182. float hx, hy, bx, bz;
  183. float halfvx, halfvy, halfvz, halfwx, halfwy, halfwz;
  184. float halfex, halfey, halfez;
  185. float qa, qb, qc;
  186. // Convert gyroscope degrees/sec to radians/sec
  187. // gx *= 0.0174533f;
  188. // gy *= 0.0174533f;
  189. // gz *= 0.0174533f;
  190. // Compute feedback only if accelerometer measurement valid
  191. // (avoids NaN in accelerometer normalisation)
  192. if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {
  193. // Normalise accelerometer measurement
  194. recipNorm = Mahony_invSqrt(ax * ax + ay * ay + az * az);
  195. ax *= recipNorm;
  196. ay *= recipNorm;
  197. az *= recipNorm;
  198. // Normalise magnetometer measurement
  199. recipNorm = Mahony_invSqrt(mx * mx + my * my + mz * mz);
  200. mx *= recipNorm;
  201. my *= recipNorm;
  202. mz *= recipNorm;
  203. // Auxiliary variables to avoid repeated arithmetic
  204. q0q0 = q0 * q0;
  205. q0q1 = q0 * q1;
  206. q0q2 = q0 * q2;
  207. q0q3 = q0 * q3;
  208. q1q1 = q1 * q1;
  209. q1q2 = q1 * q2;
  210. q1q3 = q1 * q3;
  211. q2q2 = q2 * q2;
  212. q2q3 = q2 * q3;
  213. q3q3 = q3 * q3;
  214. // Reference direction of Earth's magnetic field
  215. hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2));
  216. hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1));
  217. bx = sqrtf(hx * hx + hy * hy);
  218. bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2));
  219. // Estimated direction of gravity and magnetic field
  220. halfvx = q1q3 - q0q2;
  221. halfvy = q0q1 + q2q3;
  222. halfvz = q0q0 - 0.5f + q3q3;
  223. halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2);
  224. halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3);
  225. halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2);
  226. // Error is sum of cross product between estimated direction
  227. // and measured direction of field vectors
  228. halfex = (ay * halfvz - az * halfvy) + (my * halfwz - mz * halfwy);
  229. halfey = (az * halfvx - ax * halfvz) + (mz * halfwx - mx * halfwz);
  230. halfez = (ax * halfvy - ay * halfvx) + (mx * halfwy - my * halfwx);
  231. // Compute and apply integral feedback if enabled
  232. if(twoKi > 0.0f) {
  233. // integral error scaled by Ki
  234. integralFBx += twoKi * halfex * invSampleFreq;
  235. integralFBy += twoKi * halfey * invSampleFreq;
  236. integralFBz += twoKi * halfez * invSampleFreq;
  237. gx += integralFBx; // apply integral feedback
  238. gy += integralFBy;
  239. gz += integralFBz;
  240. } else {
  241. integralFBx = 0.0f; // prevent integral windup
  242. integralFBy = 0.0f;
  243. integralFBz = 0.0f;
  244. }
  245. // Apply proportional feedback
  246. gx += twoKpDef * halfex;
  247. gy += twoKpDef * halfey;
  248. gz += twoKpDef * halfez;
  249. }
  250. // Integrate rate of change of quaternion
  251. gx *= (0.5f * invSampleFreq); // pre-multiply common factors
  252. gy *= (0.5f * invSampleFreq);
  253. gz *= (0.5f * invSampleFreq);
  254. qa = q0;
  255. qb = q1;
  256. qc = q2;
  257. q0 += (-qb * gx - qc * gy - q3 * gz);
  258. q1 += (qa * gx + qc * gz - q3 * gy);
  259. q2 += (qa * gy - qb * gz + q3 * gx);
  260. q3 += (qa * gz + qb * gy - qc * gx);
  261. // Normalise quaternion
  262. recipNorm = Mahony_invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
  263. q0 *= recipNorm;
  264. q1 *= recipNorm;
  265. q2 *= recipNorm;
  266. q3 *= recipNorm;
  267. anglesComputed = 0;
  268. }
  269. float roll, pitch, yaw;
  270. char anglesComputed;
  271. void Mahony_computeAngles()
  272. {
  273. roll = atan2f(q0*q1 + q2*q3, 0.5f - q1*q1 - q2*q2);
  274. pitch = asinf(-2.0f * (q1*q3 - q0*q2));
  275. yaw = atan2f(q1*q2 + q0*q3, 0.5f - q2*q2 - q3*q3);
  276. anglesComputed = 1;
  277. }
  278. float getRoll() {
  279. if (!anglesComputed) Mahony_computeAngles();
  280. return roll * 57.29578f;
  281. }
  282. float getPitch() {
  283. if (!anglesComputed) Mahony_computeAngles();
  284. return pitch * 57.29578f;
  285. }
  286. float getYaw() {
  287. if (!anglesComputed) Mahony_computeAngles();
  288. return yaw * 57.29578f + 180.0f;
  289. }
  290. //============================================================================================
  291. // END OF CODE
  292. //============================================================================================