Skip to content

Commit 4cd8d55

Browse files
author
Zhiquan Yeo
committed
AHRS Implementation
1 parent 97c0573 commit 4cd8d55

File tree

4 files changed

+249
-68
lines changed

4 files changed

+249
-68
lines changed

include/imu.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,24 @@ bool imuIsEnabled();
1616
void imuInit(uint8_t addr, TwoWire *theWire);
1717
void imuCalibrate(unsigned long calibrationTime);
1818

19-
bool imuPeriodic();
19+
void imuPeriodic();
20+
bool imuDataReady();
2021

21-
float gyroGetAngleX();
22-
float gyroGetRateX();
22+
float imuGetAccelX();
23+
float imuGetAccelY();
24+
float imuGetAccelZ();
2325

24-
float gyroGetAngleY();
25-
float gyroGetRateY();
26+
float imuGetGyroRateX();
27+
float imuGetGyroRateY();
28+
float imuGetGyroRateZ();
2629

27-
float gyroGetAngleZ();
28-
float gyroGetRateZ();
30+
float imuGetRoll();
31+
float imuGetPitch();
32+
float imuGetYaw();
33+
34+
void imuResetRoll();
35+
void imuResetPitch();
36+
void imuResetYaw();
2937

3038
void gyroReset();
3139

platformio.ini

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ lib_deps =
1212
Wire
1313
adafruit/Adafruit LSM6DS@^4.7.0
1414
adafruit/Adafruit BusIO@^1.14.1
15-
adafruit/Adafruit Unified Sensor@^1.1.9
15+
adafruit/Adafruit Unified Sensor@^1.1.9
16+
arduino-libraries/Madgwick@^1.2.0

src/imu.cpp

Lines changed: 225 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
#include "imu.h"
22

3+
#include <MadgwickAHRS.h>
4+
35
#define IMU_DEFAULT_CALIBRATION_TIME_MS 3000
46

7+
#define IMU_MADGWICK_LOOP_FREQ_HZ 25
8+
59
namespace xrp {
610

711
unsigned long _imuUpdatePeriod = 1000 / IMU_UPDATE_RATE_HZ;
@@ -12,14 +16,27 @@ bool _imuEnabled = false;
1216
unsigned long _lastIMUUpdateTime = 0;
1317
bool _imuOnePassComplete = false;
1418

15-
float _gyroOffsetsDegPerSec[3] = {0, 0, 0};
16-
float _gyroRatesDegPerSec[3] = {0, 0, 0};
19+
float _accelOffsetsG[3] = {0, 0, 0};
20+
float _accelG[3] = {0, 0, 0};
21+
22+
float _gyroOffsetsDPS[3] = {0, 0, 0};
23+
float _gyroRatesDPS[3] = {0, 0, 0};
1724
float _gyroAnglesDeg[3] = {0, 0, 0};
1825

26+
float _ahrsOffsets[3] = {0, 0, 0};
27+
28+
Madgwick _ahrsFilter;
29+
bool _filterStarted = false;
30+
unsigned long _microsPerReading, _microsPrevious;
31+
1932
float _radToDeg(float angleRad) {
2033
return angleRad * 180.0 / PI;
2134
}
2235

36+
float _accelToG(float accelMS2) {
37+
return accelMS2 / 9.80665;
38+
}
39+
2340
bool imuIsReady() {
2441
return _imuReady;
2542
}
@@ -96,6 +113,8 @@ void imuCalibrate(unsigned long calibrationTimeMs) {
96113
Serial.printf("[IMU] Beginning calibration. Running for %u ms\n", calibrationTimeMs);
97114

98115
float gyroAvgValues[3] = {0, 0, 0};
116+
float accelAvgValues[3] = {0, 0, 0};
117+
99118
int numVals = 0;
100119

101120
bool ledBlinkState = true;
@@ -119,7 +138,13 @@ void imuCalibrate(unsigned long calibrationTimeMs) {
119138
sensors_event_t temp;
120139

121140
_lsm6.getEvent(&accel, &gyro, &temp);
141+
142+
// Accelerometer averages
143+
accelAvgValues[0] += _accelToG(accel.acceleration.x);
144+
accelAvgValues[1] += _accelToG(accel.acceleration.y);
145+
accelAvgValues[2] += _accelToG(accel.acceleration.z);
122146

147+
// Gyro averages
123148
gyroAvgValues[0] += _radToDeg(gyro.gyro.x);
124149
gyroAvgValues[1] += _radToDeg(gyro.gyro.y);
125150
gyroAvgValues[2] += _radToDeg(gyro.gyro.z);
@@ -128,91 +153,235 @@ void imuCalibrate(unsigned long calibrationTimeMs) {
128153
delay(loopDelayTime);
129154
}
130155

131-
_gyroOffsetsDegPerSec[0] = gyroAvgValues[0] / numVals;
132-
_gyroOffsetsDegPerSec[1] = gyroAvgValues[1] / numVals;
133-
_gyroOffsetsDegPerSec[2] = gyroAvgValues[2] / numVals;
134-
135-
Serial.printf("[IMU] Offsets: X(%f) Y(%f) Z(%f)\n",
136-
_gyroOffsetsDegPerSec[0],
137-
_gyroOffsetsDegPerSec[1],
138-
_gyroOffsetsDegPerSec[2]);
156+
_accelOffsetsG[0] = accelAvgValues[0] / numVals;
157+
_accelOffsetsG[1] = accelAvgValues[1] / numVals;
158+
_accelOffsetsG[2] = accelAvgValues[2] / numVals;
159+
160+
_gyroOffsetsDPS[0] = gyroAvgValues[0] / numVals;
161+
_gyroOffsetsDPS[1] = gyroAvgValues[1] / numVals;
162+
_gyroOffsetsDPS[2] = gyroAvgValues[2] / numVals;
163+
164+
Serial.printf("[IMU] Gyro Offsets(dps): X(%f) Y(%f) Z(%f), Accel Offsets(g): X(%f) Y(%f) Z(%f)\n",
165+
_gyroOffsetsDPS[0],
166+
_gyroOffsetsDPS[1],
167+
_gyroOffsetsDPS[2],
168+
_accelOffsetsG[0],
169+
_accelOffsetsG[1],
170+
_accelOffsetsG[2]);
139171
Serial.println("[IMU] Calibration Complete");
140172

141173
digitalWrite(LED_BUILTIN, LOW);
142174
}
143175

144-
bool imuPeriodic() {
176+
void imuPeriodic() {
177+
// Initialize the filter if this is the first time we are running through the periodic
178+
if (!_filterStarted) {
179+
Serial.printf("[IMU] Starting Madgwick filter at %u hz\n", IMU_MADGWICK_LOOP_FREQ_HZ);
180+
_microsPerReading = 1000000 / IMU_MADGWICK_LOOP_FREQ_HZ;
181+
_microsPrevious = micros();
182+
_ahrsFilter.begin(IMU_MADGWICK_LOOP_FREQ_HZ);
183+
_filterStarted = true;
184+
return;
185+
}
186+
187+
unsigned long microsNow = micros();
188+
if (microsNow - _microsPrevious >= _microsPerReading) {
189+
// Read data
190+
sensors_event_t accel;
191+
sensors_event_t gyro;
192+
sensors_event_t temp;
193+
194+
_lsm6.getEvent(&accel, &gyro, &temp);
195+
196+
_gyroRatesDPS[0] = _radToDeg(gyro.gyro.x) - _gyroOffsetsDPS[0];
197+
_gyroRatesDPS[1] = _radToDeg(gyro.gyro.y) - _gyroOffsetsDPS[1];
198+
_gyroRatesDPS[2] = _radToDeg(gyro.gyro.z) - _gyroOffsetsDPS[2];
199+
200+
_accelG[0] = _accelToG(accel.acceleration.x) - _accelOffsetsG[0];
201+
_accelG[1] = _accelToG(accel.acceleration.y) - _accelOffsetsG[1];
202+
_accelG[2] = _accelToG(accel.acceleration.z) - _accelOffsetsG[2];
203+
204+
// Update the filter, which will compute orientation
205+
_ahrsFilter.updateIMU(_accelG[0], _accelG[1], _accelG[2], _gyroRatesDPS[0], _gyroRatesDPS[1], _gyroRatesDPS[2]);
206+
207+
// Increment the previous time so that we keep proper pace
208+
_microsPrevious = _microsPrevious + _microsPerReading;
209+
}
210+
}
211+
212+
/**
213+
* Determine if we have data ready to send upstream
214+
*/
215+
bool imuDataReady() {
145216
if (!_imuReady) return false;
146217
if (!_imuEnabled) return false;
147218

148219
unsigned long currTime = millis();
149220
unsigned long dt = currTime - _lastIMUUpdateTime;
150221

151-
// Send the IMU data at the specified IMU_UPDATE_RATE_HZ
152222
if (dt < _imuUpdatePeriod) return false;
153223

154-
// Get IMU data
155-
sensors_event_t accel;
156-
sensors_event_t gyro;
157-
sensors_event_t temp;
158-
159-
_lsm6.getEvent(&accel, &gyro, &temp);
160-
161-
float rateX = _radToDeg(gyro.gyro.x);
162-
float rateY = _radToDeg(gyro.gyro.y);
163-
float rateZ = _radToDeg(gyro.gyro.z);
164-
_gyroRatesDegPerSec[0] = rateX - _gyroOffsetsDegPerSec[0];
165-
_gyroRatesDegPerSec[1] = rateY - _gyroOffsetsDegPerSec[1];
166-
_gyroRatesDegPerSec[2] = rateZ - _gyroOffsetsDegPerSec[2];
167-
168-
// We can't integrate with only a single value, so bail out
169-
if (!_imuOnePassComplete) {
170-
_imuOnePassComplete = true;
171-
_lastIMUUpdateTime = currTime;
172-
return false;
173-
}
224+
_lastIMUUpdateTime = currTime;
225+
return true;
226+
}
227+
228+
// bool imuPeriodic() {
229+
// if (!_imuReady) return false;
230+
// if (!_imuEnabled) return false;
231+
232+
// unsigned long currTime = millis();
233+
// unsigned long dt = currTime - _lastIMUUpdateTime;
234+
235+
// // Send the IMU data at the specified IMU_UPDATE_RATE_HZ
236+
// if (dt < _imuUpdatePeriod) return false;
237+
238+
// // Get IMU data
239+
// sensors_event_t accel;
240+
// sensors_event_t gyro;
241+
// sensors_event_t temp;
242+
243+
// _lsm6.getEvent(&accel, &gyro, &temp);
244+
245+
// float rateX = _radToDeg(gyro.gyro.x);
246+
// float rateY = _radToDeg(gyro.gyro.y);
247+
// float rateZ = _radToDeg(gyro.gyro.z);
248+
// _gyroRatesDPS[0] = rateX - _gyroOffsetsDPS[0];
249+
// _gyroRatesDPS[1] = rateY - _gyroOffsetsDPS[1];
250+
// _gyroRatesDPS[2] = rateZ - _gyroOffsetsDPS[2];
251+
252+
// // We can't integrate with only a single value, so bail out
253+
// if (!_imuOnePassComplete) {
254+
// _imuOnePassComplete = true;
255+
// _lastIMUUpdateTime = currTime;
256+
// return false;
257+
// }
174258

175-
float dtInSeconds = dt / 1000.0;
259+
// float dtInSeconds = dt / 1000.0;
260+
261+
// for (int i = 0; i < 3; i++) {
262+
// _gyroAnglesDeg[i] = _gyroAnglesDeg[i] + (_gyroRatesDPS[i] * dtInSeconds);
263+
// }
264+
265+
// _lastIMUUpdateTime = currTime;
266+
// return true;
267+
// }
268+
269+
// ===============================
270+
// AHRS Values
271+
// ===============================
272+
273+
/**
274+
* Get acceleration in the X axis
275+
*
276+
* @return Acceleration in X (in G)
277+
*/
278+
float imuGetAccelX() {
279+
return _accelG[0];
280+
}
176281

177-
for (int i = 0; i < 3; i++) {
178-
_gyroAnglesDeg[i] = _gyroAnglesDeg[i] + (_gyroRatesDegPerSec[i] * dtInSeconds);
179-
}
282+
/**
283+
* Get acceleration in the Y axis
284+
*
285+
* @return Acceleration in Y (in G)
286+
*/
287+
float imuGetAccelY() {
288+
return _accelG[1];
289+
}
180290

181-
_lastIMUUpdateTime = currTime;
182-
return true;
291+
/**
292+
* Get acceleration in the Z axis
293+
*
294+
* @return Acceleration in Z (in G)
295+
*/
296+
float imuGetAccelZ() {
297+
return _accelG[2];
183298
}
184299

185-
float gyroGetAngleX() {
186-
return _gyroAnglesDeg[0];
300+
/**
301+
* Get gyro rate in the X axis
302+
*
303+
* @return Gyro rate in X (in DPS)
304+
*/
305+
float imuGetGyroRateX() {
306+
return _gyroRatesDPS[0];
187307
}
188308

189-
float gyroGetRateX() {
190-
return _gyroRatesDegPerSec[0];
309+
/**
310+
* Get gyro rate in the Y axis
311+
*
312+
* @return Gyro rate in Y (in DPS)
313+
*/
314+
float imuGetGyroRateY() {
315+
return _gyroRatesDPS[1];
191316
}
192317

193-
float gyroGetAngleY() {
194-
return _gyroAnglesDeg[1];
318+
/**
319+
* Get gyro rate in the Z axis
320+
*
321+
* @return Gyro rate in Z (in DPS)
322+
*/
323+
float imuGetGyroRateZ() {
324+
return _gyroRatesDPS[2];
195325
}
196326

197-
float gyroGetRateY() {
198-
return _gyroRatesDegPerSec[1];
327+
/**
328+
* Get current roll angle
329+
*
330+
* @return Current roll angle (in degrees)
331+
*/
332+
float imuGetRoll() {
333+
return _ahrsFilter.getRoll() - _ahrsOffsets[0];
199334
}
200335

201-
float gyroGetAngleZ() {
202-
return _gyroAnglesDeg[2];
336+
/**
337+
* Get current pitch angle
338+
*
339+
* @return Current pitch angle (in degrees)
340+
*/
341+
float imuGetPitch() {
342+
return _ahrsFilter.getPitch() - _ahrsOffsets[1];
203343
}
204344

205-
float gyroGetRateZ() {
206-
return _gyroRatesDegPerSec[2];
345+
/**
346+
* Get current yaw angle
347+
*
348+
* @return Current yaw angle (in degrees)
349+
*/
350+
float imuGetYaw() {
351+
return _ahrsFilter.getYaw() - _ahrsOffsets[2];
207352
}
208353

209-
void gyroReset() {
210-
for (int i = 0; i < 3; i++) {
211-
_gyroRatesDegPerSec[i] = 0;
212-
_gyroAnglesDeg[i] = 0;
213-
}
354+
/**
355+
* Reset the roll angle.
356+
*
357+
* The AHRS filter always runs, so this basically sets an offset value
358+
*/
359+
void imuResetRoll() {
360+
_ahrsOffsets[0] = _ahrsFilter.getRoll();
361+
}
362+
363+
/**
364+
* Reset the pitch angle.
365+
*
366+
* The AHRS filter always runs, so this basically sets an offset value
367+
*/
368+
void imuResetPitch() {
369+
_ahrsOffsets[1] = _ahrsFilter.getPitch();
370+
}
214371

215-
_imuOnePassComplete = false;
372+
/**
373+
* Reset the yaw angle.
374+
*
375+
* The AHRS filter always runs, so this basically sets an offset value
376+
*/
377+
void imuResetYaw() {
378+
_ahrsOffsets[2] = _ahrsFilter.getYaw();
379+
}
380+
381+
void gyroReset() {
382+
imuResetRoll();
383+
imuResetPitch();
384+
imuResetYaw();
216385
}
217386

218387
} // namespace xrp

0 commit comments

Comments
 (0)