-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathMpu.cpp
241 lines (198 loc) · 8.22 KB
/
Mpu.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
#include <mpu.h>
#define __PGMSPACE_H_ 1 // pgmsplace.h define PGMSPACE_INCLUDE instead of __PGMSPACE_H
#ifdef USE_V6_12
#include <MPU6050_6Axis_MotionApps_V6_12.h> // longer to init & bug with sensitivity
#else
#include <MPU6050_6Axis_MotionApps20.h>
#endif
//--------------------------------------
#ifdef MPU_GET_CORE
SemaphoreHandle_t OutputMutex = xSemaphoreCreateMutex();
EventGroupHandle_t FlagReady = xEventGroupCreate();
TaskHandle_t NotifyToCalibrate;
SensorOutput SharedOutput; // shared with update so that the mutex is barely taken by MPUComputeTask
void MPUComputeTask(void* _mpu)
{
MPU* mpu = (MPU*) _mpu;
SensorOutput taskOutput; //output to store computation
TickType_t lastWakeTime = xTaskGetTickCount();
for (;;) // forever
{
if(mpu->getFiFoPacket())
{
mpu->compute(taskOutput);
xSemaphoreTake(OutputMutex, portMAX_DELAY);
memcpy(&SharedOutput, &taskOutput, sizeof(SensorOutput));
xSemaphoreGive(OutputMutex);
xEventGroupSetBits(FlagReady, 1);
if(ulTaskNotifyTake(pdTRUE, 0)) // pool the the task semaphore
mpu->calibrate();
}
// should got a packet every 10ms
vTaskDelayUntil(&lastWakeTime, pdMS_TO_TICKS(10));
}
}
#endif
//--------------------------------------
void MPU::init()
{
// save calibration
#define AddOffset(var) AddVarHid(var, 0, -32768, 32767)
AddOffset(mXGyroOffset); AddOffset(mYGyroOffset); AddOffset(mZGyroOffset);
AddOffset(mXAccelOffset); AddOffset(mYAccelOffset); AddOffset(mZAccelOffset);
AddBoolNameHid("gotOffset", mGotOffset, false);
#ifdef MPU_GET_CORE
xTaskCreatePinnedToCore(MPUComputeTask, "mpuTask", MPU_GET_STACK, this, MPU_GET_PRIO, &NotifyToCalibrate, MPU_GET_CORE);
_log << _FMT(F("Mpu runs on Core % with Prio %"), MPU_GET_CORE, MPU_GET_PRIO) << endl;
AddCmd("calibrate", xTaskNotifyGive(NotifyToCalibrate) ) // trigger a calibration
#else
AddCmd("calibrate", calibrate() )
#endif
AddBoolName("auto", mAutoCalibrate, false);
AddVarName ("neutralAcc", mNeutralAcc, 60, 0, 300);
AddVarName ("maxAcc", mMaxAcc, 2000, 500, 8192);
AddVarName ("smoothAcc", mSmoothAcc, 1600, 1, 32767)
AddVarName ("neutralW", mNeutralW, 3000, 0, 32767);
AddVarName ("maxW", mMaxW, 7000, 0, 32767);
}
//--------------------------------------
void MPU::calibrate()
{
CalibrateAccel(CALIBRATION_LOOP);
CalibrateGyro(CALIBRATION_LOOP);
mXGyroOffset = getXGyroOffset(); mYGyroOffset = getYGyroOffset(); mZGyroOffset = getZGyroOffset();
mXAccelOffset = getXAccelOffset(); mYAccelOffset = getYAccelOffset(); mZAccelOffset = getZAccelOffset();
printOffsets(F("MPU calibrated"));
mGotOffset = true;
}
bool MPU::setOffsets()
{
_log << F("Get Offset...");
if (mGotOffset)
{
setXGyroOffset(mXGyroOffset); setYGyroOffset(mYGyroOffset); setZGyroOffset(mZGyroOffset);
setXAccelOffset(mXAccelOffset); setYAccelOffset(mYAccelOffset); setZAccelOffset(mZAccelOffset);
printOffsets(F("Got internal offsets"));
}
else
_log << endl;
return mGotOffset;
}
void MPU::printOffsets(const __FlashStringHelper* txt)
{
_log << txt << endl;
_log << F("Acc Offset: ") << SpaceIt(_WIDTH(getXAccelOffset(), 6), _WIDTH(getYAccelOffset(), 6), _WIDTH(getZAccelOffset(), 6)) << endl;
_log << F("Gyr Offset: ") << SpaceIt(_WIDTH(getXGyroOffset(), 6), _WIDTH(getYGyroOffset(), 6), _WIDTH(getZGyroOffset(), 6)) << endl;
}
//--------------------------------------
void MPU::begin()
{
mHasBegun = true;
Wire.begin(SDA, SCL, I2C_CLOCK);
initialize(); reset(); resetI2CMaster(); //help with startup reliabilily
_log << F("MPU connection...") << (testConnection() ? F("successful") : F("failed")) << endl;
uint8_t devStatus = dmpInitialize();
if (devStatus == 0) // did it work ?
{
if(!mAutoCalibrate || !setOffsets())
calibrate();
mFifoBuffer = (uint8_t* )malloc(dmpGetFIFOPacketSize() * sizeof(uint8_t)); // FIFO storage buffer
assert (mFifoBuffer!=nullptr);
setDMPEnabled(true);
mDmpReady = true;
}
else // error
{
const __FlashStringHelper* error = devStatus == 1 ? F("initial memory load") : (devStatus == 2 ? F("DMP configuration updates") : F("unknown"));
_log << _FMT(F("DMP ERROR #% : % failure"), devStatus, error) << endl;
}
}
//--------------------------------------
void MPU::getAxiSAngle(VectorInt16& v, int& angle, Quaternion& q)
{
if (q.w > 1) q.normalize(); // needs q.w < 1 for acos and sqrt
angle = acos(q.w) * 2 * 318.; // 999 / PI
float s = sqrt(1 - q.w * q.w);
if (s < 0.001) // div 0
{
v.x = 1; v.y = v.z = 0;
}
else
{
float n = 255. / s;
v.x = q.x * n; v.y = q.y * n; v.z = q.z * n;
}
}
//--------------------------------------
bool MPU::getFiFoPacket()
{
ulong dt = micros() - mT; // best place to get the actual dt if called right after the delay function
if (mDmpReady && dmpGetCurrentFIFOPacket(mFifoBuffer))
{
mdt = dt;
mT += dt;
return true;
}
return false;
}
//--------------------------------------
inline int thresh(int v, uint16_t t) { return v > 0 ? max(0, v - t) : min(v + t, 0); }
inline int16_t stayshort(int v) { return constrain(v, -32768, 32767); }
inline int16_t staybyte(int16_t v) { return constrain(v, -255, 255); }
inline void shiftrVector(VectorInt16 &v, byte n) { v.x = v.x >> n; v.y = v.y >> n; v.z = v.z >> n; }
void MPU::compute(SensorOutput& output)
{
// measures
dmpGetQuaternion(&mQuat, mFifoBuffer);
dmpGetGyro(&mW, mFifoBuffer);
dmpGetAccel(&mAcc, mFifoBuffer);
#ifdef USE_V6_12
// fix sensibility bug in MPU6050_6Axis_MotionApps_V6_12.h
shiftrVector(mW, 2)
shiftrVector(mAcc, 1)
#endif
// gravity & corrected accel
dmpGetGravity(&mGrav, &mQuat);
dmpGetLinearAccel(&mAccReal, &mAcc, &mGrav); // remove measured grav
// smooth acc & gyro
uint16_t smooth = - int(pow(1. - ACCEL_AVG, mdt * ACCEL_BASE_FREQ * .000001) * 65536.); // 1 - (1-accel_avg) ^ (dt * 60 / 1000 000) using fract16
mAccY = lerp15by16(mAccY, stayshort(mAccReal.y), smooth);
mWZ = lerp15by16(mWZ, stayshort(mW.z * -655), smooth);
// output
getAxiSAngle(output.axis, output.angle, mQuat);
// int16_t acc = stayshort(thresh(mAccY / mDivAcc, mNeutralAcc) << 8);
// mAccYsmooth = acc * mAccYsmooth < 0 || abs(acc) > abs(mAccYsmooth) ? acc : lerp15by16(mAccYsmooth, acc, mSmoothAcc);
// output.acc = staybyte(mAccYsmooth >> 7);
// _log << mAccY << " " << mMaxAcc << " " << thresh(mAccY, mNeutralAcc) << " " << constrain(thresh(mAccY, mNeutralAcc),-mMaxAcc, mMaxAcc);
int16_t acc = map(constrain(thresh(mAccY, mNeutralAcc),-mMaxAcc, mMaxAcc), -mMaxAcc, mMaxAcc, -32767, 32767);
mAccYsmooth = acc * mAccYsmooth < 0 || abs(acc) > abs(mAccYsmooth) ? acc : lerp15by16(mAccYsmooth, acc, mSmoothAcc);
output.acc = mAccYsmooth >> 7;
output.w = staybyte((thresh(mWZ, mNeutralW) << 8) / mMaxW);
output.updated = true;
#ifdef MPU_DBG
_log << "[ dt " << _WIDTH(mdt * .001, 6) << "ms - smooth " << _WIDTH(smooth / 65536., 6) << "] ";
_log << "[ smooth acc " << _WIDTH(acc, 6) << " " << _WIDTH(mAccYsmooth, 6) << " - smooth w " << _WIDTH(mWZ, 6) << "] ";
_log << "[ ouput acc " << _WIDTH(output.acc, 4) << " - ouput w " << _WIDTH(output.w, 4) << "] ";
_log << "[ grav " << SpaceIt( _WIDTH(mGrav.x, 5), _WIDTH(mGrav.y, 5), _WIDTH(mGrav.z, 5)) << "] ";
_log << "[ gyr " << SpaceIt( _WIDTH(mW.x, 4), _WIDTH(mW.y, 4), _WIDTH(mW.z, 4)) << "] ";
_log << "[ acc " << SpaceIt( _WIDTH(mAcc.x, 6), _WIDTH(mAcc.y, 6), _WIDTH(mAcc.z, 6)) << "] ";
_log << "[ real " << SpaceIt( _WIDTH(mAccReal.x, 6), _WIDTH(mAccReal.y, 6), _WIDTH(mAccReal.z, 6)) << "] ";
_log << endl;
#endif
}
//--------------------------------------
void MPU::update()
{
if (!mHasBegun)
begin();
#ifdef MPU_GET_CORE
if (xEventGroupGetBits(FlagReady) && xSemaphoreTake(OutputMutex, 0) == pdTRUE) // pool the mpuTask
{
memcpy(&mOutput, &SharedOutput, sizeof(SensorOutput));
xSemaphoreGive(OutputMutex); // release the mutex after measures have been copied
}
#else
if (getFiFoPacket())
compute(mOutput);
#endif
}