Skip to content

Commit 921ef00

Browse files
Fixes to make Gyro more stable
- Changed to using moving window average (8 samples every 5ms) without blocking.
1 parent ae39b2d commit 921ef00

2 files changed

Lines changed: 76 additions & 26 deletions

File tree

src/Gyro.cpp

Lines changed: 64 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ POP_NO_WARNINGS
1616
* */
1717

1818
bool Gyro::isPresent(false);
19+
float Gyro::_pitchSamples[WINDOW_SIZE] = {};
20+
float Gyro::_rollSamples[WINDOW_SIZE] = {};
21+
int Gyro::_ringIndex(0);
22+
int Gyro::_samplesCollected(0);
23+
unsigned long Gyro::_lastSampleTime(0);
1924

2025
void Gyro::startup()
2126
/* Starts up the MPU-6050 device.
@@ -54,6 +59,16 @@ void Gyro::startup()
5459
Wire.write(6); // 5Hz bandwidth (lowest) for smoothing
5560
Wire.endTransmission(true);
5661

62+
// Reset moving average state
63+
for (int i = 0; i < WINDOW_SIZE; i++)
64+
{
65+
_pitchSamples[i] = 0;
66+
_rollSamples[i] = 0;
67+
}
68+
_ringIndex = 0;
69+
_samplesCollected = 0;
70+
_lastSampleTime = 0;
71+
5772
LOG(DEBUG_INFO, "[GYRO]:: Started");
5873
}
5974

@@ -66,45 +81,69 @@ void Gyro::shutdown()
6681
// Nothing to do
6782
}
6883

84+
void Gyro::collectSample()
85+
/* Reads one accelerometer sample and stores it in the circular buffer.
86+
*/
87+
{
88+
// Execute 6 byte read from MPU6050_REG_ACCEL_XOUT_H
89+
Wire.beginTransmission(MPU6050_I2C_ADDR);
90+
Wire.write(MPU6050_REG_ACCEL_XOUT_H);
91+
Wire.endTransmission(false);
92+
Wire.requestFrom(MPU6050_I2C_ADDR, 6, 1); // Read 6 registers total, each axis value is stored in 2 registers
93+
int16_t AcX = Wire.read() << 8 | Wire.read(); // X-axis value
94+
int16_t AcY = Wire.read() << 8 | Wire.read(); // Y-axis value
95+
int16_t AcZ = Wire.read() << 8 | Wire.read(); // Z-axis value
96+
97+
// Calculating the Pitch angle (rotation around Y-axis)
98+
_pitchSamples[_ringIndex] = atan2f(-1.0f * AcX, sqrtf((float) AcY * AcY + (float) AcZ * AcZ)) * 180.0f / static_cast<float>(PI);
99+
// Calculating the Roll angle (rotation around X-axis)
100+
_rollSamples[_ringIndex] = atan2f(-1.0f * AcY, sqrtf((float) AcX * AcX + (float) AcZ * AcZ)) * 180.0f / static_cast<float>(PI);
101+
102+
_ringIndex = (_ringIndex + 1) % WINDOW_SIZE;
103+
if (_samplesCollected < WINDOW_SIZE)
104+
{
105+
_samplesCollected++;
106+
}
107+
}
108+
69109
angle_t Gyro::getCurrentAngles()
70110
/* Returns roll & tilt angles from MPU-6050 device in angle_t object in degrees.
71-
If MPU-6050 is not found then returns {0,0}.
111+
Non-blocking: collects one sample per call if at least SAMPLE_INTERVAL ms
112+
have elapsed since the last sample. Returns the moving average over the
113+
last WINDOW_SIZE samples.
72114
*/
73115
{
74-
const int windowSize = 16;
75-
// Read the accelerometer data
76-
struct angle_t result;
77-
result.pitchAngle = 0;
78-
result.rollAngle = 0;
116+
angle_t result;
117+
79118
if (!isPresent)
80-
return result; // Gyro is not available
119+
return result;
120+
121+
unsigned long now = millis();
122+
if (now - _lastSampleTime >= SAMPLE_INTERVAL)
123+
{
124+
_lastSampleTime = now;
125+
collectSample();
126+
}
81127

82-
for (int i = 0; i < windowSize; i++)
128+
if (_samplesCollected == 0)
129+
return result;
130+
131+
float pitchSum = 0;
132+
float rollSum = 0;
133+
for (int i = 0; i < _samplesCollected; i++)
83134
{
84-
// Execute 6 byte read from MPU6050_REG_WHO_AM_I
85-
Wire.beginTransmission(MPU6050_I2C_ADDR);
86-
Wire.write(MPU6050_REG_ACCEL_XOUT_H);
87-
Wire.endTransmission(false);
88-
Wire.requestFrom(MPU6050_I2C_ADDR, 6, 1); // Read 6 registers total, each axis value is stored in 2 registers
89-
int16_t AcX = Wire.read() << 8 | Wire.read(); // X-axis value
90-
int16_t AcY = Wire.read() << 8 | Wire.read(); // Y-axis value
91-
int16_t AcZ = Wire.read() << 8 | Wire.read(); // Z-axis value
92-
93-
// Calculating the Pitch angle (rotation around Y-axis)
94-
result.pitchAngle += atan2f(-1.0f * AcX, sqrtf((float) AcY * AcY + (float) AcZ * AcZ)) * 180.0f / static_cast<float>(PI);
95-
// Calculating the Roll angle (rotation around X-axis)
96-
result.rollAngle += atan2f(-1.0f * AcY, sqrtf((float) AcX * AcX + (float) AcZ * AcZ)) * 180.0f / static_cast<float>(PI);
97-
98-
delay(10); // Decorrelate measurements
135+
pitchSum += _pitchSamples[i];
136+
rollSum += _rollSamples[i];
99137
}
138+
result.pitchAngle = pitchSum / _samplesCollected;
139+
result.rollAngle = rollSum / _samplesCollected;
100140

101-
result.pitchAngle /= windowSize;
102-
result.rollAngle /= windowSize;
103141
#if GYRO_AXIS_SWAP == 1
104142
float temp = result.pitchAngle;
105143
result.pitchAngle = result.rollAngle;
106144
result.rollAngle = temp;
107145
#endif
146+
108147
return result;
109148
}
110149

src/Gyro.hpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class Gyro
1515
static float getCurrentTemperature();
1616

1717
private:
18+
static void collectSample();
19+
1820
// MPU6050 constants
1921
enum
2022
{
@@ -28,6 +30,15 @@ class Gyro
2830
MPU6050_REG_WHO_AM_I = 0x75
2931
};
3032

31-
static bool isPresent; // True if gyro correctly detected on startup
33+
// Use about 72 bytes of RAM for the moving average buffer
34+
static const int WINDOW_SIZE = 8; // Number of samples to keep for moving average
35+
static const unsigned long SAMPLE_INTERVAL = 5; // ms between samples
36+
37+
static bool isPresent;
38+
static float _pitchSamples[WINDOW_SIZE];
39+
static float _rollSamples[WINDOW_SIZE];
40+
static int _ringIndex;
41+
static int _samplesCollected; // Counts up to WINDOW_SIZE, then stays there
42+
static unsigned long _lastSampleTime;
3243
};
3344
#endif

0 commit comments

Comments
 (0)