MahonyAHRS.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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 twoKpDef (120.0f * 0.5f) // 2 * proportional gain
  27. #define twoKiDef (0.5f * 1.0f) // 2 * integral gain
  28. #define sampleFrequency 100 //²ÉÑùƵÂÊ
  29. float q0, q1, q2, q3; // quaternion of sensor frame relative to auxiliary frame
  30. float integralFBx, integralFBy, integralFBz; // integral error terms scaled by Ki
  31. float invSampleFreq;
  32. float roll, pitch, yaw;
  33. char anglesComputed;
  34. void Mahony_Init(void)
  35. {
  36. q0 = 1.0f;
  37. q1 = 0.0f;
  38. q2 = 0.0f;
  39. q3 = 0.0f;
  40. integralFBx = 0.0f;
  41. integralFBy = 0.0f;
  42. integralFBz = 0.0f;
  43. anglesComputed = 0;
  44. invSampleFreq = 1.0f / sampleFrequency;
  45. }
  46. float Mahony_invSqrt(float x)
  47. {
  48. float halfx = 0.5f * x;
  49. float y = x;
  50. long i = *(long*)&y;
  51. i = 0x5f3759df - (i>>1);
  52. y = *(float*)&i;
  53. y = y * (1.5f - (halfx * y * y));
  54. y = y * (1.5f - (halfx * y * y));
  55. return y;
  56. }
  57. void Mahony_update(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz)
  58. {
  59. float recipNorm;
  60. float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3;
  61. float hx, hy, bx, bz;
  62. float halfvx, halfvy, halfvz, halfwx, halfwy, halfwz;
  63. float halfex, halfey, halfez;
  64. float qa, qb, qc;
  65. // Convert gyroscope degrees/sec to radians/sec
  66. gx *= 0.0174533f;
  67. gy *= 0.0174533f;
  68. gz *= 0.0174533f;
  69. // Compute feedback only if accelerometer measurement valid
  70. // (avoids NaN in accelerometer normalisation)
  71. if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {
  72. // Normalise accelerometer measurement
  73. recipNorm = Mahony_invSqrt(ax * ax + ay * ay + az * az);
  74. ax *= recipNorm;
  75. ay *= recipNorm;
  76. az *= recipNorm;
  77. // Normalise magnetometer measurement
  78. recipNorm = Mahony_invSqrt(mx * mx + my * my + mz * mz);
  79. mx *= recipNorm;
  80. my *= recipNorm;
  81. mz *= recipNorm;
  82. // Auxiliary variables to avoid repeated arithmetic
  83. q0q0 = q0 * q0;
  84. q0q1 = q0 * q1;
  85. q0q2 = q0 * q2;
  86. q0q3 = q0 * q3;
  87. q1q1 = q1 * q1;
  88. q1q2 = q1 * q2;
  89. q1q3 = q1 * q3;
  90. q2q2 = q2 * q2;
  91. q2q3 = q2 * q3;
  92. q3q3 = q3 * q3;
  93. // Reference direction of Earth's magnetic field
  94. hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2));
  95. hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1));
  96. bx = sqrtf(hx * hx + hy * hy);
  97. bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2));
  98. // Estimated direction of gravity and magnetic field
  99. halfvx = q1q3 - q0q2;
  100. halfvy = q0q1 + q2q3;
  101. halfvz = q0q0 - 0.5f + q3q3;
  102. halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2);
  103. halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3);
  104. halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2);
  105. // Error is sum of cross product between estimated direction
  106. // and measured direction of field vectors
  107. halfex = (ay * halfvz - az * halfvy) + (my * halfwz - mz * halfwy);
  108. halfey = (az * halfvx - ax * halfvz) + (mz * halfwx - mx * halfwz);
  109. halfez = (ax * halfvy - ay * halfvx) + (mx * halfwy - my * halfwx);
  110. // Compute and apply integral feedback if enabled
  111. if(twoKiDef > 0.0f) {
  112. // integral error scaled by Ki
  113. integralFBx += twoKiDef * halfex * invSampleFreq;
  114. integralFBy += twoKiDef * halfey * invSampleFreq;
  115. integralFBz += twoKiDef * halfez * invSampleFreq;
  116. gx += integralFBx; // apply integral feedback
  117. gy += integralFBy;
  118. gz += integralFBz;
  119. } else {
  120. integralFBx = 0.0f; // prevent integral windup
  121. integralFBy = 0.0f;
  122. integralFBz = 0.0f;
  123. }
  124. // Apply proportional feedback
  125. gx += twoKpDef * halfex;
  126. gy += twoKpDef * halfey;
  127. gz += twoKpDef * halfez;
  128. }
  129. // Integrate rate of change of quaternion
  130. gx *= (0.5f * invSampleFreq); // pre-multiply common factors
  131. gy *= (0.5f * invSampleFreq);
  132. gz *= (0.5f * invSampleFreq);
  133. qa = q0;
  134. qb = q1;
  135. qc = q2;
  136. q0 += (-qb * gx - qc * gy - q3 * gz);
  137. q1 += (qa * gx + qc * gz - q3 * gy);
  138. q2 += (qa * gy - qb * gz + q3 * gx);
  139. q3 += (qa * gz + qb * gy - qc * gx);
  140. // Normalise quaternion
  141. recipNorm = Mahony_invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
  142. q0 *= recipNorm;
  143. q1 *= recipNorm;
  144. q2 *= recipNorm;
  145. q3 *= recipNorm;
  146. anglesComputed = 0;
  147. }
  148. void Mahony_computeAngles()
  149. {
  150. roll = atan2f(q0*q1 + q2*q3, 0.5f - q1*q1 - q2*q2);
  151. pitch = asinf(-2.0f * (q1*q3 - q0*q2));
  152. yaw = atan2f(q1*q2 + q0*q3, 0.5f - q2*q2 - q3*q3);
  153. anglesComputed = 1;
  154. }
  155. float getRoll() {
  156. if (!anglesComputed) Mahony_computeAngles();
  157. return roll * 57.29578f;
  158. }
  159. float getPitch() {
  160. if (!anglesComputed) Mahony_computeAngles();
  161. return pitch * 57.29578f;
  162. }
  163. float getYaw() {
  164. if (!anglesComputed) Mahony_computeAngles();
  165. return yaw * 57.29578f + 180.0f;
  166. }
  167. //============================================================================================
  168. // END OF CODE
  169. //============================================================================================