Statistics
| Branch: | Tag: | Revision:

root / common / IMU.cpp @ 177eb1b610321312bf5141bd36952554e753559b

History | View | Annotate | Download (10.1 kB)

1
#include "IMU.h"
2
#include "Hardware.h"
3
4
#include <QObject>
5
#include <QDebug>
6
#include <QTime>
7
8
#include <math.h>     // sqrt, atan
9
10
11
#define Kp 2.0f       // proportional gain governs rate of convergence to accelerometer/magnetometer
12
#define Ki 0.005f     // integral gain governs rate of convergence of gyroscope biases
13
14
15
IMU::IMU(Hardware *hardware):
16
  QObject(), hardware(hardware), enabled(false),
17
  yaw(0), pitch(0), roll(0),
18
  q0(1), q1(0), q2(0), q3(0),
19
  exInt(0), eyInt(0), ezInt(0),
20
  lastUpdate(0), now(0),
21
  halfT(0)
22
{
23
  // FIXME: how to do this more nicely?
24
  accRaw[0]  = accRaw[1]  = accRaw[2]  = 0;
25
  gyroRaw[0] = gyroRaw[1] = gyroRaw[2] = 0;
26
  magnRaw[0] = magnRaw[1] = magnRaw[2] = 0;
27
28
  accRaw8bit[0]  = accRaw8bit[1]  = accRaw8bit[2]  = 0;
29
  gyroRaw8bit[0] = gyroRaw8bit[1] = gyroRaw8bit[2] = 0;
30
  magnRaw8bit[0] = magnRaw8bit[1] = magnRaw8bit[2] = 0;
31
32
  qDebug() << "In" << __FUNCTION__ << ", qX:" << q0 << q1 << q2 << q3;
33
34
}
35
36
37
38
IMU::~IMU()
39
{ 
40
  // Disable IMU, if still running
41
  if (enabled && !hardware->enableIMU(false)) {
42
        qWarning("Failed to stop IMU");
43
  }
44
}
45
46
47
48
bool IMU::enable(bool enable)
49
{
50
51
  qDebug() << "In" << __FUNCTION__ << ", Enable:" << enable;
52
53
  if (!hardware) {
54
        qWarning("No hardware plugin set!");
55
        return false;
56
  }
57
58
  // Enable IMU
59
  if (enable) {
60
61
        // Do nothing, if already enabled
62
        if (enabled) {
63
          return true;
64
        }
65
        
66
        if (!hardware->initIMU(this)) {
67
          qWarning("Failed to initialize IMU");
68
          return false;
69
        }
70
        
71
        hardware->enableIMU(true);
72
        enabled = true;
73
        return true;
74
  }
75
76
  // Disable IMU
77
78
  // Do nothing if already disabled
79
  if (!enabled) {
80
        return true;
81
  }
82
83
  if (!hardware->enableIMU(false)) {
84
        qWarning("Failed to stop IMU");
85
        return false;
86
  }
87
88
  enabled = false;
89
90
  return true;
91
}
92
93
94
95
double *IMU::getYawPitchRoll(void)
96
{
97
  qDebug() << "In" << __FUNCTION__;
98
99
  double *data, *dp;
100
101
  data = new double[3];
102
  dp = data;
103
104
  *dp++ = yaw;
105
  *dp++ = pitch;
106
  *dp++ = roll;
107
108
  return data;
109
}
110
111
112
113
114
QByteArray *IMU::get9DoFRaw(void)
115
{
116
  qDebug() << "In" << __FUNCTION__;
117
118
  QByteArray *data;
119
120
  // One byte for each 9DoF value
121
  data = new QByteArray(9, 0); 
122
  
123
  // Scale the raw values to 1 byte (gyro values from millidegrees/sec to degrees/sec)
124
  (*data)[0] = accRaw8bit[0];
125
  (*data)[1] = accRaw8bit[1];
126
  (*data)[2] = accRaw8bit[2];
127
  (*data)[3] = gyroRaw8bit[0];
128
  (*data)[4] = gyroRaw8bit[1];
129
  (*data)[5] = gyroRaw8bit[2];
130
  (*data)[6] = magnRaw8bit[0];
131
  (*data)[7] = magnRaw8bit[1];
132
  (*data)[8] = magnRaw8bit[2];
133
134
  return data;
135
}
136
137
138
139
void IMU::pushSensorData(quint8 raw8bit[9], double data[9])
140
{
141
142
  qDebug() << "In" << __FUNCTION__ << ", data: " << data[0] << data[1] << data[2] << data[3] << data[4] << data[5] << data[6] << data[7] << data[8];
143
144
  // Accelerometer data
145
  accRaw[0] = data[0];
146
  accRaw[1] = data[1];
147
  accRaw[2] = data[2];
148
149
  // Gyroscope data
150
  gyroRaw[0] = data[3];
151
  gyroRaw[1] = data[4];
152
  gyroRaw[2] = data[5];
153
154
  // Magnetometer data
155
  magnRaw[0] = data[6];
156
  magnRaw[1] = data[7];
157
  magnRaw[2] = data[8];
158
159
  // 8bit Accelerometer data
160
  accRaw8bit[0] = raw8bit[0];
161
  accRaw8bit[1] = raw8bit[1];
162
  accRaw8bit[2] = raw8bit[2];
163
164
  // 8bit Gyroscope data
165
  gyroRaw8bit[0] = raw8bit[3];
166
  gyroRaw8bit[1] = raw8bit[4];
167
  gyroRaw8bit[2] = raw8bit[5];
168
169
  // 8bit Magnetometer data
170
  magnRaw8bit[0] = raw8bit[6];
171
  magnRaw8bit[1] = raw8bit[7];
172
  magnRaw8bit[2] = raw8bit[8];
173
174
  doIMUCalc();
175
}
176
177
178
179
void IMU::pushYawPitchRoll(double yaw, double pitch, double roll)
180
{
181
  this->yaw   = yaw;
182
  this->pitch = pitch;
183
  this->roll  = roll;
184
}
185
186
187
188
/*
189
 * Update yaw/pitch/roll
190
 */
191
void IMU::doIMUCalc()
192
{
193
  //qDebug() << "In" << __FUNCTION__;
194
195
  // Update yaw/pitch/roll
196
  /*
197
   * Taken from FreeIMU: http://www.varesano.net/projects/hardware/FreeIMU
198
   */
199
  double q[4]; // quaternion
200
  double gx, gy, gz; // estimated gravity direction
201
  getQ(q);
202
203
  //qDebug() << "In" << __FUNCTION__ << ", q: " << q[0] << q[1] << q[2] << q[3];
204
205
  gx = 2 * (q[1]*q[3] - q[0]*q[2]);
206
  gy = 2 * (q[0]*q[1] + q[2]*q[3]);
207
  gz = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3];
208
209
  //qDebug() << "In" << __FUNCTION__ << ", gravity: " << gx << gy << gz;
210
211
  yaw = atan2(2 * q[1] * q[2] - 2 * q[0] * q[3], 2 * q[0]*q[0] + 2 * q[1] * q[1] - 1) * 180/M_PI;
212
  if (isnan(yaw)) {
213
        yaw = 0;
214
  }
215
  pitch = atan(gx / sqrt(gy*gy + gz*gz))  * 180/M_PI;
216
  if (isnan(pitch)) {
217
        pitch = 0;
218
  }
219
  roll = atan(gy / sqrt(gx*gx + gz*gz))  * 180/M_PI;
220
  if (isnan(roll)) {
221
        roll = 0;
222
  }
223
224
  qDebug() << "In" << __FUNCTION__ << ", yaw/pitch/roll:" << yaw << pitch << roll;
225
}
226
227
228
/*
229
 * Taken from FreeIMU: http://www.varesano.net/projects/hardware/FreeIMU
230
 */
231
//=====================================================================================================
232
// AHRS.c
233
// S.O.H. Madgwick
234
// 25th August 2010
235
//=====================================================================================================
236
// Description:
237
//
238
// Quaternion implementation of the 'DCM filter' [Mayhony et al].  Incorporates the magnetic distortion
239
// compensation algorithms from my filter [Madgwick] which eliminates the need for a reference
240
// direction of flux (bx bz) to be predefined and limits the effect of magnetic distortions to yaw
241
// axis only.
242
//
243
// User must define 'halfT' as the (sample period / 2), and the filter gains 'Kp' and 'Ki'.
244
//
245
// Global variables 'q0', 'q1', 'q2', 'q3' are the quaternion elements representing the estimated
246
// orientation.  See my report for an overview of the use of quaternions in this application.
247
//
248
// User must call 'AHRSupdate()' every sample period and parse calibrated gyroscope ('gx', 'gy', 'gz'),
249
// accelerometer ('ax', 'ay', 'ay') and magnetometer ('mx', 'my', 'mz') data.  Gyroscope units are
250
// radians/second, accelerometer and magnetometer units are irrelevant as the vector is normalised.
251
//
252
//=====================================================================================================
253
void IMU::AHRSupdate(double gx, double gy, double gz, double ax, double ay, double az, double mx, double my, double mz) {
254
  double norm;
255
  double hx, hy, hz, bx, bz;
256
  double vx, vy, vz, wx, wy, wz;
257
  double ex, ey, ez;
258
259
  //qDebug() << "In" << __FUNCTION__ << ", data: " << gx << gy << gz << ax << ay << az << mx << my << mz;
260
261
  //qDebug() << "In" << __FUNCTION__ << ", qX:" << q0 << q1 << q2 << q3;
262
263
  // auxiliary variables to reduce number of repeated operations
264
  double q0q0 = q0*q0;
265
  double q0q1 = q0*q1;
266
  double q0q2 = q0*q2;
267
  double q0q3 = q0*q3;
268
  double q1q1 = q1*q1;
269
  double q1q2 = q1*q2;
270
  double q1q3 = q1*q3;
271
  double q2q2 = q2*q2;   
272
  double q2q3 = q2*q3;
273
  double q3q3 = q3*q3;          
274
  
275
  //qDebug() << "In" << __FUNCTION__ << ", qXqY:" << q0q0 << q0q1 << q0q2 << q0q3 << q1q1 << q1q2 << q1q3 << q2q2 << q2q3 << q3q3;
276
277
  // normalise the measurements
278
  
279
  norm = sqrt(ax*ax + ay*ay + az*az);       
280
  if (norm == 0) {
281
        norm = 1;
282
  }
283
  ax = ax / norm;
284
  ay = ay / norm;
285
  az = az / norm;
286
287
  //qDebug() << "In" << __FUNCTION__ << ", acc:" << norm << ax << ay << az;
288
289
  // Get current time in milliseconds
290
  QTime time = QTime::currentTime();
291
  now = time.hour() * 3600 * 1000 + time.minute() * 60 * 1000 + time.second() * 1000 + time.msec();
292
293
  // Estimate the lastUpdate if running this for the first time
294
  if (lastUpdate == 0) {
295
        lastUpdate = now - 10;
296
  }
297
298
  // If midnight just passed by, calculate the correct halfT
299
  if (now < lastUpdate) {
300
        halfT = (((24 * 3600 * 1000) - lastUpdate) + now) / 2000.0;
301
  } else {
302
        halfT = (now - lastUpdate) / 2000.0;
303
  }
304
  lastUpdate = now;
305
306
  //qDebug() << "In" << __FUNCTION__ << ", halfT:" << halfT;
307
308
  norm = sqrt(mx*mx + my*my + mz*mz);          
309
  if (norm == 0) {
310
        norm = 1;
311
  }
312
  mx = mx / norm;
313
  my = my / norm;
314
  mz = mz / norm;
315
316
  //qDebug() << "In" << __FUNCTION__ << ", magn:" << mx << my << mz;
317
318
  // compute reference direction of flux
319
  hx = 2*mx*(0.5 - q2q2 - q3q3) + 2*my*(q1q2 - q0q3) + 2*mz*(q1q3 + q0q2);
320
  hy = 2*mx*(q1q2 + q0q3) + 2*my*(0.5 - q1q1 - q3q3) + 2*mz*(q2q3 - q0q1);
321
  hz = 2*mx*(q1q3 - q0q2) + 2*my*(q2q3 + q0q1) + 2*mz*(0.5 - q1q1 - q2q2);         
322
  bx = sqrt((hx*hx) + (hy*hy));
323
  bz = hz;     
324
325
  //qDebug() << "In" << __FUNCTION__ << ", direction of flux:" << hx << hy << hz;
326
  
327
  // estimated direction of gravity and flux (v and w)
328
  vx = 2*(q1q3 - q0q2);
329
  vy = 2*(q0q1 + q2q3);
330
  vz = q0q0 - q1q1 - q2q2 + q3q3;
331
  wx = 2*bx*(0.5 - q2q2 - q3q3) + 2*bz*(q1q3 - q0q2);
332
  wy = 2*bx*(q1q2 - q0q3) + 2*bz*(q0q1 + q2q3);
333
  wz = 2*bx*(q0q2 + q1q3) + 2*bz*(0.5 - q1q1 - q2q2);  
334
  
335
  //qDebug() << "In" << __FUNCTION__ << ", direction of grav:" << vx << vy << vz;
336
  //qDebug() << "In" << __FUNCTION__ << ", direction of flux:" << wx << wy << wz;
337
338
  // error is sum of cross product between reference direction of fields and direction measured by sensors
339
  ex = (ay*vz - az*vy) + (my*wz - mz*wy);
340
  ey = (az*vx - ax*vz) + (mz*wx - mx*wz);
341
  ez = (ax*vy - ay*vx) + (mx*wy - my*wx);
342
343
  //qDebug() << "In" << __FUNCTION__ << ", sensor error:" << ex << ey << ez;
344
  
345
  // integral error scaled integral gain
346
  exInt = exInt + ex*Ki;
347
  eyInt = eyInt + ey*Ki;
348
  ezInt = ezInt + ez*Ki;
349
  
350
  //qDebug() << "In" << __FUNCTION__ << ", integral error:" << exInt << eyInt << ezInt;
351
352
  // adjusted gyroscope measurements
353
  gx = gx + Kp*ex + exInt;
354
  gy = gy + Kp*ey + eyInt;
355
  gz = gz + Kp*ez + ezInt;
356
357
  //qDebug() << "In" << __FUNCTION__ << ", gyro:" << gx << gy << gz;
358
359
  // integrate quaternion rate and normalise
360
  q0 = q0 + (-q1*gx - q2*gy - q3*gz)*halfT;
361
  q1 = q1 + (q0*gx + q2*gz - q3*gy)*halfT;
362
  q2 = q2 + (q0*gy - q1*gz + q3*gx)*halfT;
363
  q3 = q3 + (q0*gz + q1*gy - q2*gx)*halfT;  
364
  
365
  //qDebug() << "In" << __FUNCTION__ << ", qX1:" << q0 << q1 << q2 << q3;
366
367
  // normalise quaternion
368
  norm = sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3);
369
  if (norm == 0) {
370
        norm = 1;
371
  }
372
  q0 = q0 / norm;
373
  q1 = q1 / norm;
374
  q2 = q2 / norm;
375
  q3 = q3 / norm;
376
377
  //qDebug() << "In" << __FUNCTION__ << ", qX2:" << q0 << q1 << q2 << q3;
378
}
379
380
381
382
/*
383
 * Taken from FreeIMU: http://www.varesano.net/projects/hardware/FreeIMU
384
 */
385
void IMU::getQ(double * q) {
386
  
387
  // gyro values are expressed in millidegrees/sec, the * M_PI/180 will convert it to radians/sec
388
  AHRSupdate((gyroRaw[0] / (double)1000) * M_PI/180,
389
                         (gyroRaw[1] / (double)1000) * M_PI/180,
390
                         (gyroRaw[2] / (double)1000) * M_PI/180,
391
                         accRaw[0], accRaw[1], accRaw[2],
392
                         magnRaw[0], magnRaw[1], magnRaw[2]);
393
  q[0] = q0;
394
  q[1] = q1;
395
  q[2] = q2;
396
  q[3] = q3;
397
}