MPU6050 — 6-осевой акселерометр и гироскоп (IMU)

MPU6050 — это 6-осевой инерциальный датчик (IMU, Inertial Measurement Unit) производства InvenSense, объединяющий в одном корпусе 3-осевой МЭМС-гироскоп и 3-осевой МЭМС-акселерометр. Отличительная особенность чипа — встроенный DMP (Digital Motion Processor), аппаратный блок, способный самостоятельно выполнять сенсорное слияние (sensor fusion) и выдавать готовые кватернионы ориентации, снижая нагрузку на микроконтроллер. Чаще всего встречается в виде готового модуля GY-521. Используется в балансирующих роботах и сегвеях, квадрокоптерах, системах стабилизации изображения и управления жестами.

GY-521 (MPU6050) ITG

Распиновка модуля GY-521 (MPU6050)

ВыводОписание
VCCПитание
GNDЗемля
SCLЛиния тактирования I2C (Serial CLock)
SDAЛиния данных I2C (Serial DAta)
XDA / XCLВспомогательная шина I2C для подключения внешних датчиков (например, магнитометра)
AD0Выбор адреса I2C
INTВыход прерывания

Так выглядит модуль GY-521: вид сверху (на плате подписаны выводы и нанесены обозначения осей X и Y) и вид снизу (контактные площадки выводов).

Модуль GY-521 на MPU6050, вид сверху: чип, стабилизатор, конденсаторы и подписи выводов INT, AD0, XCL, XDA, SDA, SCL, GND, VCC
Модуль GY-521 на MPU6050, вид снизу: контактные площадки VCC, GND, SCL, SDA, XDA, XCL, AD0 и INT

Технические характеристики MPU6050

  • Интерфейс: I2C (до 400 кГц в Fast-mode). SPI есть только у родственной микросхемы MPU-6000, у MPU6050 его нет
  • Акселерометр: 3 оси, программируемый диапазон ±2/±4/±8/±16 g, ток потребления 500 мкА
  • Гироскоп: 3 оси, программируемый диапазон ±250/±500/±1000/±2000 °/с, ток потребления 3,6 мА
  • АЦП: 16-битный, по одному на каждую из 6 осей
  • Частота выборки: гироскоп 4–8000 Гц, акселерометр 4–1000 Гц; программируемые фильтры низких частот
  • Встроенный DMP: аппаратное сенсорное слияние, расчёт кватернионов; ток потребления при включённых 6 осях и DMP 3,9 мА
  • Встроенный датчик температуры: диапазон -40…+85 °C
  • Буфер FIFO: 1024 байта
  • Напряжение питания чипа (VDD): 2,375–3,46 В (на модуле GY-521 стоит стабилизатор для работы от 3,3–5 В)
  • I2C адрес: 0x68 (0x69, если AD0 подключен к VCC)
  • Ударопрочность: до 10 000 g
  • Размеры чипа: 4 х 4 х 0,9 мм (QFN)

Чувствительность: во сколько раз делить показания

Чип выдаёт «сырые» числа АЦП, а чтобы получить физические величины, их нужно делить на коэффициент, который зависит от выбранного диапазона. После включения питания по умолчанию выбраны диапазоны ±2 g и ±250 °/с.

Диапазон акселерометраКоэффициент, LSB/gДиапазон гироскопаКоэффициент, LSB/(°/с)
±2 g16384±250 °/с131
±4 g8192±500 °/с65,5
±8 g4096±1000 °/с32,8
±16 g2048±2000 °/с16,4

Например, если акселерометр лежит горизонтально (диапазон ±2 g), по оси Z получится около 16384, то есть +1 g.

Принцип работы и DMP

Гироскоп и акселерометр — это МЭМС-структуры: миниатюрные механические элементы на кристалле, изменяющие электрическую емкость при ускорении (акселерометр) или воздействии кориолисовой силы при вращении (гироскоп). Сырые данные (raw) с каждой оси представляют собой 16-битные знаковые числа (дополнительный код) в условных единицах АЦП — их нужно делить на коэффициент чувствительности (зависит от выбранного диапазона, см. таблицу выше) для получения g или °/с. Главная сложность в работе с IMU — дрейф и шум: акселерометр подвержен шуму и вибрациям, а показания гироскопа со временем «уплывают» (drift) из-за интегрирования угловой скорости. Встроенный DMP объединяет данные обоих сенсоров прямо в чипе и отдаёт уже готовую ориентацию в виде кватернионов, но настройка и работа с DMP существенно сложнее, чем чтение сырых данных: прошивку DMP нужно загружать в чип при каждом включении питания, это делает библиотека i2cdevlib.

Оси чувствительности и направление вращения

Все показания привязаны к осям X, Y и Z относительно корпуса микросхемы. На рисунке из даташита стрелками показаны положительные направления осей и положительное направление вращения вокруг каждой из них, а точка на корпусе обозначает вывод 1. Знак показаний гироскопа и акселерометра определяется именно этими направлениями, поэтому перед использованием в проекте стоит проверить, как модуль повёрнут относительно вашей конструкции. На модуле GY-521 направления осей X и Y нанесены на плату рядом с микросхемой (см. фото выше), ось Z направлена от платы к вам.

Оси чувствительности MPU-6050 и положительное направление вращения вокруг осей X, Y и Z, точка на корпусе обозначает вывод 1

Структурная схема MPU6050

Структурная схема из даташита одинаковая для MPU-6000 и MPU-6050. Названия выводов в круглых скобках относятся только к MPU-6000, а в квадратных (например, [VLOGIC] на выводе 8) только к MPU-6050.

Структурная схема MPU-6050: гироскопы и акселерометры с АЦП, блок обработки сигналов, процессор DMP, FIFO, регистры и интерфейсы I2C
  • Датчики. Три гироскопа (X, Y, Z) и три акселерометра, у каждого свой 16-битный АЦП, а затем общий блок обработки сигналов (Signal Conditioning). У каждого датчика есть собственный блок самотестирования (Self test). Рядом стоит датчик температуры с АЦП;
  • Регистры, FIFO и прерывания. Результаты измерений попадают в регистры данных (Sensor Registers) и в буфер FIFO на 1024 байта, а настройками управляют конфигурационные регистры (Config Registers). Выход INT (вывод 12) сообщает микроконтроллеру о новых данных;
  • DMP (Digital Motion Processor) обрабатывает данные датчиков и складывает результат в FIFO;
  • Основной интерфейс. Через выводы SCL и SDA микроконтроллер читает регистры, у MPU-6000 ещё есть SPI. У MPU6050 вывод 9 (AD0) задаёт младший бит I2C-адреса;
  • Вспомогательный интерфейс. Выводы AUX_DA и AUX_CL (6 и 7) — вторая шина I2C, по которой сам чип может читать внешние датчики, например магнитометр. Интерфейсный мультиплексор (Serial Interface Bypass Mux) позволяет соединить её напрямую с основной шиной;
  • Питание и тактирование. Внутренний стабилизатор (Bias & LDO) формирует напряжение REGOUT, а генератор напряжения (Charge Pump, вывод CPOUT) нужен для работы датчиков. Тактовый генератор (CLOCK) работает от внутреннего источника с точностью ±1% или от внешнего сигнала на выводе CLKIN (32,768 кГц или 19,2 МГц);
  • Вывод FSYNC (11) принимает внешний синхросигнал для синхронизации с видео или GPS.

Девятиосевое слияние данных по I2C: подключение магнитометра

Для определения абсолютного направления (компаса) к MPU6050 можно подключить внешний магнитометр. Чип умеет собирать данные с него сам: у него есть вторая, вспомогательная шина I2C (выводы AUX_DA и AUX_CL, на модуле GY-521 они выведены как XDA и XCL), а DMP объединяет данные гироскопа, акселерометра и магнитометра в 9-осевой результат.

Подключение магнитометра к MPU-6050 для 9-осевого слияния данных по I2C: шина процессора, вспомогательная шина датчиков и интерфейсный мультиплексор

Схема работает так:

  • микроконтроллер (системный процессор) подключён к основной шине I2C чипа и читает через неё все данные и настраивает всё остальное;
  • сам MPU6050 для магнитометра выступает ведущим устройством (мастером) на вспомогательной шине, но возможности у него ограничены, поэтому первоначально настроить магнитометр должен микроконтроллер;
  • для этого в чипе есть интерфейсный мультиплексор, который соединяет основную шину (выводы 23 и 24, SCL и SDA) напрямую со вспомогательной (выводы 6 и 7), и микроконтроллер видит магнитометр как будто он подключён к нему самому;
  • когда магнитометр настроен, мультиплексор нужно отключить, и тогда вспомогательный мастер чипа берёт управление шиной на себя и сам собирает данные с магнитометра.

Такой режим нужен только при подключении внешнего датчика, а для обычной работы с гироскопом и акселерометром вспомогательную шину можно не трогать. Подробности про управление вспомогательным мастером даташит выносит в раздел 10 и в документ Register Map.

Типовая схема включения чипа

Если вы делаете собственную плату на MPU6050, а не используете готовый модуль, обвязка чипа состоит всего из четырёх конденсаторов. На модуле GY-521 она уже установлена, вместе со стабилизатором напряжения и резисторами шины I2C.

Типовая схема включения MPU-6050 из даташита: конденсаторы C1 - C4 на выводах питания, VLOGIC и генератора напряжения, шины I2C
ДетальВывод чипаНазначениеПараметры
C110 (REGOUT)Конденсатор внутреннего стабилизатора0,1 мкФ, керамика X7R, ±10%, не менее 2 В
C213 (VDD)Блокировочный конденсатор питания0,1 мкФ, керамика X7R, ±10%, не менее 4 В
C320 (CPOUT)Конденсатор генератора напряжения (Charge Pump)2,2 нФ, керамика X7R, ±10%, 50 В
C48 (VLOGIC)Блокировочный конденсатор питания логики, только у MPU605010 нФ, керамика X7R, ±10%, не менее 4 В

Вывод VLOGIC задаёт уровни сигналов шины I2C: это 1,8 В ±5% или напряжение VDD. Порядок включения питания важен: напряжение VLOGIC никогда не должно быть выше VDD, оба напряжения должны нарастать плавно, время нарастания VDD (от 10% до 90%) не больше 100 мс, а VLOGIC не больше 3 мс. Выводы SDA, SCL и AD0, INT и FSYNC подключают так, как показано на схеме: AD0 к земле или к питанию, FSYNC, если синхронизация не нужна, к земле.

Подключение к Arduino

Arduino Uno/Nano/Pro MiniMPU6050
5VVCC
GNDGND
A4 (SDA)SDA
A5 (SCL)SCL

Библиотеки

Arduino:

  • i2cdevlib (jrowberg) — исходная и самая функциональная библиотека, включает полноценную работу с DMP (кватернионы, углы Эйлера).
  • Adafruit_MPU6050 — простая библиотека в стиле Adafruit Unified Sensor, без DMP, но с удобным API для сырых данных акселерометра/гироскопа и температуры.
  • MPU6050 (Electronic Cats) — форк i2cdevlib с добавленной поддержкой ESP32, ESP8266 и других платформ.

Raspberry Pi / Python: пакет mpu6050-raspberrypi (pip install mpu6050-raspberrypi), работает через smbus/smbus2, предоставляет методы get_accel_data(), get_gyro_data(), get_temp().

MicroPython (ESP32/ESP8266): сообществом поддерживаются несколько реализаций драйвера mpu6050.py, работающих через стандартный модуль machine.I2C.

Пример скетча: настройка и чтение данных без библиотек

Ниже полноценный пример, который работает только со стандартной библиотекой Wire. Он проверяет, что на шине действительно MPU6050, настраивает датчик, определяет смещение нуля гироскопа и выводит в Монитор порта ускорения в g, угловые скорости в °/с, температуру и углы наклона. Если датчик пропадёт с шины, программа сообщит об ошибке и настроит его заново.

Что делает программа

ШагРегистрЧто записываем или читаем
ПроверкаWHO_AM_I (0x75)Читаем байт: у MPU6050 всегда 0x68, независимо от состояния вывода AD0. Если ответа нет, значит, неверно подключены провода или адрес
СбросPWR_MGMT_1 (0x6B)Записываем 0x80: все регистры возвращаются к значениям по умолчанию. После сброса чип засыпает, поэтому нужна пауза 100 мс
ПробуждениеPWR_MGMT_1 (0x6B)Записываем 0x01: выходим из спящего режима и берём тактовую частоту от гироскопа оси X. Производитель рекомендует именно такой источник, он точнее встроенного генератора
ФильтрCONFIG (0x1A)Значение 3: цифровой фильтр с полосой около 44 Гц у акселерометра и 42 Гц у гироскопа, он подавляет шум и вибрации. Включённый фильтр задаёт частоту выборки гироскопа 1 кГц
Частота выборкиSMPLRT_DIV (0x19)Значение 9: частота = 1000 / (1 + 9) = 100 Гц
Диапазон гироскопаGYRO_CONFIG (0x1B)Биты 4..3: 0 = ±250 °/с, 1 = ±500, 2 = ±1000, 3 = ±2000. В примере 0
Диапазон акселерометраACCEL_CONFIG (0x1C)Биты 4..3: 0 = ±2 g, 1 = ±4, 2 = ±8, 3 = ±16. В примере 0
Чтение данныхACCEL_XOUT_H (0x3B)За один запрос читаем 14 байт подряд: ускорения X, Y, Z, температура, угловые скорости X, Y, Z. Каждое значение занимает два байта, старший байт идёт первым

Если вы меняете диапазоны, замените в скетче и коэффициенты ACCEL_LSB_PER_G и GYRO_LSB_PER_DPS на значения из таблицы чувствительности выше.

#include <Wire.h>

/*
  MPU6050 без сторонних библиотек: настройка и чтение данных по I2C.
  Плата: Arduino Uno/Nano/Pro Mini, модуль GY-521.
    VCC -> 5V, GND -> GND, SDA -> A4, SCL -> A5.
  Монитор порта: 115200 бод.
*/

// Адрес на шине I2C (0x69, если вывод AD0 подключён к питанию)
const uint8_t MPU_ADDR = 0x68;

// Регистры MPU6050 (из документа Register Map)
const uint8_t REG_SMPLRT_DIV   = 0x19;  // делитель частоты выборки
const uint8_t REG_CONFIG       = 0x1A;  // цифровой фильтр низких частот (DLPF)
const uint8_t REG_GYRO_CONFIG  = 0x1B;  // диапазон гироскопа
const uint8_t REG_ACCEL_CONFIG = 0x1C;  // диапазон акселерометра
const uint8_t REG_ACCEL_XOUT_H = 0x3B;  // с него начинаются 14 байт данных
const uint8_t REG_PWR_MGMT_1   = 0x6B;  // питание и источник тактовой частоты
const uint8_t REG_WHO_AM_I     = 0x75;  // всегда читается 0x68

// Коэффициенты для диапазонов ±2 g и ±250 °/с (см. таблицу чувствительности)
const float ACCEL_LSB_PER_G  = 16384.0;
const float GYRO_LSB_PER_DPS = 131.0;

// Сколько раз усреднять показания гироскопа при калибровке
const uint16_t CALIBRATION_SAMPLES = 200;
// Как часто выводить данные, мс
const uint16_t PRINT_INTERVAL_MS = 200;

// Смещение нуля гироскопа в «сырых» единицах (определяется при запуске)
int16_t gyroOffsetX = 0;
int16_t gyroOffsetY = 0;
int16_t gyroOffsetZ = 0;

uint32_t lastPrintTime = 0;

/*
  Записывает один байт в регистр.
  Возвращает true, если датчик подтвердил приём.
*/
bool writeRegister(uint8_t reg, uint8_t value) {
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(reg);
  Wire.write(value);
  return Wire.endTransmission() == 0;
}

/*
  Читает подряд len байт, начиная с регистра reg.
  Возвращает true, если все байты получены.
*/
bool readRegisters(uint8_t reg, uint8_t *buffer, uint8_t len) {
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(reg);
  // false: не освобождаем шину, чтобы сразу прочитать ответ
  if (Wire.endTransmission(false) != 0) {
    return false;
  }
  if (Wire.requestFrom(MPU_ADDR, len) != len) {
    return false;
  }
  for (uint8_t i = 0; i < len; i++) {
    buffer[i] = Wire.read();
  }
  return true;
}

/*
  Собирает число со знаком из двух байт (старший байт идёт первым).
*/
int16_t toInt16(const uint8_t *bytes) {
  return (int16_t)((bytes[0] << 8) | bytes[1]);
}

/*
  Читает все шесть осей и температуру одной посылкой.
  Результат в «сырых» единицах АЦП.
*/
bool readRaw(int16_t &ax, int16_t &ay, int16_t &az,
             int16_t &temp,
             int16_t &gx, int16_t &gy, int16_t &gz) {
  uint8_t data[14];
  if (!readRegisters(REG_ACCEL_XOUT_H, data, sizeof(data))) {
    return false;
  }
  ax = toInt16(&data[0]);
  ay = toInt16(&data[2]);
  az = toInt16(&data[4]);
  temp = toInt16(&data[6]);
  gx = toInt16(&data[8]);
  gy = toInt16(&data[10]);
  gz = toInt16(&data[12]);
  return true;
}

/*
  Настраивает датчик. Возвращает false, если он не отвечает
  или это не MPU6050.
*/
bool initMpu() {
  uint8_t whoAmI = 0;
  if (!readRegisters(REG_WHO_AM_I, &whoAmI, 1)) {
    Serial.println(F("MPU6050 не отвечает: проверьте провода и адрес"));
    return false;
  }
  if (whoAmI != 0x68) {
    Serial.print(F("Неожиданный WHO_AM_I: 0x"));
    Serial.println(whoAmI, HEX);
    return false;
  }

  // Сброс всех регистров в значения по умолчанию (бит DEVICE_RESET)
  writeRegister(REG_PWR_MGMT_1, 0x80);
  delay(100);

  // Выходим из спящего режима и берём тактовую частоту от гироскопа по оси X:
  // так рекомендует производитель, это точнее встроенного генератора
  if (!writeRegister(REG_PWR_MGMT_1, 0x01)) {
    return false;
  }
  delay(50);

  // Цифровой фильтр низких частот: значение 3 это около 44 Гц у акселерометра
  // и 42 Гц у гироскопа. Он убирает высокочастотный шум и вибрации.
  // Частота выборки гироскопа при включённом фильтре равна 1 кГц
  writeRegister(REG_CONFIG, 3);

  // Частота выборки = 1000 Гц / (1 + SMPLRT_DIV) = 1000 / (1 + 9) = 100 Гц
  writeRegister(REG_SMPLRT_DIV, 9);

  // Диапазон гироскопа: 0 = ±250 °/с, 1 = ±500, 2 = ±1000, 3 = ±2000 (биты 4..3)
  writeRegister(REG_GYRO_CONFIG, 0 << 3);

  // Диапазон акселерометра: 0 = ±2 g, 1 = ±4, 2 = ±8, 3 = ±16 (биты 4..3)
  writeRegister(REG_ACCEL_CONFIG, 0 << 3);

  delay(100);
  return true;
}

/*
  Определяет смещение нуля гироскопа. Во время калибровки
  датчик должен лежать неподвижно.
*/
void calibrateGyro() {
  int32_t sumX = 0;
  int32_t sumY = 0;
  int32_t sumZ = 0;
  uint16_t good = 0;

  for (uint16_t i = 0; i < CALIBRATION_SAMPLES; i++) {
    int16_t ax, ay, az, temp, gx, gy, gz;
    if (readRaw(ax, ay, az, temp, gx, gy, gz)) {
      sumX += gx;
      sumY += gy;
      sumZ += gz;
      good++;
    }
    delay(10);  // выборка идёт с частотой 100 Гц
  }

  if (good > 0) {
    gyroOffsetX = sumX / good;
    gyroOffsetY = sumY / good;
    gyroOffsetZ = sumZ / good;
  }
}

void setup() {
  Serial.begin(115200);
  Wire.begin();
  Wire.setClock(400000);  // быстрый режим I2C, 400 кГц

  if (!initMpu()) {
    Serial.println(F("Остановка: датчик не настроен"));
    while (true) {
      delay(1000);
    }
  }

  Serial.println(F("Калибровка гироскопа: не двигайте датчик..."));
  calibrateGyro();
  Serial.println(F("Готово"));
}

void loop() {
  if (millis() - lastPrintTime < PRINT_INTERVAL_MS) {
    return;
  }
  lastPrintTime = millis();

  int16_t rawAx, rawAy, rawAz, rawTemp, rawGx, rawGy, rawGz;
  if (!readRaw(rawAx, rawAy, rawAz, rawTemp, rawGx, rawGy, rawGz)) {
    Serial.println(F("Ошибка чтения, пробуем настроить датчик заново"));
    initMpu();
    return;
  }

  // Перевод в физические величины
  float ax = rawAx / ACCEL_LSB_PER_G;                   // g
  float ay = rawAy / ACCEL_LSB_PER_G;
  float az = rawAz / ACCEL_LSB_PER_G;
  float gx = (rawGx - gyroOffsetX) / GYRO_LSB_PER_DPS;  // °/с
  float gy = (rawGy - gyroOffsetY) / GYRO_LSB_PER_DPS;
  float gz = (rawGz - gyroOffsetZ) / GYRO_LSB_PER_DPS;
  float temperature = rawTemp / 340.0 + 36.53;          // °C, формула из Register Map

  // Углы наклона по акселерометру (в покое): крен вокруг X, тангаж вокруг Y
  float roll = atan2(ay, az) * 180.0 / PI;
  float pitch = atan2(-ax, sqrt(ay * ay + az * az)) * 180.0 / PI;

  Serial.print(F("Acc g: "));
  Serial.print(ax, 2); Serial.print(' ');
  Serial.print(ay, 2); Serial.print(' ');
  Serial.print(az, 2);
  Serial.print(F(" | Gyro °/с: "));
  Serial.print(gx, 1); Serial.print(' ');
  Serial.print(gy, 1); Serial.print(' ');
  Serial.print(gz, 1);
  Serial.print(F(" | T: "));
  Serial.print(temperature, 1);
  Serial.print(F(" °C | Крен: "));
  Serial.print(roll, 1);
  Serial.print(F("° Тангаж: "));
  Serial.print(pitch, 1);
  Serial.println(F("°"));
}

Результат

В Мониторе порта (скорость 115200 бод) сначала появится сообщение о калибровке гироскопа, во время которой датчик должен лежать неподвижно, а затем строки такого вида:

Acc g: -0.07 -0.01 1.09 | Gyro °/с: -0.1 0.0 -0.1 | T: 27.1 °C | Крен: -0.5° Тангаж: 3.5°

Так выглядит вывод на реальном модуле GY-521 с Arduino Uno. Датчик лежит на столе: ускорение по оси Z около 1 g, угловые скорости близки к нулю, потому что гироскоп откалиброван при запуске. Заметно и смещение нуля акселерометра: по оси X он показывает −0,07 g, а по оси Z 1,09 g. Из-за этого датчик «думает», что наклонён на 3,5° по тангажу, хотя лежит ровно. В последних строках датчик взяли в руки и наклонили: ускорения, углы и угловые скорости заметно изменились.

Монитор порта Arduino IDE: калибровка гироскопа MPU6050 и строки с ускорениями, угловыми скоростями, температурой, креном и тангажом

Акселерометр в примере не калибруется, поэтому в покое его показания отличаются от идеальных. По даташиту начальное отклонение нуля может достигать ±50 mg по осям X и Y и ±80 mg по оси Z, а у конкретного модуля оно бывает и больше. Отклонение в 0,07 g даёт ошибку угла около 4°, так что для точных углов акселерометр тоже нужно калибровать. Крен (roll) и тангаж (pitch) в примере считаются только по акселерометру и работают, пока датчик неподвижен или движется плавно. Резкие движения создают ускорения, которые искажают угол. В разделе про комплементарный фильтр ниже показано, как соединить их с данными гироскопа.

Применение

  • Балансирующие роботы и сегвеи (определение угла наклона для удержания равновесия);
  • Квадрокоптеры и другие дроны (стабилизация полёта по показаниям гироскопа и акселерометра);
  • Системы стабилизации изображения (gimbal) для камер;
  • Управление жестами (наклон контроллера меняет поведение устройства);
  • Шагомеры и трекеры активности (обнаружение характерных пиков ускорения при ходьбе);
  • Определение ориентации устройства (портретная/ландшафтная) в самодельных гаджетах.

Калибровка смещения нуля (offset)

Из-за технологического разброса МЭМС-структур у каждого экземпляра MPU6050 есть собственное отклонение нулевой точки (offset) — если положить датчик на идеально горизонтальную поверхность, акселерометр по оси Z покажет не ровно 1g, а, например, 0,96g, а гироскоп в состоянии покоя может показывать небольшое ненулевое значение вместо нуля. По даташиту начальное отклонение нуля акселерометра может достигать ±50 mg по осям X и Y и ±80 mg по оси Z, а гироскопа — ±20 °/с. Перед точными измерениями калибровку выполняют одним из способов:

  • Снять показания в состоянии покоя несколько сотен раз, усреднить и вычесть полученное смещение из всех последующих измерений (простой программный способ);
  • Воспользоваться готовым скетчем калибровки IMU_Zero из состава библиотеки i2cdevlib — он автоматически подбирает регистры смещения (offset registers), встроенные в сам чип;
  • Для гироскопа дополнительно учитывать температурный дрейф — смещение нуля гироскопа слегка меняется при изменении температуры чипа (до ±20 °/с во всём рабочем диапазоне температур).

Сенсорное слияние: простой фильтр без DMP

Если DMP кажется избыточно сложным, базовое сенсорное слияние (получение стабильного угла наклона без дрейфа гироскопа и без шума акселерометра) можно реализовать простым комплементарным фильтром:

/*
  angleAcc - угол, вычисленный из акселерометра (подвержен шуму, но не дрейфует)
  gyroRate - угловая скорость с гироскопа, град/с
  dt - интервал времени между измерениями, секунды
*/
float angle = 0;

angle = 0.98 * (angle + gyroRate * dt) + 0.02 * angleAcc;

Коэффициенты 0,98/0,02 определяют, насколько фильтр «доверяет» гироскопу (быстрый, но дрейфующий) против акселерометра (медленный, но не дрейфующий) — это самый простой способ получить стабильный угол без написания полноценного фильтра Калмана.

MPU6050 и аналоги

МодульОтличие от MPU6050
MPU9250 / MPU9255Добавлен 3-осевой магнитометр (9 DOF) — позволяет определять абсолютное направление (компас), а не только относительный наклон
MPU6500Преемник MPU6050: более низкий уровень шума, поддержка SPI, питание от 1,71 В; как и у MPU6050, магнитометра нет
BMI160 / BMI270 (Bosch)Альтернативные 6-осевые IMU с меньшим энергопотреблением, популярны во фитнес-трекерах

Типичные проблемы

  • Датчик не отвечает по I2C (ошибка подключения) — проверьте, что используется правильный I2C-адрес (0x68 по умолчанию, 0x69 при AD0=HIGH), а также качество пайки/контактов на выводах SDA/SCL;
  • Показания гироскопа «уплывают» с течением времени — это нормальное физическое свойство МЭМС-гироскопов (дрейф); решается либо периодической коррекцией по акселерометру (см. комплементарный фильтр выше), либо использованием встроенного DMP;
  • Сильный шум в показаниях акселерометра — механические вибрации платформы (моторы, винты дрона) передаются через корпус; помогает демпфирующее крепление модуля (резиновые проставки) и программное усреднение.

Материалы и Datasheet

Оставьте комментарий

Отправляя комментарий, вы соглашаетесь с обработкой ваших данных (имя, адрес электронной почты, текст комментария) в соответствии с политикой конфиденциальности.