Skip to content

Commit 2d49c15

Browse files
committed
Added MadgwickAHRS
1 parent b748789 commit 2d49c15

File tree

4 files changed

+347
-0
lines changed

4 files changed

+347
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
idf_component_register(SRCS "MadgwickAHRS.cpp"
2+
PRIV_REQUIRES esp_timer
3+
INCLUDE_DIRS ".")
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
//=============================================================================================
2+
// MadgwickAHRS.c
3+
//=============================================================================================
4+
//
5+
// Implementation of Madgwick's IMU and AHRS algorithms.
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+
// 19/02/2012 SOH Madgwick Magnetometer measurement is normalised
16+
//
17+
//=============================================================================================
18+
19+
//-------------------------------------------------------------------------------------------
20+
// Header files
21+
22+
#include "MadgwickAHRS.h"
23+
#include <math.h>
24+
25+
//-------------------------------------------------------------------------------------------
26+
// Definitions
27+
28+
#define sampleFreqDef 512.0f // sample frequency in Hz
29+
#define betaDef 0.1f // 2 * proportional gain
30+
31+
32+
//============================================================================================
33+
// Functions
34+
35+
//-------------------------------------------------------------------------------------------
36+
// AHRS algorithm update
37+
38+
Madgwick::Madgwick() {
39+
beta = betaDef;
40+
q0 = 1.0f;
41+
q1 = 0.0f;
42+
q2 = 0.0f;
43+
q3 = 0.0f;
44+
invSampleFreq = 1.0f / sampleFreqDef;
45+
anglesComputed = 0;
46+
}
47+
48+
void Madgwick::update(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, float dt) {
49+
float recipNorm;
50+
float s0, s1, s2, s3;
51+
float qDot1, qDot2, qDot3, qDot4;
52+
float hx, hy;
53+
float _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3;
54+
55+
// Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation)
56+
if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) {
57+
updateIMU(gx, gy, gz, ax, ay, az, dt);
58+
return;
59+
}
60+
61+
// Convert gyroscope degrees/sec to radians/sec
62+
gx *= 0.0174533f;
63+
gy *= 0.0174533f;
64+
gz *= 0.0174533f;
65+
66+
// Rate of change of quaternion from gyroscope
67+
qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz);
68+
qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy);
69+
qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx);
70+
qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx);
71+
72+
// Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
73+
if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {
74+
75+
// Normalise accelerometer measurement
76+
recipNorm = invSqrt(ax * ax + ay * ay + az * az);
77+
ax *= recipNorm;
78+
ay *= recipNorm;
79+
az *= recipNorm;
80+
81+
// Normalise magnetometer measurement
82+
recipNorm = invSqrt(mx * mx + my * my + mz * mz);
83+
mx *= recipNorm;
84+
my *= recipNorm;
85+
mz *= recipNorm;
86+
87+
// Auxiliary variables to avoid repeated arithmetic
88+
_2q0mx = 2.0f * q0 * mx;
89+
_2q0my = 2.0f * q0 * my;
90+
_2q0mz = 2.0f * q0 * mz;
91+
_2q1mx = 2.0f * q1 * mx;
92+
_2q0 = 2.0f * q0;
93+
_2q1 = 2.0f * q1;
94+
_2q2 = 2.0f * q2;
95+
_2q3 = 2.0f * q3;
96+
_2q0q2 = 2.0f * q0 * q2;
97+
_2q2q3 = 2.0f * q2 * q3;
98+
q0q0 = q0 * q0;
99+
q0q1 = q0 * q1;
100+
q0q2 = q0 * q2;
101+
q0q3 = q0 * q3;
102+
q1q1 = q1 * q1;
103+
q1q2 = q1 * q2;
104+
q1q3 = q1 * q3;
105+
q2q2 = q2 * q2;
106+
q2q3 = q2 * q3;
107+
q3q3 = q3 * q3;
108+
109+
// Reference direction of Earth's magnetic field
110+
hx = mx * q0q0 - _2q0my * q3 + _2q0mz * q2 + mx * q1q1 + _2q1 * my * q2 + _2q1 * mz * q3 - mx * q2q2 - mx * q3q3;
111+
hy = _2q0mx * q3 + my * q0q0 - _2q0mz * q1 + _2q1mx * q2 - my * q1q1 + my * q2q2 + _2q2 * mz * q3 - my * q3q3;
112+
_2bx = sqrtf(hx * hx + hy * hy);
113+
_2bz = -_2q0mx * q2 + _2q0my * q1 + mz * q0q0 + _2q1mx * q3 - mz * q1q1 + _2q2 * my * q3 - mz * q2q2 + mz * q3q3;
114+
_4bx = 2.0f * _2bx;
115+
_4bz = 2.0f * _2bz;
116+
117+
// Gradient decent algorithm corrective step
118+
s0 = -_2q2 * (2.0f * q1q3 - _2q0q2 - ax) + _2q1 * (2.0f * q0q1 + _2q2q3 - ay) - _2bz * q2 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q3 + _2bz * q1) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q2 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
119+
s1 = _2q3 * (2.0f * q1q3 - _2q0q2 - ax) + _2q0 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q1 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + _2bz * q3 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q2 + _2bz * q0) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q3 - _4bz * q1) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
120+
s2 = -_2q0 * (2.0f * q1q3 - _2q0q2 - ax) + _2q3 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q2 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + (-_4bx * q2 - _2bz * q0) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q1 + _2bz * q3) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q0 - _4bz * q2) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
121+
s3 = _2q1 * (2.0f * q1q3 - _2q0q2 - ax) + _2q2 * (2.0f * q0q1 + _2q2q3 - ay) + (-_4bx * q3 + _2bz * q1) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q0 + _2bz * q2) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q1 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
122+
recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude
123+
s0 *= recipNorm;
124+
s1 *= recipNorm;
125+
s2 *= recipNorm;
126+
s3 *= recipNorm;
127+
128+
// Apply feedback step
129+
qDot1 -= beta * s0;
130+
qDot2 -= beta * s1;
131+
qDot3 -= beta * s2;
132+
qDot4 -= beta * s3;
133+
}
134+
135+
// Integrate rate of change of quaternion to yield quaternion
136+
//q0 += qDot1 * invSampleFreq;
137+
//q1 += qDot2 * invSampleFreq;
138+
//q2 += qDot3 * invSampleFreq;
139+
//q3 += qDot4 * invSampleFreq;
140+
q0 += qDot1 * dt;
141+
q1 += qDot2 * dt;
142+
q2 += qDot3 * dt;
143+
q3 += qDot4 * dt;
144+
145+
// Normalise quaternion
146+
recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
147+
q0 *= recipNorm;
148+
q1 *= recipNorm;
149+
q2 *= recipNorm;
150+
q3 *= recipNorm;
151+
anglesComputed = 0;
152+
}
153+
154+
//-------------------------------------------------------------------------------------------
155+
// IMU algorithm update
156+
157+
void Madgwick::updateIMU(float gx, float gy, float gz, float ax, float ay, float az, float dt) {
158+
float recipNorm;
159+
float s0, s1, s2, s3;
160+
float qDot1, qDot2, qDot3, qDot4;
161+
float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3;
162+
163+
// Convert gyroscope degrees/sec to radians/sec
164+
gx *= 0.0174533f;
165+
gy *= 0.0174533f;
166+
gz *= 0.0174533f;
167+
168+
// Rate of change of quaternion from gyroscope
169+
qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz);
170+
qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy);
171+
qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx);
172+
qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx);
173+
174+
// Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
175+
if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {
176+
177+
// Normalise accelerometer measurement
178+
recipNorm = invSqrt(ax * ax + ay * ay + az * az);
179+
ax *= recipNorm;
180+
ay *= recipNorm;
181+
az *= recipNorm;
182+
183+
// Auxiliary variables to avoid repeated arithmetic
184+
_2q0 = 2.0f * q0;
185+
_2q1 = 2.0f * q1;
186+
_2q2 = 2.0f * q2;
187+
_2q3 = 2.0f * q3;
188+
_4q0 = 4.0f * q0;
189+
_4q1 = 4.0f * q1;
190+
_4q2 = 4.0f * q2;
191+
_8q1 = 8.0f * q1;
192+
_8q2 = 8.0f * q2;
193+
q0q0 = q0 * q0;
194+
q1q1 = q1 * q1;
195+
q2q2 = q2 * q2;
196+
q3q3 = q3 * q3;
197+
198+
// Gradient decent algorithm corrective step
199+
s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay;
200+
s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az;
201+
s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az;
202+
s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay;
203+
recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude
204+
s0 *= recipNorm;
205+
s1 *= recipNorm;
206+
s2 *= recipNorm;
207+
s3 *= recipNorm;
208+
209+
// Apply feedback step
210+
qDot1 -= beta * s0;
211+
qDot2 -= beta * s1;
212+
qDot3 -= beta * s2;
213+
qDot4 -= beta * s3;
214+
}
215+
216+
// Integrate rate of change of quaternion to yield quaternion
217+
//q0 += qDot1 * invSampleFreq;
218+
//q1 += qDot2 * invSampleFreq;
219+
//q2 += qDot3 * invSampleFreq;
220+
//q3 += qDot4 * invSampleFreq;
221+
q0 += qDot1 * dt;
222+
q1 += qDot2 * dt;
223+
q2 += qDot3 * dt;
224+
q3 += qDot4 * dt;
225+
226+
// Normalise quaternion
227+
recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
228+
q0 *= recipNorm;
229+
q1 *= recipNorm;
230+
q2 *= recipNorm;
231+
q3 *= recipNorm;
232+
anglesComputed = 0;
233+
}
234+
235+
//-------------------------------------------------------------------------------------------
236+
// Fast inverse square-root
237+
// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root
238+
239+
float Madgwick::invSqrt(float x) {
240+
float halfx = 0.5f * x;
241+
float y = x;
242+
long i = *(long*)&y;
243+
i = 0x5f3759df - (i>>1);
244+
y = *(float*)&i;
245+
y = y * (1.5f - (halfx * y * y));
246+
y = y * (1.5f - (halfx * y * y));
247+
return y;
248+
}
249+
250+
//-------------------------------------------------------------------------------------------
251+
252+
void Madgwick::computeAngles()
253+
{
254+
roll = atan2f(q0*q1 + q2*q3, 0.5f - q1*q1 - q2*q2);
255+
pitch = asinf(-2.0f * (q1*q3 - q0*q2));
256+
yaw = atan2f(q1*q2 + q0*q3, 0.5f - q2*q2 - q3*q3);
257+
anglesComputed = 1;
258+
}
259+
260+
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//=============================================================================================
2+
// MadgwickAHRS.h
3+
//=============================================================================================
4+
//
5+
// Implementation of Madgwick's IMU and AHRS algorithms.
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+
//=============================================================================================
17+
#ifndef MadgwickAHRS_h
18+
#define MadgwickAHRS_h
19+
#include <math.h>
20+
21+
//--------------------------------------------------------------------------------------------
22+
// Variable declaration
23+
class Madgwick{
24+
private:
25+
static float invSqrt(float x);
26+
float beta; // algorithm gain
27+
float q0;
28+
float q1;
29+
float q2;
30+
float q3; // quaternion of sensor frame relative to auxiliary frame
31+
float invSampleFreq;
32+
float roll;
33+
float pitch;
34+
float yaw;
35+
char anglesComputed;
36+
void computeAngles();
37+
38+
//-------------------------------------------------------------------------------------------
39+
// Function declarations
40+
public:
41+
Madgwick(void);
42+
void begin(float sampleFrequency) { invSampleFreq = 1.0f / sampleFrequency; }
43+
void update(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, float dt);
44+
void updateIMU(float gx, float gy, float gz, float ax, float ay, float az, float dt);
45+
//float getPitch(){return atan2f(2.0f * q2 * q3 - 2.0f * q0 * q1, 2.0f * q0 * q0 + 2.0f * q3 * q3 - 1.0f);};
46+
//float getRoll(){return -1.0f * asinf(2.0f * q1 * q3 + 2.0f * q0 * q2);};
47+
//float getYaw(){return atan2f(2.0f * q1 * q2 - 2.0f * q0 * q3, 2.0f * q0 * q0 + 2.0f * q1 * q1 - 1.0f);};
48+
float getRoll() {
49+
if (!anglesComputed) computeAngles();
50+
return roll * 57.29578f;
51+
}
52+
float getPitch() {
53+
if (!anglesComputed) computeAngles();
54+
return pitch * 57.29578f;
55+
}
56+
float getYaw() {
57+
if (!anglesComputed) computeAngles();
58+
return yaw * 57.29578f + 180.0f;
59+
}
60+
float getRollRadians() {
61+
if (!anglesComputed) computeAngles();
62+
return roll;
63+
}
64+
float getPitchRadians() {
65+
if (!anglesComputed) computeAngles();
66+
return pitch;
67+
}
68+
float getYawRadians() {
69+
if (!anglesComputed) computeAngles();
70+
return yaw;
71+
}
72+
};
73+
#endif
74+

components/MadgwickAHRS/component.mk

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#
2+
# Main component makefile.
3+
#
4+
# This Makefile can be left empty. By default, it will take the sources in the
5+
# src/ directory, compile them and link them into lib(subdirectory_name).a
6+
# in the build directory. This behaviour is entirely configurable,
7+
# please read the ESP-IDF documents if you need to do this.
8+
#
9+
10+
COMPONENT_ADD_INCLUDEDIRS=.

0 commit comments

Comments
 (0)