Загрузка данных


#include <Wire.h>

#define DEV_ADDR 0x68

int16_t ax_raw, ay_raw, az_raw;
int16_t temp_raw;
int16_t gx_raw, gy_raw, gz_raw;

float ax, ay, az;
float wx, wy, wz;

float T[3][3] = {
  {0, -1, 0},
  {1,  0, 0},
  {0,  0, 1}
};

float aS[3];
float wS[3];
float aB[3];
float wB[3];

float aBx, aBy, aBz;
float p, q, r;

float roll = 0;
float pitch = 0;
float yaw = 0;

float beta_z = 0;
const float alphaFilter = 0.02;

float q0 = 1.0;
float q1 = 0.0;
float q2 = 0.0;
float q3 = 0.0;

unsigned long lastTime;

float angle360(float angle) {
  while (angle < 0) {
    angle += 360.0;
  }

  while (angle >= 360.0) {
    angle -= 360.0;
  }

  return angle;
}


// Чтение данных MPU6050
void readMPU() {
  Wire.beginTransmission(DEV_ADDR);
  Wire.write(0x3B);
  Wire.endTransmission(false);

  Wire.requestFrom(DEV_ADDR, 14, true);

  ax_raw = (Wire.read() << 8) | Wire.read();
  ay_raw = (Wire.read() << 8) | Wire.read();
  az_raw = (Wire.read() << 8) | Wire.read();

  temp_raw = (Wire.read() << 8) | Wire.read();

  gx_raw = (Wire.read() << 8) | Wire.read();
  gy_raw = (Wire.read() << 8) | Wire.read();
  gz_raw = (Wire.read() << 8) | Wire.read();

  ax = ax_raw / 16384.0 * 9.81;
  ay = ay_raw / 16384.0 * 9.81;
  az = az_raw / 16384.0 * 9.81;

  wx = gx_raw / 131.0 * PI / 180.0;
  wy = gy_raw / 131.0 * PI / 180.0;
  wz = gz_raw / 131.0 * PI / 180.0;
}


// ===== ЗАДАНИЕ 1 =====
// Переход в новый базис
void changeBasis() {
  aS[0] = ax;
  aS[1] = ay;
  aS[2] = az;

  wS[0] = wx;
  wS[1] = wy;
  wS[2] = wz;

  for (int i = 0; i < 3; i++) {
    aB[i] = 0;
    wB[i] = 0;
  }

  for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
      aB[i] += T[i][j] * aS[j];
    }
  }

  for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
      wB[i] += T[i][j] * wS[j];
    }
  }

  aBx = aB[0];
  aBy = aB[1];
  aBz = aB[2];

  p = wB[0];
  q = wB[1];
  r = wB[2];
}


// ===== ЗАДАНИЕ 3 =====
// Умножение кватернионов
void quatMultiply(
  float a0, float a1, float a2, float a3,
  float b0, float b1, float b2, float b3,
  float &c0, float &c1, float &c2, float &c3
) {
  c0 = a0*b0 - a1*b1 - a2*b2 - a3*b3;
  c1 = a0*b1 + a1*b0 + a2*b3 - a3*b2;
  c2 = a0*b2 - a1*b3 + a2*b0 + a3*b1;
  c3 = a0*b3 + a1*b2 - a2*b1 + a3*b0;
}


// Нормировка кватерниона
void normalizeQuaternion() {
  float norm = sqrt(
    q0*q0 +
    q1*q1 +
    q2*q2 +
    q3*q3
  );

  if (norm == 0) {
    return;
  }

  q0 /= norm;
  q1 /= norm;
  q2 /= norm;
  q3 /= norm;
}


void setup() {
  Serial.begin(115200);
  Wire.begin();

  Wire.beginTransmission(DEV_ADDR);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);

  Wire.beginTransmission(DEV_ADDR);
  Wire.write(0x1B);
  Wire.write(0);
  Wire.endTransmission(true);

  Wire.beginTransmission(DEV_ADDR);
  Wire.write(0x1C);
  Wire.write(0);
  Wire.endTransmission(true);

  delay(100);

  // Калибровка гироскопа
  float sum = 0;

  for (int i = 0; i < 500; i++) {
    readMPU();
    changeBasis();
    sum += r;
    delay(3);
  }

  beta_z = sum / 500.0;

  readMPU();
  changeBasis();

  roll = atan2(aBy, aBz);

  pitch = atan2(
    -aBx,
    sqrt(aBy*aBy + aBz*aBz)
  );

  yaw = 0;

  // Начальный кватернион
  float cr = cos(roll / 2.0);
  float sr = sin(roll / 2.0);

  float cp = cos(pitch / 2.0);
  float sp = sin(pitch / 2.0);

  float cy = cos(yaw / 2.0);
  float sy = sin(yaw / 2.0);

  q0 = cr*cp*cy + sr*sp*sy;
  q1 = sr*cp*cy - cr*sp*sy;
  q2 = cr*sp*cy + sr*cp*sy;
  q3 = cr*cp*sy - sr*sp*cy;

  normalizeQuaternion();

  lastTime = micros();
}


void loop() {
  readMPU();


  // ===== ЗАДАНИЕ 1 =====
  changeBasis();

  unsigned long now = micros();

  float dt =
    (now - lastTime) / 1000000.0;

  lastTime = now;

  Serial.println("===== ЗАДАНИЕ 1 =====");

  Serial.print("aBx = ");
  Serial.print(aBx);

  Serial.print("  aBy = ");
  Serial.print(aBy);

  Serial.print("  aBz = ");
  Serial.print(aBz);

  Serial.println(" м/с^2");

  Serial.print("p = ");
  Serial.print(p, 4);

  Serial.print("  q = ");
  Serial.print(q, 4);

  Serial.print("  r = ");
  Serial.print(r, 4);

  Serial.println(" рад/с");


  // ===== ЗАДАНИЕ 2 =====
  // Расчёт Roll, Pitch, Yaw
  float roll_acc =
    atan2(aBy, aBz);

  float pitch_acc =
    atan2(
      -aBx,
      sqrt(aBy*aBy + aBz*aBz)
    );

  float oldRoll = roll;
  float oldPitch = pitch;

  roll =
    (1.0 - alphaFilter) *
    (
      oldRoll +
      dt *
      (
        p
        + q * sin(oldRoll) * tan(oldPitch)
        + r * cos(oldRoll) * tan(oldPitch)
      )
    )
    + alphaFilter * roll_acc;

  pitch =
    (1.0 - alphaFilter) *
    (
      oldPitch +
      dt *
      (
        q * cos(oldRoll)
        - r * sin(oldRoll)
      )
    )
    + alphaFilter * pitch_acc;

  yaw =
    yaw + dt * (r - beta_z);

  float roll_deg =
    angle360(roll * 180.0 / PI);

  float pitch_deg =
    angle360(pitch * 180.0 / PI);

  float yaw_deg =
    angle360(yaw * 180.0 / PI);

  Serial.println("===== ЗАДАНИЕ 2 =====");

  Serial.print("Крен (Roll) = ");
  Serial.print(roll_deg);
  Serial.println(" град");

  Serial.print("Тангаж (Pitch) = ");
  Serial.print(pitch_deg);
  Serial.println(" град");

  Serial.print("Рыскание (Yaw) = ");
  Serial.print(yaw_deg);
  Serial.println(" град");


  // ===== ЗАДАНИЕ 3 =====
  // Обновление ориентации через кватернион
  float rCorrected = r - beta_z;

  float dThetaX = p * dt;
  float dThetaY = q * dt;
  float dThetaZ = rCorrected * dt;

  float alphaStep = sqrt(
    dThetaX*dThetaX +
    dThetaY*dThetaY +
    dThetaZ*dThetaZ
  );

  float dq0;
  float dq1;
  float dq2;
  float dq3;

  if (alphaStep > 0.000001) {
    float sinHalf =
      sin(alphaStep / 2.0);

    dq0 =
      cos(alphaStep / 2.0);

    dq1 =
      (dThetaX / alphaStep) * sinHalf;

    dq2 =
      (dThetaY / alphaStep) * sinHalf;

    dq3 =
      (dThetaZ / alphaStep) * sinHalf;
  }
  else {
    dq0 = 1.0;
    dq1 = 0.0;
    dq2 = 0.0;
    dq3 = 0.0;
  }

  float newQ0;
  float newQ1;
  float newQ2;
  float newQ3;

  quatMultiply(
    q0, q1, q2, q3,
    dq0, dq1, dq2, dq3,
    newQ0, newQ1, newQ2, newQ3
  );

  q0 = newQ0;
  q1 = newQ1;
  q2 = newQ2;
  q3 = newQ3;

  normalizeQuaternion();

  float rollQ = atan2(
    2.0 * (q0*q1 + q2*q3),
    1.0 - 2.0 * (q1*q1 + q2*q2)
  );

  float sinPitch =
    2.0 * (q0*q2 - q3*q1);

  if (sinPitch > 1.0) {
    sinPitch = 1.0;
  }

  if (sinPitch < -1.0) {
    sinPitch = -1.0;
  }

  float pitchQ =
    asin(sinPitch);

  float yawQ = atan2(
    2.0 * (q0*q3 + q1*q2),
    1.0 - 2.0 * (q2*q2 + q3*q3)
  );

  float rollQ_deg =
    angle360(rollQ * 180.0 / PI);

  float pitchQ_deg =
    angle360(pitchQ * 180.0 / PI);

  float yawQ_deg =
    angle360(yawQ * 180.0 / PI);

  Serial.println("===== ЗАДАНИЕ 3 =====");

  Serial.print("q0 = ");
  Serial.print(q0, 5);

  Serial.print("  q1 = ");
  Serial.print(q1, 5);

  Serial.print("  q2 = ");
  Serial.print(q2, 5);

  Serial.print("  q3 = ");
  Serial.println(q3, 5);

  Serial.print("Крен (Roll) = ");
  Serial.print(rollQ_deg);
  Serial.println(" град");

  Serial.print("Тангаж (Pitch) = ");
  Serial.print(pitchQ_deg);
  Serial.println(" град");

  Serial.print("Рыскание (Yaw) = ");
  Serial.print(yawQ_deg);
  Serial.println(" град");

  Serial.println("==============================");

  delay(100);
}