DIT168 Group9  0.9
Miniature remote-controlled vehicle using OpenDavinci and Libcluon
IMU.cpp
1 //
2 // Created by Firas Cheaib on 23-Apr-18.
3 // Adapted from openKorp's IMU implementation
4 // Comments and code modified with permission
5 //
6 
7 #include <iostream>
8 #include <linux/i2c-dev.h>
9 #include <sys/ioctl.h>
10 #include <fcntl.h>
11 #include <unistd.h>
12 #include <cmath>
13 #include "IMU.hpp"
14 
15 int main(int argc, char** argv) {
16  int returnValue = 0;
17  auto commandlineArguments = cluon::getCommandlineArguments(argc, argv);
18 
19  if (0 == commandlineArguments.count("dev") || 0 == commandlineArguments.count("freq") ||
20  0 == commandlineArguments.count("cid") || 0 == commandlineArguments.count("verbose")) {
21  std::cerr << "argv[0] reads inputs from the IMU Sensors and transmits them to the car's components."
22  << std::endl;
23  std::cerr << "Usage: " << argv[0] << " --dev=<path toIMU> --freq=<int Frequency> --cid=<OD4Session Session>"
24  << std::endl;
25  std::cerr << "Example: " << argv[0] << " --dev=/dev/i2c-2 --freq=100 --cid=200 " << std::endl;
26  returnValue = 1;
27  }
28  else {
29  const std::string DEV = commandlineArguments["dev"];
30  std::cout << DEV << std::endl;
31  const uint16_t CID = (uint16_t) std::stoi(commandlineArguments["cid"]);
32  const float FREQ = std::stof(commandlineArguments["freq"]);
33  const bool VERBOSE = commandlineArguments.count("verbose") != 0;
34 
35  int16_t deviceFile = open(DEV.c_str(), O_RDWR);
36 
37  if (deviceFile < 0) {
38  std::cerr << "Failed to open the i2c bus." << std::endl;
39  return 1;
40  }
41 
42  initializeMpu(deviceFile);
43  initializeAK8963(deviceFile);
44  setGyroOffset(calibrateMPU9250(deviceFile), deviceFile);
45 
46  cluon::OD4Session od4{CID};
47 
48  auto atFrequency{[&deviceFile, &VERBOSE, &od4]() -> bool {
49  opendlv::proxy::GyroscopeReading gyroscopeReading = readGyroscope(deviceFile);
50  od4.send(gyroscopeReading);
51 
52  opendlv::proxy::AccelerationReading accelerometerReading = readAccelerometer(deviceFile);
53  od4.send(accelerometerReading);
54 
55  opendlv::proxy::MagneticFieldReading magnetometerReading = readMagnetometer(deviceFile);
56  od4.send(magnetometerReading);
57 
58  opendlv::proxy::AltitudeReading altimeterReading = readAltimeter(deviceFile);
59  od4.send(altimeterReading);
60 
61  opendlv::proxy::TemperatureReading thermometerReading = readThermometer(deviceFile);
62  od4.send(thermometerReading);
63 
64  if (VERBOSE) {
65  std::cout << "[OD4] Sending IMU data: " << std::endl
66  << "Gyroscope -" <<
67  " X: " << gyroscopeReading.GyroscopeReadingX() <<
68  " Y: " << gyroscopeReading.GyroscopeReadingY() <<
69  " Z: " << gyroscopeReading.GyroscopeReadingZ() << std::endl
70  << "Accelerometer -" <<
71  " X: " << accelerometerReading.accelerationX() <<
72  " Y: " << accelerometerReading.accelerationY() <<
73  " Z: " << accelerometerReading.accelerationZ() << std::endl
74  << "Magnetometer-" <<
75  " X: " << magnetometerReading.magneticFieldX() <<
76  " Y: " << magnetometerReading.magneticFieldY() <<
77  " Z: " << magnetometerReading.magneticFieldZ() << std::endl
78  << "Altimeter -" <<
79  " Altitude: " << altimeterReading.altitude() << std::endl
80  << "Thermometer -" <<
81  " Temperature: "<< thermometerReading.temperature() << std::endl;
82  }
83  return true;
84  }};
85  od4.timeTrigger(FREQ, atFrequency);
86  }
87  return 0;
88 }
89 
90 int8_t i2cAccessDevice(int16_t deviceFile, uint8_t const addr) {
91  if (ioctl(deviceFile, I2C_SLAVE, addr) < 0) {
92  std::cerr << "[MPU9250] Failed to acquire bus access or talk to slave device. " << std::endl;
93  return -1;
94  }
95  return 0;
96 }
97 
98 int8_t i2cWriteRegister(std::vector<uint8_t> a_data, int16_t deviceFile) {
99  uint8_t* buffer = a_data.data();
100 
101  uint8_t status = write(deviceFile, buffer, a_data.size());
102 
103  if (status != a_data.size()) {
104  std::cerr << "[MPU9250] Failed to write on I2C bus." << std::endl;
105  return -1;
106  }
107  return 0;
108 }
109 
110 int8_t i2cReadRegister(int16_t deviceFile, uint8_t const addr, uint8_t *data, uint8_t const length) {
111  uint8_t buffer[1];
112  buffer[0] = addr;
113  uint8_t statusOut = write(deviceFile, buffer, 1);
114  uint8_t statusIn = read(deviceFile, data, length);
115  if (statusOut != 1 || statusIn != length) {
116  std::cerr << "[MPU9250] Failed to read I2C on bus." << std::endl;
117  return -1;
118  }
119  return 0;
120 }
121 
122 void initializeMpu(int16_t deviceFile) {
123  // wake up device
124  // Clear sleep mode bit (6), enable all sensors
125  uint8_t addr = MPU9250_ADDRESS;
126  i2cAccessDevice(deviceFile, addr);
127  uint8_t reg = MPU9250::PWR_MGMT_1;
128  i2cWriteRegister(std::vector<uint8_t>{reg,0x00}, deviceFile);
129  usleep(100000); // Wait for all registers to reset
130 
131  // Get stable time source
132  // Auto select clock source to be PLL gyroscope reference if ready else
133  i2cWriteRegister(std::vector<uint8_t>{reg,0x01}, deviceFile);
134  usleep(200000);
135 
136  // Configure Gyro and Thermometer
137  // Disable FSYNC and set thermometer and gyro bandwidth to 41 and 42 Hz,
138  // respectively;
139  // minimum delay time for this setting is 5.9 ms, which means sensor fusion
140  // update rates cannot be higher than 1 / 0.0059 = 170 Hz
141  // DLPF_CFG = bits 2:0 = 011; this limits the sample rate to 1000 Hz for both
142  // With the MPU9250, it is possible to get gyro sample rates of 32 kHz (!),
143  // 8 kHz, or 1 kHz
144  reg = MPU9250::CONFIG;
145  i2cWriteRegister(std::vector<uint8_t>{reg,0x03}, deviceFile);
146 
147  // Set sample rate = gyroscope output rate/(1 + SMPLRT_DIV)
148  // Use a 200 Hz rate; a rate consistent with the filter update rate
149  // determined inset in CONFIG above.
150  reg = MPU9250::SMPLRT_DIV;
151  i2cWriteRegister(std::vector<uint8_t>{reg, 0x04}, deviceFile);
152 
153  // Set gyroscope full scale range
154  // Range selects FS_SEL and AFS_SEL are 0 - 3, so 2-bit values are
155  // left-shifted into positions 4:3
156 
157  // get current GYRO_CONFIG register value
158  uint8_t c;
159  reg = MPU9250::GYRO_CONFIG;
160  i2cReadRegister(deviceFile, reg, &c, 1);
161  c = c & ~0x02; // Clear Fchoice bits [1:0]
162  c = c & ~0x18; // Clear AFS bits [4:3]
163  c = c | m_gscale << 3; // Set full scale range for the gyro
164  // Write new GYRO_CONFIG value to register
165  i2cWriteRegister(std::vector<uint8_t>{reg, c}, deviceFile);
166 
167  // Set accelerometer full-scale range configuration
168  // Get current ACCEL_CONFIG register value
169  reg = MPU9250::ACCEL_CONFIG;
170  i2cReadRegister(deviceFile, reg, &c, 1);
171  c = c & ~0x18; // Clear AFS bits [4:3]
172  c = c | m_ascale << 3; // Set full scale range for the accelerometer
173  // Write new ACCEL_CONFIG register value
174  i2cWriteRegister(std::vector<uint8_t>{reg, c}, deviceFile);
175 
176  // Set accelerometer sample rate configuration
177  // It is possible to get a 4 kHz sample rate from the accelerometer by
178  // choosing 1 for accel_fchoice_b bit [3]; in this case the bandwidth is
179  // 1.13 kHz
180  // Get current ACCEL_CONFIG2 register value
181  reg = MPU9250::ACCEL_CONFIG2;
182  i2cReadRegister(deviceFile, reg, &c, 1);
183  c = c & ~0x0F; // Clear accel_fchoice_b (bit 3) and A_DLPFG (bits [2:0])
184  c = c | 0x03; // Set accelerometer rate to 1 kHz and bandwidth to 41 Hz
185  // Write new ACCEL_CONFIG2 register value
186  i2cWriteRegister(std::vector<uint8_t>{reg, c}, deviceFile);
187  // The accelerometer, gyro, and thermometer are set to 1 kHz sample rates,
188  // but all these rates are further reduced by a factor of 5 to 200 Hz because
189  // of the SMPLRT_DIV setting
190 
191  // Configure Interrupts and Bypass Enable
192  // Set interrupt pin active high, push-pull, hold interrupt pin level HIGH
193  // until interrupt cleared, clear on read of INT_STATUS, and enable
194  // I2C_BYPASS_EN so additional chips can join the I2C bus (such as the Magnetometer)
195  reg = MPU9250::INT_PIN_CFG;
196  i2cWriteRegister(std::vector<uint8_t>{reg, 0x22}, deviceFile);
197  // Enable data ready (bit 0) interrupt
198  reg = MPU9250::INT_ENABLE;
199  i2cWriteRegister(std::vector<uint8_t>{reg, 0x01}, deviceFile);
200  usleep(100000);
201 }
202 
203 void initializeAK8963(int16_t deviceFile) {
204  // First extract the factory calibration for each magnetometer axis
205  uint8_t rawData[3]; // x/y/z gyro calibration data stored here
206  i2cWriteRegister(std::vector<uint8_t>{MPU9250::AK8963_CNTL, 0x00}, deviceFile); // Power down magnetometer
207  usleep(100000);
208  i2cWriteRegister(std::vector<uint8_t>{MPU9250::AK8963_CNTL, 0x0F}, deviceFile); // Enter Fuse ROM access mode
209  usleep(100000);
210  i2cReadRegister(deviceFile, MPU9250::AK8963_ASAX, &rawData[0], 3); // Read the x-, y-, and z-axis calibration values
211  i2cWriteRegister(std::vector<uint8_t>{MPU9250::AK8963_CNTL, 0x00}, deviceFile); // Power down magnetometer
212  usleep(100000);
213  // Configure the magnetometer for continuous read and highest resolution
214  // set Mscale bit 4 to 1 (0) to enable 16 (14) bit resolution in CNTL register,
215  // and enable continuous mode data acquisition Mmode (bits [3:0]), 0010 for 8 Hz and 0110 for 100 Hz sample rates
216  i2cWriteRegister(std::vector<uint8_t>{MPU9250::AK8963_CNTL, 1 << 4 | 0110}, deviceFile); // Set magnetometer data resolution and sample ODR
217  usleep(100000);
218 }
219 
220 std::vector<float> calibrateMPU9250(int16_t deviceFile) {
221 
222  std::cout << "[MPU9250] Starting calibration...\n";
223  uint8_t rawData[12];
224 
225  i2cAccessDevice(deviceFile, MPU9250_ADDRESS);
226 
227  i2cWriteRegister(std::vector<uint8_t>{MPU9250::PWR_MGMT_1, 0x80}, deviceFile);
228  usleep(100000);
229  i2cWriteRegister(std::vector<uint8_t>{MPU9250::PWR_MGMT_1, 0x01}, deviceFile);
230  i2cWriteRegister(std::vector<uint8_t>{MPU9250::PWR_MGMT_2, 0x00}, deviceFile);
231  usleep(200000);
232 
233  i2cWriteRegister(std::vector<uint8_t>{MPU9250::INT_ENABLE, 0x00}, deviceFile);
234  i2cWriteRegister(std::vector<uint8_t>{MPU9250::FIFO_EN, 0x00}, deviceFile);
235  i2cWriteRegister(std::vector<uint8_t>{MPU9250::PWR_MGMT_1, 0x00}, deviceFile);
236  i2cWriteRegister(std::vector<uint8_t>{MPU9250::I2C_MST_CTRL, 0x00}, deviceFile);
237  i2cWriteRegister(std::vector<uint8_t>{MPU9250::USER_CTRL, 0x00}, deviceFile);
238  i2cWriteRegister(std::vector<uint8_t>{MPU9250::USER_CTRL, 0x0C}, deviceFile);
239  usleep(15000);
240 
241  i2cWriteRegister(std::vector<uint8_t>{MPU9250::CONFIG, 0x01}, deviceFile);
242  i2cWriteRegister(std::vector<uint8_t>{MPU9250::SMPLRT_DIV, 0x00}, deviceFile);
243  i2cWriteRegister(std::vector<uint8_t>{MPU9250::GYRO_CONFIG, 0x00}, deviceFile);
244  i2cWriteRegister(std::vector<uint8_t>{MPU9250::ACCEL_CONFIG, 0x00}, deviceFile);
245 
246  float const gyroSens = (250.0f / 32768.0f * static_cast<float>(M_PI) / 180.0f);
247 
248  i2cWriteRegister(std::vector<uint8_t>{MPU9250::USER_CTRL, 0x40}, deviceFile);
249  i2cWriteRegister(std::vector<uint8_t>{MPU9250::FIFO_EN, 0x78}, deviceFile);
250  usleep(40000);
251  i2cWriteRegister(std::vector<uint8_t>{MPU9250::FIFO_EN, 0x00}, deviceFile);
252 
253  i2cReadRegister(deviceFile, MPU9250::FIFO_COUNTH, &rawData[0], 2);
254 
255  uint16_t fifoCount = ((uint16_t) rawData[0] << 8) | rawData[1];
256  std::cout << "[MPU9250] FIFO Count: " << fifoCount << std::endl;
257  uint16_t packetCount = fifoCount/12;
258  std::cout << "[MPU9250] Packet Count: " << packetCount << std::endl;
259 
260  int32_t gyroBias[3] = {0,0,0};
261  for (uint8_t i = 0; i < packetCount; i++) {
262  int16_t gyroSampl[3] = {0,0,0};
263  i2cReadRegister(deviceFile, MPU9250::FIFO_R_W, &rawData[0], 12);
264 
265  gyroSampl[0] = (int16_t) (((int16_t)rawData[6] << 8) | rawData[7] );
266  gyroSampl[1] = (int16_t) (((int16_t)rawData[8] << 8) | rawData[9] );
267  gyroSampl[2] = (int16_t) (((int16_t)rawData[10] << 8) | rawData[11]);
268 
269  gyroBias[0] += (int32_t) gyroSampl[0];
270  gyroBias[1] += (int32_t) gyroSampl[1];
271  gyroBias[2] += (int32_t) gyroSampl[2];
272  }
273 
274  gyroBias[0] /= packetCount;
275  gyroBias[1] /= packetCount;
276  gyroBias[2] /= packetCount;
277 
278 
279  // std::cout << "[MPU9250] Gyro bias: " << gyroBias.at(0) << ", " << gyroBias.at(1) << ", " << gyroBias.at(2) << std::endl;
280  std::vector<float> gyroBiasVec;
281  gyroBiasVec.push_back(gyroBias[0] * gyroSens);
282  gyroBiasVec.push_back(gyroBias[1] * gyroSens);
283  gyroBiasVec.push_back(gyroBias[2] * gyroSens);
284  std::cout << "[MPU9250] Gyro bias: " << gyroBiasVec.at(0) << ", " << gyroBiasVec.at(1) << ", " << gyroBiasVec.at(2) << std::endl;
285  return gyroBiasVec;
286 }
287 
288 int8_t setGyroOffset(std::vector<float> const a_offset, int16_t deviceFile) {
289  if (a_offset.size() != 3) {
290  std::cerr << "[MPU9250] setGyroOffset received a vector of a length not supported." << std::endl;
291  return -1;
292  }
293 
294  float const gyroSens = (250.0f / 32768.0f * static_cast<float>(M_PI) / 180.0f);
295 
296  int32_t xOffset = std::lround(a_offset.at(0) / gyroSens);
297  int32_t yOffset = std::lround(a_offset.at(1) / gyroSens);
298  int32_t zOffset = std::lround(a_offset.at(2) / gyroSens);
299 
300  uint8_t xh = (-xOffset/4 >> 8);
301  uint8_t xl = ((-xOffset/4) & 0xFF);
302  uint8_t yh = (-yOffset/4 >> 8);
303  uint8_t yl = ((-yOffset/4) & 0xFF);
304  uint8_t zh = (-zOffset/4 >> 8);
305  uint8_t zl = ((-zOffset/4) & 0xFF);
306 
307  i2cAccessDevice(deviceFile, MPU9250_ADDRESS);
308  i2cWriteRegister(std::vector<uint8_t>{MPU9250::XG_OFFSET_H, xh, xl, yh, yl, zh, zl}, deviceFile);
309 
310  return 0;
311 }
312 
313 opendlv::proxy::AccelerationReading readAccelerometer(int16_t deviceFile) {
314  uint8_t addr = MPU9250_ADDRESS;
315  i2cAccessDevice(deviceFile, addr);
316  uint8_t reg = MPU9250::ACCEL_XOUT_H;
317  uint8_t rawData[6];
318  i2cReadRegister(deviceFile, reg, &rawData[0], 6);
319 
320  float const c = getAscale();
321 
322  int16_t x = (((int16_t)rawData[0] << 8) | rawData[1] );
323  int16_t y = (((int16_t)rawData[2] << 8) | rawData[3] );
324  int16_t z = (((int16_t)rawData[4] << 8) | rawData[5] );
325  opendlv::proxy::AccelerationReading accelerometerReading;
326  accelerometerReading.accelerationX(x*c);
327  accelerometerReading.accelerationY(y*c);
328  accelerometerReading.accelerationZ(z*c);
329  return accelerometerReading;
330 }
331 
332 opendlv::proxy::MagneticFieldReading readMagnetometer(int16_t deviceFile) {
333  uint8_t addr = AK8963_ADDRESS;
334  i2cAccessDevice(deviceFile, addr);
335  uint8_t reg = MPU9250::AK8963_XOUT_L;
336  uint8_t rawData[7];
337  i2cReadRegister(deviceFile, reg, &rawData[0], 7);
338 
339  float const c = getMscale();
340 
341  int16_t x = (((int16_t) rawData[0] << 8) | rawData[1]);
342  int16_t y = (((int16_t) rawData[2] << 8) | rawData[3]);
343  int16_t z = (((int16_t) rawData[4] << 8) | rawData[5]);
344 
345  opendlv::proxy::MagneticFieldReading magneticFieldReading;
346  magneticFieldReading.magneticFieldX(x);
347  magneticFieldReading.magneticFieldY(y);
348  magneticFieldReading.magneticFieldZ(z);
349 
350  return magneticFieldReading;
351 }
352 
353 opendlv::proxy::GyroscopeReading readGyroscope(int16_t deviceFile) {
354  uint8_t addr = MPU9250_ADDRESS;
355  i2cAccessDevice(deviceFile, addr);
356  uint8_t reg = MPU9250::GYRO_XOUT_H;
357  uint8_t rawData[6];
358  i2cReadRegister(deviceFile, reg, &rawData[0], 6);
359 
360  float const c = getGscale(true);
361 
362  int16_t x = (((int16_t)rawData[0] << 8) | rawData[1] );
363  int16_t y = (((int16_t)rawData[2] << 8) | rawData[3] );
364  int16_t z = (((int16_t)rawData[4] << 8) | rawData[5] );
365 
366  opendlv::proxy::GyroscopeReading gyroscopeReading;
367  gyroscopeReading.GyroscopeReadingX(x*c);
368  gyroscopeReading.GyroscopeReadingY(y*c);
369  gyroscopeReading.GyroscopeReadingZ(z*c);
370  return gyroscopeReading;
371 }
372 
373 opendlv::proxy::AltitudeReading readAltimeter(int16_t deviceFile) {
374  opendlv::proxy::AltitudeReading altimeterReading;
375  altimeterReading.altitude(0);
376  return altimeterReading;
377 }
378 
379 opendlv::proxy::TemperatureReading readThermometer(int16_t deviceFile) {
380  uint8_t addr = MPU9250_ADDRESS;
381  i2cAccessDevice(deviceFile, addr);
382  uint8_t reg = MPU9250::TEMP_OUT_H;
383  uint8_t rawData[2];
384  i2cReadRegister(deviceFile, reg, &rawData[0], 2);
385 
386  int16_t temp = (((int16_t)rawData[0] << 8) | rawData[1]) / 1.0f;
387 
388  opendlv::proxy::TemperatureReading temperatureReading;
389  temperatureReading.temperature(21.0f + temp / 333.87f);
390  // opendlv::proxy::TemperatureReading temperatureReading(0);
391  return temperatureReading;
392 }
393 
394 float getGscale(bool a_radFlag) {
395  float conversion = 1;
396  if (a_radFlag) {
397  conversion = static_cast<float>(M_PI) / 180.0f;
398  }
399  switch (m_gscale) {
400  case GFS_250DPS:
401  return (250.0f / 32768.0f) * conversion;
402  case GFS_500DPS:
403  return (500.0f / 32768.0f) * conversion;
404  case GFS_1000DPS:
405  return (1000.0f / 32768.0f) * conversion;
406  case GFS_2000DPS:
407  return (2000.0f / 32768.0f) * conversion;
408  default:
409  return 0.0f;
410  }
411 }
412 
413 float getAscale() {
414  switch (m_ascale) {
415  case AFS_2G:
416  return (9.82f * 2.0f / 32768.0f);
417  case AFS_4G:
418  return (9.82f * 4.0f / 32768.0f);
419  case AFS_8G:
420  return (9.82f * 8.0f / 32768.0f);
421  case AFS_16G:
422  return (9.82f * 16.0f / 32768.0f);
423  default:
424  return 0.0f;
425  }
426 }
427 
428 float getMscale() {
429  switch (m_mscale) {
430  // Possible magnetometer scales (and their register bit settings) are:
431  // 14 bit resolution (0) and 16 bit resolution (1)
432  case MFS_14BITS:
433  return 10.0f * 4912.0f / 8190.0f; // Proper scale to return milliGauss
434  case MFS_16BITS:
435  return 10.0f * 4912.0f / 32760.0f; // Proper scale to return milliGauss
436  default:
437  return 0.0f;
438  }
439 }