Загрузка данных
#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;
// ==========================================
// ДАННЫЕ В ИСХОДНОМ БАЗИСЕ
// ==========================================
// Ускорения, м/с^2
float ax, ay, az;
// Угловые скорости, рад/с
float wx, wy, wz;
// Температура, °C
float temperature;
// ==========================================
// МАТРИЦА ПЕРЕХОДА
//
// | 0 -1 0 |
// T = | 1 0 0 |
// | 0 0 1 |
//
// ==========================================
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;
// Ошибка нуля гироскопа по Z
float beta_z = 0;
// Коэффициент комплементарного фильтра
const float alpha = 0.02;
// Время предыдущего измерения
unsigned long lastTime;
// ==========================================
// ФУНКЦИЯ 0...360 ГРАДУСОВ
// ==========================================
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);
// Начинаем чтение с ACCEL_XOUT_H
Wire.write(0x3B);
Wire.endTransmission(false);
// Читаем 14 байт:
// AX AY AZ TEMP GX GY GZ
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;
// Температура -> °C
temperature = temp_raw / 340.0 + 36.53;
// Гироскоп:
// сырые данные -> град/с -> рад/с
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;
}
// ========================================
// aB = T * aS
// ========================================
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
aB[i] += T[i][j] * aS[j];
}
}
// ========================================
// wB = T * wS
// ========================================
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];
}
// ==========================================
// SETUP
// ==========================================
void setup() {
Serial.begin(115200);
Wire.begin();
// ========================================
// БУДИМ MPU6050
// ========================================
Wire.beginTransmission(DEV_ADDR);
Wire.write(0x6B); // PWR_MGMT_1
Wire.write(0); // выход из режима сна
Wire.endTransmission(true);
// ========================================
// ГИРОСКОП ±250 град/с
// ========================================
Wire.beginTransmission(DEV_ADDR);
Wire.write(0x1B);
Wire.write(0);
Wire.endTransmission(true);
// ========================================
// АКСЕЛЕРОМЕТР ±2g
// ========================================
Wire.beginTransmission(DEV_ADDR);
Wire.write(0x1C);
Wire.write(0);
Wire.endTransmission(true);
delay(100);
// ========================================
// КАЛИБРОВКА ГИРОСКОПА ПО Z
//
// ДАТЧИК НЕ ДВИГАТЬ!
// ========================================
float sum = 0;
for (int i = 0; i < 500; i++) {
readMPU();
changeBasis();
sum += r;
delay(3);
}
// Средняя ошибка по Z
beta_z = sum / 500.0;
// ========================================
// НАЧАЛЬНЫЕ УГЛЫ
// ========================================
readMPU();
changeBasis();
// Начальный крен по акселерометру
roll = atan2(aBy, aBz);
// Начальный тангаж по акселерометру
pitch = atan2(
-aBx,
sqrt(aBy * aBy + aBz * aBz)
);
// Начальный Yaw принимаем за 0
yaw = 0;
lastTime = micros();
}
// ==========================================
// ОСНОВНОЙ ЦИКЛ
// ==========================================
void loop() {
// Читаем MPU6050
readMPU();
// ========================================
// ЗАДАНИЕ 1
// ПЕРЕХОД В НОВЫЙ БАЗИС
// ========================================
changeBasis();
// ========================================
// СЧИТАЕМ dt
// ========================================
unsigned long now = micros();
float dt =
(now - lastTime) / 1000000.0;
lastTime = now;
// ========================================
// ИСХОДНЫЕ ДАННЫЕ
// ========================================
Serial.println("===== ИСХОДНЫЕ ДАННЫЕ =====");
Serial.print("ax = ");
Serial.print(ax);
Serial.print(" ay = ");
Serial.print(ay);
Serial.print(" az = ");
Serial.print(az);
Serial.println(" м/с^2");
Serial.print("wx = ");
Serial.print(wx, 4);
Serial.print(" wy = ");
Serial.print(wy, 4);
Serial.print(" wz = ");
Serial.print(wz, 4);
Serial.println(" рад/с");
Serial.print("Температура = ");
Serial.print(temperature);
Serial.println(" C");
// ========================================
// ЗАДАНИЕ 1 — ВЫВОД
// ========================================
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
// ========================================
// ----------------------------------------
// УГЛЫ ПО АКСЕЛЕРОМЕТРУ
// ----------------------------------------
// Крен
float roll_acc =
atan2(aBy, aBz);
// Тангаж
float pitch_acc =
atan2(
-aBx,
sqrt(aBy * aBy + aBz * aBz)
);
// Сохраняем старые значения углов
float oldRoll = roll;
float oldPitch = pitch;
// ========================================
// ROLL — КРЕН
// ========================================
roll =
(1.0 - alpha) *
(
oldRoll +
dt *
(
p
+ q * sin(oldRoll) * tan(oldPitch)
+ r * cos(oldRoll) * tan(oldPitch)
)
)
+ alpha * roll_acc;
// ========================================
// PITCH — ТАНГАЖ
// ========================================
pitch =
(1.0 - alpha) *
(
oldPitch +
dt *
(
q * cos(oldRoll)
- r * sin(oldRoll)
)
)
+ alpha * pitch_acc;
// ========================================
// YAW — РЫСКАНИЕ
// ========================================
yaw =
yaw + dt * (r - beta_z);
// ========================================
// ПЕРЕВОД В ГРАДУСЫ
// И ПРИВЕДЕНИЕ К 0...360
// ========================================
float roll_deg =
angle360(roll * 180.0 / PI);
float pitch_deg =
angle360(pitch * 180.0 / PI);
float yaw_deg =
angle360(yaw * 180.0 / PI);
// ========================================
// ЗАДАНИЕ 2 — ВЫВОД
// ========================================
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(" град");
Serial.println("==============================");
delay(100);
}