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


#include <Wire.h>

#define DEV_ADDR 0x68

// Сырые данные MPU6050
int16_t ax_raw, ay_raw, az_raw;
int16_t temp_raw;
int16_t gx_raw, gy_raw, gz_raw;

// Данные в базисе датчика S
float ax, ay, az;
float wx, wy, wz;

// Данные в новом базисе B
float aBx, aBy, aBz;
float p, q, r;

// Углы
float roll = 0;
float pitch = 0;
float yaw = 0;

// Смещение гироскопа по Z
float beta_z = 0;

// Вес акселерометра в комплементарном фильтре
const float alpha = 0.02;

// Время
unsigned long lastTime;


// Чтение MPU6050
void readMPU() {

  Wire.beginTransmission(DEV_ADDR);
  Wire.write(0x3B);                 // начинаем с ACCEL_XOUT_H
  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();


  // Акселерометр: перевод в м/с^2
  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;


  // Матрица перехода:
  //
  //     | 0  -1  0 |
  // T = | 1   0  0 |
  //     | 0   0  1 |
  //
  // a_B = T * a_S

  aBx = -ay;
  aBy =  ax;
  aBz =  az;

  // omega_B = T * omega_S

  p = -wy;
  q =  wx;
  r =  wz;
}


void setup() {

  Serial.begin(115200);
  Wire.begin();


  // Разбудить MPU6050
  Wire.beginTransmission(DEV_ADDR);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);

  delay(100);


  // Калибровка гироскопа по Z.
  // Датчик в этот момент НЕ ДВИГАТЬ.
  float sum = 0;

  for (int i = 0; i < 500; i++) {

    readMPU();

    sum += r;

    delay(3);
  }

  beta_z = sum / 500.0;


  // Начальные roll и pitch берём с акселерометра

  readMPU();

  roll = atan2(aBy, aBz);

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

  yaw = 0;

  lastTime = micros();
}


void loop() {

  readMPU();


  // Считаем время между измерениями
  unsigned long now = micros();

  float dt = (now - lastTime) / 1000000.0;

  lastTime = now;


  // Углы по акселерометру

  float roll_acc = atan2(aBy, aBz);

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


  // -------- ROLL --------

  roll =
    (1.0 - alpha) *
    (
      roll +
      dt *
      (
        p +
        q * sin(roll) * tan(pitch) +
        r * cos(roll) * tan(pitch)
      )
    )
    +
    alpha * roll_acc;


  // -------- PITCH --------

  pitch =
    (1.0 - alpha) *
    (
      pitch +
      dt *
      (
        q * cos(roll) -
        r * sin(roll)
      )
    )
    +
    alpha * pitch_acc;


  // -------- YAW --------

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


  // Перевод радианов в градусы для вывода

  float roll_deg  = roll  * 180.0 / PI;
  float pitch_deg = pitch * 180.0 / PI;
  float yaw_deg   = yaw   * 180.0 / PI;


  Serial.print("Roll: ");
  Serial.print(roll_deg);

  Serial.print("  Pitch: ");
  Serial.print(pitch_deg);

  Serial.print("  Yaw: ");
  Serial.println(yaw_deg);


  delay(10);
}