Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
c68be57e1This file is pinned to the selected release. Download the original to open it in your design tool.
// Copyright (c) 2023 Oleg Kalachev <okalachev@gmail.com>
// Repository: https://github.com/okalachev/flix
// Work with the IMU sensor
#include <SPI.h>
#include <FlixPeriph.h>
#include "vector.h"
#include "lpf.h"
#include "util.h"
MPU9250 imu(SPI);
Vector imuRotation(0, 0, PI / 2); // imu orientation as Euler angles
Vector gyro; // gyroscope output, rad/s
Vector gyroBias;
Vector acc; // accelerometer output, m/s/s
Vector accBias;
Vector accScale(1, 1, 1);
LowPassFilter<Vector> gyroBiasFilter(0.001);
void setupIMU() {
print("Setup IMU\n");
imu.begin();
configureIMU();
}
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);
imu.setupInterrupt();
}
void readIMU() {
imu.waitForData();
imu.getGyro(gyro.x, gyro.y, gyro.z);
imu.getAccel(acc.x, acc.y, acc.z);
calibrateGyroOnce();
// apply scale and bias
acc = (acc - accBias) / accScale;
gyro = gyro - gyroBias;
// rotate to body frame
Quaternion rotation = Quaternion::fromEuler(imuRotation);
acc = Quaternion::rotateVector(acc, rotation.inversed());
gyro = Quaternion::rotateVector(gyro, rotation.inversed());
}
void calibrateGyroOnce() {
static Delay landedDelay(2);
if (!landedDelay.update(landed)) return; // calibrate only if definitely stationary
gyroBias = gyroBiasFilter.update(gyro);
}
void calibrateAccel() {
print("Calibrating accelerometer\n");
imu.setAccelRange(imu.ACCEL_RANGE_2G); // the most sensitive mode
print("1/6 Place level [8 sec]\n");
pause(8);
calibrateAccelOnce();
print("2/6 Place nose up [8 sec]\n");
pause(8);
calibrateAccelOnce();
print("3/6 Place nose down [8 sec]\n");
pause(8);
calibrateAccelOnce();
print("4/6 Place on right side [8 sec]\n");
pause(8);
calibrateAccelOnce();
print("5/6 Place on left side [8 sec]\n");
pause(8);
calibrateAccelOnce();
print("6/6 Place upside down [8 sec]\n");
pause(8);
calibrateAccelOnce();
printIMUCalibration();
print("✓ Calibration done!\n");
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;
// Compute scale and bias
accScale = (accMax - accMin) / 2 / ONE_G;
accBias = (accMax + accMin) / 2;
}
void printIMUCalibration() {
print("gyro bias: %f %f %f\n", gyroBias.x, gyroBias.y, gyroBias.z);
print("accel bias: %f %f %f\n", accBias.x, accBias.y, accBias.z);
print("accel scale: %f %f %f\n", accScale.x, accScale.y, accScale.z);
}
void printIMUInfo() {
imu.status() ? print("status: ERROR %d\n", imu.status()) : print("status: OK\n");
print("model: %s\n", imu.getModel());
print("who am I: 0x%02X\n", imu.whoAmI());
print("rate: %.0f\n", loopRate);
print("gyro: %f %f %f\n", gyro.x, gyro.y, gyro.z);
print("acc: %f %f %f\n", acc.x, acc.y, acc.z);
imu.waitForData();
Vector rawGyro, rawAcc;
imu.getGyro(rawGyro.x, rawGyro.y, rawGyro.z);
imu.getAccel(rawAcc.x, rawAcc.y, rawAcc.z);
print("raw gyro: %f %f %f\n", rawGyro.x, rawGyro.y, rawGyro.z);
print("raw acc: %f %f %f\n", rawAcc.x, rawAcc.y, rawAcc.z);
}