Уровень для полки в кладовке я собрал за вечер, а следующий потратил на борьбу с ним три. Скетч печатал az, и на ровно уложенной плате там стояло 16384. Я решил: брак. Переткнул вторую GY-521 — снова 16384. Третья из набора — то же самое. К полуночи я уже гуглил, где продают MPU6050 без дефектов, а утром дошло: один g — это и есть 16384 единицы. Гравитация не выключается, и ровный стол кладёт её целиком на ось Z. Датчик был прав во всех трёх случаях, а заблуждался я.
Тот вечер подарил мне привычку: прежде чем верить библиотеке, посмотри, что чип отдаёт сам. В знакомстве с MPU6050 готовые углы рисовались из коробки — здесь спускаемся этажом ниже: читаем регистры 0x3B–0x40 голым Wire, склеиваем 16-битные значения, переводим их в g и находим настоящий ноль на ровном столе. Без этой базы пороги детектора ударов и фильтры балансира остаются магией: непонятно, откуда взялись 16384 и почему ноль не ноль.
Сырые данные — целые числа, которые чип выдаёт без всякой обработки: по одному 16-битному знаковому значению на ось, в дополнительном коде. Библиотека берёт ровно те же байты и делит их на 16384 — но прячет две вещи: сколько времени занимает обмен и что между нулем и вашим порогом нет никакого волшебства. Чтение шести байтов по шине на 100 кГц укладывается примерно в 0,8 мс вместе с адресной фазой. Для детектора удара, где надо опрашивать чип каждые 5 мс, это не роскошь, а условие выживания.
Механика обмена — та же, что в уроке про чтение регистров: передали адрес регистра, запросили байты, склеили по два. Нюансов три, и каждый стоит вечера, если узнать о нём на практике: чип после подачи питания спит и требует пробуждения; старший байт идёт первым; а склейку надо приводить к знаковому типу, иначе минусовая перегрузка превратится в число около 60 000.
Пара слов про сам формат. Значение лежит в пределах от −32768 до 32767, и при диапазоне ±2g потолок шкалы наступает чуть раньше двух g — на 32767 единицах всё сильнее обрубается. Ноль в покое у гироскопа, но не у акселерометра: на одной из его осей всегда висит g. Поэтому сырые значения разных осей и разных плат сравнивать между собой бесполезно — сначала деление на масштаб, и уже g можно сравнивать с g.
Про знак отдельно, раз всплыл минус. В 16-битном слове в дополнительном коде знак сидит в старшем бите: бит взведён — значение отрицательное. Чтение через побитовые сдвиги знаку не мешает, если результат сразу попадает в знаковый 16-битный тип: int16_t ax = (hi << 8) | lo одинаково работает на Uno и на Mega. А вот мой фирменный тупик: как-то я собрал то же значение в long, рассудив, что широкий тип надёжнее, — и получил 49152 вместо минус 16384. В широком типе знак за вас никто не растягивает: единица из старшего бита остаётся в величине, и перегрузка переворачивается. Приведение результата к int16_t — одна строчка в скобках, и с тех пор она в моих шаблонах неприкосновенна.
Регистры 0x3B–0x40: шесть байтов на три оси
Карта простая: 0x3B и 0x3C — ось X (старший и младший байт), 0x3D и 0x3E — Y, 0x3F и 0x40 — Z. Дальше 0x41–0x42 отдаёт температуру кристалла, а с 0x43 начинается гироскоп — до него доберёмся в следующем уроке. Полная карта чипа со служебными и конфигурационными регистрами заслуживает отдельного вечера с даташитом; здесь — только рабочий путь от акселерометра.
Перед первым чтением чип будят: в регистр 0x6B (PWR_MGMT_1) пишется 0x00, что сбрасывает бит сна. MPU6050 после подачи питания засыпает сам, ток падает до микроампер, регистры данных молчат. Симптом узнаваемый: сканер шины плату видит, запросы проходят, а числа стоят на месте. Я однажды полчаса читал стабильные нули, пока не заставил себя открыть раздел даташита про режимы питания.
Адрес модуля — 0x68 при AD0 на земле и 0x69 при AD0 на питании. Хуже того, 0x68 делится с часами DS3231: две такие платы на одной шине конфликтуют молча, обе отвечают чепухой. После пробуждения стоит проверить, с кем вы говорите: регистр WHO_AM_I по адресу 0x75 возвращает 0x68. Я добавляю эту проверку в setup, когда шина длинная или на ней уже живут другие устройства — ложный адрес читается как ноль или 255, и скетч сразу честно сообщает, что общается не с тем, а не печатает чушь полчаса.
// MPU6050: сырые данные акселерометра без библиотек #include <Wire.h> const byte MPU = 0x68; // AD0 на земле; с AD0 на питании — 0x69const float G_LSB = 16384.0; // единиц на один g при диапазоне ±2g void setup() { Serial.begin(9600); Wire.begin(); // Будим чип: PWR_MGMT_1 (0x6B), бит сна в ноль Wire.beginTransmission(MPU); Wire.write(0x6B); Wire.write(0x00); Wire.endTransmission();} void loop() { // Просим шесть байтов, начиная с 0x3B (ACCEL_XOUT_H) Wire.beginTransmission(MPU); Wire.write(0x3B); Wire.endTransmission(false); Wire.requestFrom(MPU, 6); int16_t hi = Wire.read(); int16_t lo = Wire.read(); int16_t ax = (hi << 8) | lo; hi = Wire.read(); lo = Wire.read(); int16_t ay = (hi << 8) | lo; hi = Wire.read(); lo = Wire.read(); int16_t az = (hi << 8) | lo; Serial.print("ax="); Serial.print(ax); Serial.print(" ay="); Serial.print(ay); Serial.print(" az="); Serial.print(az); Serial.print(" gx="); Serial.print(ax / G_LSB, 2); Serial.print(" gz="); Serial.println(az / G_LSB, 2); delay(200);}16384 единицы на один g
| Диапазон | Единиц на g | Цена единицы | Где пригодится |
|---|---|---|---|
| ±2g | 16384 | 61 мкг | наклоны, уровень, бытовая робототехника |
| ±4g | 8192 | 122 мкг | маятники, качели, медленные платформы |
| ±8g | 4096 | 244 мкг | тряска, рывки, детектор ударов |
| ±16g | 2048 | 488 мкг | падения и жёсткие удары |
Диапазон выбирается битами AFS_SEL в регистре 0x1C, по умолчанию стоит ±2g — отсюда делитель 16384. Менять его стоит, когда данных не хватает: для уровня беру ±2g и радуюсь мелкой ступеньке в 61 мкг, для ударов — ±8g, иначе сильный толчок упрётся в потолок шкалы, и модуль честно покажет его максимум.
Лестницу делителей легко восстановить без даташита: весь 16-битный запас 32768 делится на потолок диапазона. Потолок два g — 16384 единицы на g, четыре — 8192, восемь — 4096, шестнадцать — 2048. Каждый шаг диапазона — обмен: при ±2g цена единицы 61 мкг, и уровень различает наклон в доли градуса; при ±16g единица стоит уже 488 мкг, зато удар в 12 г ложится в шкалу без обрубка. Я как-то поставил самый широкий диапазон для перестраховки — и размыл показания уровня: порог в четверть градуса в единицах ±16g оказался так мелок, что шум подобрался к нему вплотную. Сначала фон задачи, потом диапазон — не наоборот.
Оси на плате GY-521 подписаны на шелкографии: X вдоль длинной стороны, Y поперёк, Z торчит перпендикулярно из платы. Плата лежит плашмя — гравитация смотрит в стол, ось Z направлена туда же, и az стоит около +16384. Перевернули вверх ногами — минус 16384. Поставили на ребро по X — ax уйдёт к 16384, а az обнулится. Из этих проекций atan2 в уроке про угол с фильтром делает градусы. В симуляторе всё то же самое: тянете платку мышью — сырые значения бегут по Serial, как с живого чипа, а светодиод в скетче ниже следит за наклоном по X.
Проверка осей руками занимает минуту. Плашмя — az около 16384, ax и ay около нуля. На ребре по X — ax около 16384, az около нуля. Вверх ногами — az около минус 16384. Если числа перепутаны местами, просто поверните модуль в корпусе, пока логика проекта не совпадёт с шелкографией: оси — вопрос монтажа, а не брака, перепаивать ничего не нужно.
Скорость — главный аргумент сырых данных. Пакет в шесть байтов с адресной фазой занимает около 0,8 мс на 100 кГц; даже с печатью в Serial скетч опрашивает чип сто-двести раз в секунду, а без печати — все пятьсот. При опросе каждые 5 мс шина занята примерно шестую часть времени, остальные её устройства даже не заметят. Если ловите короткое событие, окно в несколько миллисекунд — разница между поймал и проморгал.
Что делать с числами дальше: пороги по модулю вектора — проект детектора ударов, углы через atan2 — урок про наклон, гироскоп с интегрированием — следующий урок. Распиновка GY-521, подтяжки и питание — на лендинге про подключение MPU6050, там же таблица проводов для тех, кто разворачивает модуль шелкографией к себе.
- 1. Плата лежит плашмя на столе. Что покажет az при диапазоне ±2g?
- 2. Сколько байтов занимают сырые значения трёх осей акселерометра?
- 3. Что нужно сделать перед первым чтением регистров данных?
- 4. При диапазоне ±8g сколько единиц приходится на один g?
- 5. Ровный стол, плата плашмя, az = 16500 вместо 16384. Что это?
- Сырые значения лежат с 0x3B по 0x40: шесть байтов, по два на ось, старший первым; склейка — (hi << 8) | lo в знаковом типе.
- Перед первым чтением сбросьте бит сна: байт 0x00 в регистр 0x6B — иначе сканер шины видит плату, а данные молчат.
- 16384 единицы = 1 g при ±2g: ровная плата даёт az около 16384, и это гравитация, а не дефект.
- Заводской сдвиг нуля до ±50 мг убирается калибровкой: среднее из 200–300 замеров на ровном столе вычитается всегда.
- Диапазон ставится в регистре 0x1C: ±2g для наклонов, ±8g и шире — для ударов; чтение шести байтов занимает около 0,8 мс.
Частые вопросы по теме урока
- Плата лежит плашмя на столе. Что покажет az при диапазоне ±2g?
- Гравитация не отключается: в покое она целиком проецируется на вертикальную ось Z, а 16384 единицы при ±2g — это ровно 1 g. 16384 — проекция гравитации на ось Z
- Сколько байтов занимают сырые значения трёх осей акселерометра?
- Каждая ось — 16-битное знаковое число из двух байтов: старший идёт первым (0x3B), младший за ним (0x3C). 6 — по два на ось, старший байт первым
- Что нужно сделать перед первым чтением регистров данных?
- По умолчанию MPU6050 засыпает: ток падает до микроампер, регистры данных молчат. Байт 0x00 в 0x6B будит чип. Записать 0x00 в PWR_MGMT_1 (0x6B) — чип после питания спит
- При диапазоне ±8g сколько единиц приходится на один g?
- 16384 — это для ±2g. С расширением диапазона в четыре раза чувствительность падает в четыре раза: 4096 единиц на g. 4096
- Ровный стол, плата плашмя, az = 16500 вместо 16384. Что это?
- Паспортный допуск смещения нуля — порядка ±50 мг, то есть до ±820 единиц. Калибровка средним из 200–300 замеров убирает его. Заводской сдвиг нуля — замерить среднее при старте и вычитать