// Copyright (c) 2023 Oleg Kalachev // Repository: https://github.com/okalachev/flix // Work with the IMU sensor #include #include #define ONE_G 9.80665 // NOTE: use 'ca' command to calibrate the accelerometer and put the values here Vector accBias(0, 0, 0); Vector accScale(1, 1, 1); MPU9250 IMU(SPI); Vector gyroBias; void setupIMU() { Serial.println("Setup IMU"); bool status = IMU.begin(); if (!status) { while (true) { Serial.println("IMU begin error"); delay(1000); } } configureIMU(); calibrateGyro(); } void configureIMU() { IMU.setAccelRange(IMU.ACCEL_RANGE_4G); IMU.setGyroRange(IMU.GYRO_RANGE_2000DPS); IMU.setDLPF(IMU.DLPF_MAX); IMU.setRate(IMU.RATE_1KHZ_APPROX); } void readIMU() { IMU.waitForData(); IMU.getGyro(gyro.x, gyro.y, gyro.z); IMU.getAccel(acc.x, acc.y, acc.z); // apply scale and bias acc = (acc - accBias) / accScale; gyro = gyro - gyroBias; // rotate rotateIMU(acc); rotateIMU(gyro); } void rotateIMU(Vector& data) { // Rotate from LFD to FLU // NOTE: In case of using other IMU orientation, change this line: data = Vector(data.y, data.x, -data.z); // Axes orientation for various boards: https://github.com/okalachev/flixperiph#imu-axes-orientation } void calibrateGyro() { const int samples = 1000; Serial.println("Calibrating gyro, stand still"); IMU.setGyroRange(IMU.GYRO_RANGE_250DPS); // the most sensitive mode gyroBias = Vector(0, 0, 0); for (int i = 0; i < samples; i++) { IMU.waitForData(); IMU.getGyro(gyro.x, gyro.y, gyro.z); gyroBias = gyroBias + gyro; } gyroBias = gyroBias / samples; printIMUCal(); configureIMU(); } void calibrateAccel() { Serial.println("Calibrating accelerometer"); IMU.setAccelRange(IMU.ACCEL_RANGE_2G); // the most sensitive mode Serial.setTimeout(60000); Serial.print("Place level [enter] "); Serial.readStringUntil('\n'); calibrateAccelOnce(); Serial.print("Place nose up [enter] "); Serial.readStringUntil('\n'); calibrateAccelOnce(); Serial.print("Place nose down [enter] "); Serial.readStringUntil('\n'); calibrateAccelOnce(); Serial.print("Place on right side [enter] "); Serial.readStringUntil('\n'); calibrateAccelOnce(); Serial.print("Place on left side [enter] "); Serial.readStringUntil('\n'); calibrateAccelOnce(); Serial.print("Place upside down [enter] "); Serial.readStringUntil('\n'); calibrateAccelOnce(); printIMUCal(); configureIMU(); } void calibrateAccelOnce() { const int samples = 1000; static Vector accMax(-INFINITY, -INFINITY, -INFINITY); static Vector accMin(INFINITY, INFINITY, INFINITY); // Compute the average of the accelerometer readings acc = Vector(0, 0, 0); for (int i = 0; i < samples; i++) { IMU.waitForData(); Vector sample; IMU.getAccel(sample.x, sample.y, sample.z); acc = acc + sample; } acc = acc / samples; // Update the maximum and minimum values if (acc.x > accMax.x) accMax.x = acc.x; if (acc.y > accMax.y) accMax.y = acc.y; if (acc.z > accMax.z) accMax.z = acc.z; if (acc.x < accMin.x) accMin.x = acc.x; if (acc.y < accMin.y) accMin.y = acc.y; if (acc.z < accMin.z) accMin.z = acc.z; Serial.printf("acc %f %f %f\n", acc.x, acc.y, acc.z); Serial.printf("max %f %f %f\n", accMax.x, accMax.y, accMax.z); Serial.printf("min %f %f %f\n", accMin.x, accMin.y, accMin.z); // Compute scale and bias accScale = (accMax - accMin) / 2 / ONE_G; accBias = (accMax + accMin) / 2; } void printIMUCal() { Serial.printf("gyro bias: %f, %f, %f\n", gyroBias.x, gyroBias.y, gyroBias.z); Serial.printf("accel bias: %f, %f, %f\n", accBias.x, accBias.y, accBias.z); Serial.printf("accel scale: %f, %f, %f\n", accScale.x, accScale.y, accScale.z); } void printIMUInfo() { Serial.printf("model: %s\n", IMU.getModel()); Serial.printf("who am I: 0x%02X\n", IMU.whoAmI()); }