Подключение гироскопа-акселерометра MPU6050 к Wemos d1

Для того чтобы наше устройство могло отображать и записывать в лог ускорение и перегрузки во время разгона, мы подключим к нему популярный высокоточный модуль GY-521, на основе чипа MPU-6050, являющегося трех-осевым гироскопом и трех-осевым акселерометром. Гироскоп нам пока не понадобится, поэтому в этой части я буду рассказывать только про акселерометр.

Основные характеристики MPU6050:

  • 16-битный АЦП
  • Напряжение питания 3-5В
  • Поддержка протокола I2C
  • Диапазон ускорений: ± 2 ± 4 ± 8 ± 16g
  • Диапазон «гиро»: ± 250 500 1000 2000 °/s

Подключение MPU6050 к Wemos d1

Модуль работает по I2C, поэтому подключить его весьма просто, соединим акселерометр с платой Wemos D1: VCC -> 3V3, GND -> GND, SCL -> D1, SDA -> D2.

Для проверки давайте зальём в плату следующий скетч:

#include <Wire.h>

const int MPU_addr = 0x68; // I2C address of the MPU-6050
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;

void setup() {
  Serial.begin(115200);
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B); // PWR_MGMT_1 register
  Wire.write(0);    // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
}

void loop() {
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr, 14, true); // request a total of 14 registers

  AcX = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
  AcY = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp = Wire.read() << 8 | Wire.read(); // 0x41 (TEMP_OUT_H)  & 0x42 (TEMP_OUT_L)
  GyX = Wire.read() << 8 | Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY = Wire.read() << 8 | Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ = Wire.read() << 8 | Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)

  Serial.print("AcX = "); Serial.print(AcX);
  Serial.print(" | AcY = "); Serial.print(AcY);
  Serial.print(" | AcZ = "); Serial.print(AcZ);
  Serial.print(" | Tmp = "); Serial.print(Tmp / 340.00 + 36.53); // temperature in degrees C from datasheet
  Serial.print(" | GyX = "); Serial.print(GyX);
  Serial.print(" | GyY = "); Serial.print(GyY);
  Serial.print(" | GyZ = "); Serial.println(GyZ);

  delay(500);
}

Скачать архив со скетчем: MPU6050.zip

Теперь, если вы откроете терминал com порта, то увидите примерно следующую картину: ускорение по трём осям, температура, углы положения по трем осям.

Из даташита, мы знаем, что при настройке чувствительности акселерометра до 2g на каждую единицу g приходится 16384 единиц измерения датчика. Самые наблюдательные наверно уже заметили по фото, что лежа на столе датчик почему-то показывает ускорения по горизонтальным осям, а по вертикальной показывает значение больше 16384 (я надеюсь все помнят со школы, что на все объекты на земле действует сила тяжести в 1g). Это проблема всех подобных акселерометров – они очень шумят. Их обязательно нужно калибровать и фильтровать получаемые данные.

Фильтр Калмана MPU6050

Давайте добавим в скетч упрощенный фильтр Калмана, для того чтобы сгладить шумы датчика.

#include <Wire.h>

const int MPU_addr = 0x68; // I2C address of the MPU-6050
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;

// переменные для Калмана
float varVolt = 78.9;   // среднее отклонение (ищем в excel)
float varProcess = 0.5; // скорость реакции на изменение (подбирается вручную)
float Pc = 0.0, G = 0.0, P = 1.0, Xp = 0.0, Zp = 0.0, Xe = 0.0;
// переменные для Калмана

// Функция фильтрации
float filter(float val) {
  Pc = P + varProcess;
  G = Pc / (Pc + varVolt);
  P = (1 - G) * Pc;
  Xp = Xe;
  Zp = Xp;
  Xe = G * (val - Zp) + Xp; // "фильтрованное" значение
  return (Xe);
}

void setup() {
  Serial.begin(115200);
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B); // PWR_MGMT_1 register
  Wire.write(0);    // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
}

void loop() {
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr, 6, true);  // request a total of 14 registers
  AcX = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
  AcY = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)

  Serial.print(AcZ); Serial.print(" "); Serial.println((int)filter(AcZ));
  delay(100);
}

Скачать архив со скетчем: MPU6050Kalman.zip

Для фильтра нам необходимо подобрать два значения:

1) varVolt - это среднее отклонение, его можно либо высчитать самостоятельно либо забить значения в файл excel который я положил в архив со скетчем

2) varProcess - это время реакции, чем меньше это число, тем плавнее будут меняться значения.

Попробуйте поиграться с этими параметрами.

Теперь зайдем в меню «Инструменты – Плоттер по последовательному соединению», откроется вот такое окно:

Синяя линия - это не фильтрованные данные, а красная это после обработки фильтром Калмана. Пики это - я поднимал и опускал акселерометр примерно на пол метра над столом. С такими данными уже можно будет комфортно работать.

На этом пока всё, в одной из следующих частей мы с вами соберем полноценный G-meter.