Files
WAVE_ROVER/IMU.cpp
T

360 lines
10 KiB
C++

#include "IMU.h"
void calibrateMagn();
void imuAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz);
float invSqrt(float x);
/******************************************************************************
* IMU module *
******************************************************************************/
// #define S_SCL 33
// #define S_SDA 32
AK09918_err_type_t err;
QMI8658 qmi8658_;
AK09918 magnetometer_;
int16_t offset_x = -12, offset_y = 0, offset_z = 0;
int16_t x, y, z;
// Find the magnetic declination at your location
// http://www.magnetic-declination.com/
double declination_shenzhen = -3.22;
#define Kp 4.50f // proportional gain governs rate of convergence to accelerometer/magnetometer
#define Ki 1.0f // integral gain governs rate of convergence of gyroscope biases
float angles[3];
float q0, q1, q2, q3;
float exInt = 0.0f;
float eyInt = 0.0f;
float ezInt = 0.0f;
uint32_t lastFilterUpdateUs = 0;
namespace {
constexpr float kDegToRad = 0.01745329251994329577f;
constexpr float kDefaultSampleDt = 0.01f;
constexpr float kMaxSampleDt = 0.1f;
float clampUnit(float value)
{
if (value > 1.0f) {
return 1.0f;
}
if (value < -1.0f) {
return -1.0f;
}
return value;
}
void resetFilterState()
{
q0 = 1.0f;
q1 = 0.0f;
q2 = 0.0f;
q3 = 0.0f;
exInt = 0.0f;
eyInt = 0.0f;
ezInt = 0.0f;
lastFilterUpdateUs = micros();
}
float getSampleDeltaSeconds()
{
const uint32_t nowUs = micros();
if (lastFilterUpdateUs == 0) {
lastFilterUpdateUs = nowUs;
return kDefaultSampleDt;
}
const uint32_t elapsedUs = nowUs - lastFilterUpdateUs;
lastFilterUpdateUs = nowUs;
const float deltaSeconds = elapsedUs * 1.0e-6f;
if (deltaSeconds <= 0.0f || deltaSeconds > kMaxSampleDt) {
return kDefaultSampleDt;
}
return deltaSeconds;
}
bool normalizeVector(float *xAxis, float *yAxis, float *zAxis)
{
const float normSquared = (*xAxis * *xAxis) + (*yAxis * *yAxis) + (*zAxis * *zAxis);
if (normSquared <= 1.0e-12f) {
return false;
}
const float norm = invSqrt(normSquared);
*xAxis *= norm;
*yAxis *= norm;
*zAxis *= norm;
return true;
}
} // namespace
void imuInit()
{
// Wire.begin(S_SDA, S_SCL);
// Serial.begin(115200);
if (qmi8658_.begin() == 0)
Serial.println("qmi8658_init fail");
if (magnetometer_.initialize())
Serial.println("AK09918_init fail") ;
magnetometer_.switchMode(AK09918_CONTINUOUS_100HZ);
err = magnetometer_.isDataReady();
int retry_times = 0;
while (err != AK09918_ERR_OK) {
Serial.println(err);
Serial.println("Waiting Sensor");
delay(100);
magnetometer_.reset();
delay(100);
magnetometer_.switchMode(AK09918_CONTINUOUS_100HZ);
err = magnetometer_.isDataReady();
retry_times ++;
if (retry_times > 10) {
break;
}
}
// Serial.println("Start figure-8 calibration after 1 seconds.");
// delay(1000);
// calibrate(10000, &offset_x, &offset_y, &offset_z);
// calibrateMagn();
resetFilterState();
}
void imuDataGet(EulerAngles *pstAngles,
IMU_ST_SENSOR_DATA_FLOAT *pstGyroRawData,
IMU_ST_SENSOR_DATA_FLOAT *pstAccelRawData,
IMU_ST_SENSOR_DATA *pstMagnRawData)
{
if (pstAngles == nullptr || pstGyroRawData == nullptr ||
pstAccelRawData == nullptr || pstMagnRawData == nullptr) {
return;
}
float acc[3] = {0.0f, 0.0f, 0.0f};
float gyro[3] = {0.0f, 0.0f, 0.0f};
float MotionVal[9];
const AK09918_err_type_t magErr = magnetometer_.getData(&x, &y, &z);
if (magErr == AK09918_ERR_OVERFLOW) {
Serial.println("AK09918 overflow detected, keeping last valid magnetic sample.");
}
pstMagnRawData->s16X = x- offset_x;
pstMagnRawData->s16Y = y- offset_y;
pstMagnRawData->s16Z = z- offset_z;
// qmi8658_.GetEulerAngles(&pstAngles->pitch,&pstAngles->roll,&pstAngles->yaw,acc,gyro);
qmi8658_.read_sensor_data(acc,gyro);
// pstAngles->roll = atan2((float)acc[1], (float)acc[2]);
// pstAngles->pitch = atan2(-(float)acc[0], sqrt((float)(acc[1] * acc[1]) + (float)(acc[2] * acc[2])));
// double Xheading = pstMagnRawData->s16X * cos(pstAngles->pitch) + pstMagnRawData->s16Y * sin(pstAngles->roll) * sin(pstAngles->pitch) + pstMagnRawData->s16Z * cos(pstAngles->roll) * sin(pstAngles->pitch);
// double Yheading = pstMagnRawData->s16Y * cos(pstAngles->roll) - pstMagnRawData->s16Z * sin(pstAngles->pitch);
// pstAngles->yaw = /*180 + */57.3 * atan2(Yheading, Xheading) + declination_shenzhen;
// pstAngles->roll = atan2((float)acc[1], (float)acc[2]) * 57.3;
// pstAngles->pitch = atan2(-(float)acc[0], sqrt((float)(acc[1] * acc[1]) + (float)(acc[2] * acc[2]))) * 57.3;
MotionVal[0]=gyro[0];
MotionVal[1]=gyro[1];
MotionVal[2]=gyro[2];
MotionVal[3]=acc[0];
MotionVal[4]=acc[1];
MotionVal[5]=acc[2];
MotionVal[6]=pstMagnRawData->s16X;
MotionVal[7]=pstMagnRawData->s16Y;
MotionVal[8]=pstMagnRawData->s16Z;
imuAHRSupdate((float)MotionVal[0] * kDegToRad, (float)MotionVal[1] * kDegToRad, (float)MotionVal[2] * kDegToRad,
(float)MotionVal[3], (float)MotionVal[4], (float)MotionVal[5],
(float)MotionVal[6], (float)MotionVal[7], MotionVal[8]);
pstAngles->pitch = asinf(clampUnit(-2.0f * q1 * q3 + 2.0f * q0 * q2)) * 57.2957795f;
pstAngles->roll = atan2f(2.0f * q2 * q3 + 2.0f * q0 * q1,
-2.0f * q1 * q1 - 2.0f * q2 * q2 + 1.0f) * 57.2957795f;
pstAngles->yaw = atan2f(-2.0f * q1 * q2 - 2.0f * q0 * q3,
2.0f * q2 * q2 + 2.0f * q3 * q3 - 1.0f) * 57.2957795f;
pstGyroRawData->X = gyro[0];
pstGyroRawData->Y = gyro[1];
pstGyroRawData->Z = gyro[2];
pstAccelRawData->X = acc[0];
pstAccelRawData->Y = acc[1];
pstAccelRawData->Z = acc[2];
return;
}
bool imuRecalibrate()
{
qmi8658_.autoOffsets();
resetFilterState();
return true;
}
void imuGetMagnOffsets(IMU_ST_SENSOR_DATA *pstMagnOffset)
{
if (pstMagnOffset == nullptr) {
return;
}
pstMagnOffset->s16X = offset_x;
pstMagnOffset->s16Y = offset_y;
pstMagnOffset->s16Z = offset_z;
}
void imuSetMagnOffsets(int16_t inputX, int16_t inputY, int16_t inputZ)
{
offset_x = inputX;
offset_y = inputY;
offset_z = inputZ;
resetFilterState();
}
float imuGetTemperature()
{
return qmi8658_.read_temperature();
}
void imuAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz)
{
const float sampleDt = getSampleDeltaSeconds();
const float halfT = 0.5f * sampleDt;
float hx = 0.0f, hy = 0.0f, hz = 0.0f, bx = 0.0f, bz = 0.0f;
float vx = 0.0f, vy = 0.0f, vz = 0.0f, wx = 0.0f, wy = 0.0f, wz = 0.0f;
float ex = 0.0f, ey = 0.0f, ez = 0.0f;
float q0q0 = q0 * q0;
float q0q1 = q0 * q1;
float q0q2 = q0 * q2;
float q0q3 = q0 * q3;
float q1q1 = q1 * q1;
float q1q2 = q1 * q2;
float q1q3 = q1 * q3;
float q2q2 = q2 * q2;
float q2q3 = q2 * q3;
float q3q3 = q3 * q3;
const bool accelValid = normalizeVector(&ax, &ay, &az);
const bool magnValid = normalizeVector(&mx, &my, &mz);
if (accelValid) {
vx = 2.0f * (q1q3 - q0q2);
vy = 2.0f * (q0q1 + q2q3);
vz = q0q0 - q1q1 - q2q2 + q3q3;
ex += (ay * vz - az * vy);
ey += (az * vx - ax * vz);
ez += (ax * vy - ay * vx);
}
if (magnValid) {
hx = 2.0f * mx * (0.5f - q2q2 - q3q3) + 2.0f * my * (q1q2 - q0q3) + 2.0f * mz * (q1q3 + q0q2);
hy = 2.0f * mx * (q1q2 + q0q3) + 2.0f * my * (0.5f - q1q1 - q3q3) + 2.0f * mz * (q2q3 - q0q1);
hz = 2.0f * mx * (q1q3 - q0q2) + 2.0f * my * (q2q3 + q0q1) + 2.0f * mz * (0.5f - q1q1 - q2q2);
bx = sqrtf((hx * hx) + (hy * hy));
bz = hz;
wx = 2.0f * bx * (0.5f - q2q2 - q3q3) + 2.0f * bz * (q1q3 - q0q2);
wy = 2.0f * bx * (q1q2 - q0q3) + 2.0f * bz * (q0q1 + q2q3);
wz = 2.0f * bx * (q0q2 + q1q3) + 2.0f * bz * (0.5f - q1q1 - q2q2);
ex += (my * wz - mz * wy);
ey += (mz * wx - mx * wz);
ez += (mx * wy - my * wx);
}
if (accelValid || magnValid) {
exInt += ex * Ki * sampleDt;
eyInt += ey * Ki * sampleDt;
ezInt += ez * Ki * sampleDt;
gx = gx + Kp * ex + exInt;
gy = gy + Kp * ey + eyInt;
gz = gz + Kp * ez + ezInt;
}
const float q0Prev = q0;
const float q1Prev = q1;
const float q2Prev = q2;
const float q3Prev = q3;
q0 = q0Prev + (-q1Prev * gx - q2Prev * gy - q3Prev * gz) * halfT;
q1 = q1Prev + (q0Prev * gx + q2Prev * gz - q3Prev * gy) * halfT;
q2 = q2Prev + (q0Prev * gy - q1Prev * gz + q3Prev * gx) * halfT;
q3 = q3Prev + (q0Prev * gz + q1Prev * gy - q2Prev * gx) * halfT;
const float normSquared = q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3;
if (normSquared <= 1.0e-12f) {
resetFilterState();
return;
}
const float norm = invSqrt(normSquared);
q0 = q0 * norm;
q1 = q1 * norm;
q2 = q2 * norm;
q3 = q3 * norm;
}
float invSqrt(float x)
{
if (x <= 0.0f) {
return 0.0f;
}
float halfx = 0.5f * x;
union {
float f;
uint32_t i;
} conv = { x };
conv.i = 0x5f3759df - (conv.i >> 1);
float y = conv.f;
y = y * (1.5f - (halfx * y * y));
return y;
}
void calibrateMagn(void)
{
int16_t temp[9];
Serial.printf("keep 10dof-imu device horizontal and it will read x y z axis offset value after 4 seconds\n");
delay(4000);
Serial.printf("start read all axises offset value\n");
magnetometer_.getData(&x, &y, &z);
temp[0] = x;
temp[1] = y;
temp[2] = z;
Serial.printf("rotate z axis 180 degrees and it will read all axises offset value after 4 seconds\n");
delay(4000);
Serial.printf("start read all axises offset value\n");
magnetometer_.getData(&x, &y, &z);
temp[3] = x;
temp[4] = y;
temp[5] = z;
Serial.printf("flip 10dof-imu device and keep it horizontal and it will read all axises offset value after 4 seconds\n");
delay(4000);
Serial.printf("start read all axises offset value\n");
magnetometer_.getData(&x, &y, &z);
temp[6] = x;
temp[7] = y;
temp[8] = z;
offset_x = (temp[0]+temp[3])/2;
offset_y = (temp[1]+temp[4])/2;
offset_z = (temp[5]+temp[8])/2;
}