Симулятор MPU6050 онлайн: акселерометр и гироскоп без платы
MPU6050 у меня лежал полгода, пока не нашёлся проект — детектор тряски в охранной коробке. Скетч из интернета молча выводил в монитор сырые ax/ay/az, и я три вечера гадал, почему угол по X уезжает в минус при наклоне вправо. Сейчас бы я завёл тот скетч сначала здесь: тянете мышью виртуальную платку, видите угол и сырые int16 в мониторе — и сразу понятно, что к чему, без паяльника и одного нерабочего вечера.
Модуль ставится на стенд симулятора из каталога: бирюзовый провод с меткой MPU, на плашке у колодки — два контакта SDA/SCL на A4/A5. Тащите платку мышью — она наклоняется в перспективе, углы angleX и angleY меняются от −90 до +90, а getMotion6 отдаёт шесть значений в массив, как живой датчик. Ниже — стартовый скетч, детектор тряски на пороге 1,5g, честная граница модели и грабли, на которые я наступил сам.
Что эмулируется на стенде
MPU6050 в каталоге компонентов — деталь «Акселерометр MPU6050»: два провода SDA/SCL на A4/A5, бирюзовый провод с меткой MPU. Сам модуль нарисован как маленькая плата: тянете её мышью — платка наклоняется в перспективе, угол по X и Y едет от −90 до +90 градусов. Внутри движка значения хранятся как int16 «сырые» (ax, ay, az, gx, gy, gz), а скетчу отдаются через ax()/ay()/az() или getMotion6(arr) — ровно тот API, что в библиотеке Adafruit_MPU6050 и в старом I2Cdev. На моей памяти это самый быстрый способ понять, что 16384 LSB это 1g, а не 1: перепутаешь — порог тряски сработает на собственном притяжении.
- Наклон мышью: платка поворачивается в перспективе, angleX/angleY меняются синхронно — глазом видно, куда «упал» модуль.
- begin() печатает «[MPU6050 на шине]»: подтверждение, что датчик нашёлся, как в реальном I2C-сканере.
- getAngleX/Y возвращают угол в градусах с одним знаком после запятой — те же числа, что в мониторе на живой плате.
- Сырые ax/ay/az: int16, как в библиотеке — 16384 LSB на 1g; для своего комплементарного фильтра получаете честные единицы.
- testConnection() отвечает 1: дубль для скетчей, которые проверяют связь при старте.
Стартовый скетч: угол наклона в монитор
#include <Wire.h>#include <MPU6050.h> MPU6050 mpu; void setup() { Wire.begin(); Serial.begin(9600); mpu.initialize(); // на стенде: "[MPU6050 на шине]" if (mpu.testConnection()) { Serial.println("MPU6050 OK"); }} void loop() { Serial.print("X="); Serial.print(mpu.getAngleX(), 1); Serial.print(" Y="); Serial.println(mpu.getAngleY(), 1); delay(100);}Запустите скетч, тяните платку мышью — монитор печатает углы, игла на плашке повторяет наклон. Те же строки в реальной сборке выводит урок про определение наклона — там же схема с подтяжками 4,7 кОм на SDA/SCL и пояснение, почему угол в браузере без шумов, а живой датчик «дрейфит».
Детектор тряски: порог по ax
#include <Wire.h>#include <MPU6050.h> MPU6050 mpu;const float POROG = 1.5; // g void setup() { Wire.begin(); Serial.begin(9600); mpu.initialize(); pinMode(13, OUTPUT);} void loop() { float a = (float)mpu.ax() / 16384.0; // 16384 LSB = 1g if (abs(a) > POROG) { digitalWrite(13, HIGH); Serial.print("TRIASKA a="); Serial.println(a, 2); } else { digitalWrite(13, LOW); } delay(20);}Порог 1,5g — компромисс между чувствительностью и ложными тревогами от хлопка двери: меньше 1g ловит собственное притяжение, больше 2g пропускает лёгкий удар. В браузере тряска эмулируется быстрым движением мыши — наклонили платку резко, ax уехал выше 1,5g, светодиод L на D13 загорелся. Полный разбор порога, антидребезга по окну и гистерезиса ведёт урок про детектор тряски и удара.
Граница: модель против живого модуля
| Проверка | Симулятор | Только железо |
|---|---|---|
| Угол по X и Y: getAngleX/Y | да: наклон мышью → число | то же, плюс шум и дрейф |
| Сырые ax/ay/az (int16) | да: 16384 LSB на 1g | то же, плюс температурный уход |
| testConnection() и begin() | да: имитация «на шине» | то же, плюс реальные 0x68/0x69 |
| Гироскоп gx/gy/gz и дрейф нуля | нет: углы стабильны | да — дрейф 1-3°/мин требует компенсации |
| Калибровка офсетов и self-test | нет: модель без смещений | да — калибровка по шести положениям |
| Температурный уход и самонагрев | нет: модель изотермическая | да — до 0,5 °C на кристалле |
Движок знает угол, а не модуль: gx/gy/gz хранятся как нули, и дрейф нуля не моделируется — в браузере гироскоп «идеальный», а живой уезжает на градус-три в минуту. Поэтому комплементарный и калмановский фильтры, которые лечат дрейф, проверяют на плате: теория работает в симе, но численных констант без живого датчика не получить. Полный разбор фильтра от дрейфа — в уроке про гироскоп и фильтр.
Грабли MPU6050 в браузере и на плате
- Wire.begin() забыт: без него I2C не стартует, mpu.initialize() молчит — в симе и на плате одинаково. Строка Wire.begin() до mpu.initialize(), а не после.
- Перепутанные оси: getAngleX справа, getAngleY в коде слева — тянете мышью вверх, а число едет не туда. Сверяйте ось с наклоном в симуляторе за минуту.
- Порог в «сырых» LSB: 16384 это 1g, а не 1 — пишите (float)ax/16384.0, иначе порог 1500 ловит всё подряд.
- Адрес 0x68 против 0x69: у модуля с AD0 на +3V3 адрес 0x69; в браузере один адрес, на плате два — уточняйте по перемычке.
- Калибровка в симе «работает» без цифр: офсеты в модель не заложены, и в живом датчике их надо снимать скетчем из примеров библиотеки — не прячьте за кнопкой «калибровать» в браузере.