Пример подключения акселерометр гироскоп модуль для arduino. Подключаем к Arduino акселерометр MMA7361

После выхода iPhone 4 многие много внимания было уделено новому дисплею, корпусу и прочим важным вещам. И лишь мимоходом отметили замену акселерометров на гироскопы для улучшения управления в играх. В своей статье для «Компьютерного обозрения» я отметил этот момент, в следствие чего даже консультировал одного из читателей. Почему бы не уделить этому моменту внимание и не разобраться зачем одни датчики были заменены на другие и чем они собственно отличаются?

Начнем с того, что и акселерометры и гироскопы являются инерционными датчиками. Акселерометры (лат. accelero - ускоряю и?????? - измеряю) - приборы, предназначенные для имерения проекции кажущегося ускорения.

Простейшая модель акселлерометра

В данном случае русская Википедия дает на удивление неплохое определение . В случае с мобильными телефонами датчики реагировали на изменение вектора ускорения свободного падения и все последующие действия исходили из этого.

Условная схема определения положения устройства в пространстве с применением двух акселлерометров

Точность в результате была довольно низкой, так как угол поворота устройства в пространстве напрямую измерить таким образом невозможно, лишь примерно оценить. На практике это выражалось в задумчивости поворота экранов, ложных срабатываниях и т.д. Какие же преимущества дает гироскоп и чем он собственно отличается?

Определение на Вики настолько далеко от общего, что обратимся к первоисточнику.

Впервые определение гироскопу дал Леон Фуко, назвавший так свой прибор, с помощью которого он наблюдал суточное вращение Земли. В Большой Советской Энциклопедии приводится следующее «Гироскоп - быстро вращающееся твердое тело, ось которого может изменять свое направление в пространстве». В современных гироскопах могут происходить разнообразные физические процессы, не обязательно основанные на вращении твердого тела. Хотя и классические гироскопы все еще применяются.

Примеры гироскопов. Банальный волчок по своей природе является гироскопом.

Примером классического гироскопа является ротор в кардановом подвесе. При вращении ротора он будет сохранять неизменным свое положение в пространстве независимо от движения основания. Таким образом можно измерять угол поворота основания, а соответственно и корабля/самолета etc. Именно по гирокомпасам ходят суда и летают самолеты, не полагаясь на примерные показания магнитного компаса, особенно в полярных широтах, а данные о положении самолета в пространстве получаются с гировертикали и гирогоризонта.

Естественно, классический гироскоп не может применяться в электронике. Для этого используются вибрационные микромеханические гироскопы - датчики угловой скорости. Чувствительный элемент таких приборов закреплен, при попытке его поворота возникает кориолисова сила, пропорциональная угловой скорости. Не вдаваясь в подробности работы, которые вряд ли будут кому-то интересны скажем, что выходным сигналом ДУС является напряжение, пропорциональное угловой скорости. Такие датчики имеют небольшие габариты (около 10x10x2 мм) и могут быть легко интегрированы в печатную плату.

Мировым лидером в производстве таких датчиков является компания Analog Devices , датчик которой изображен на рисунке. Можно с большой долей вероятности утверждать, что именно датчики этой компании установлены в iPhone 4.

Преимущества очевидны. В любой момент времени можно знать положение телефона в пространстве. В играх для управления можно использовать не только поворот устройства, но и скорость поворота, что позволяет организовать более точное и реалистичное управление.

Надеюсь, этот небольшой экскурс в теорию и практику гироскопов вас не утомил, а лишь еще раз подчеркнул, что современный мобильный телефон крайне сложное устройство, в котором применяются технологии ранее доступные только авиационной и космической промышленности. А мы тем временем не брезгуем ими открывать пивные бутылки.

Изображения датчиков взяты с сайта представительства Analog Devices в СНГ и странах Балтии

Outputs of X,Y,Z axis are connected directly to analog inputs of Arduino.

R1 is 2.7k resistor, while R2 is variable 10k resistor. Please adjust R2 to have 3.0 V output at LM317 before connecting ADXL335 board. Connect the board and adjust again, because output voltage will drop since load is connected. If readings of the acceleration are unstable please connect additional capacitor between VSS and COM.

An accelerometer can be used not only measure acceleration, but also orientation. It’s because accelerometer sense the force of gravity which is pointed to the center of earth.

This is how sensing axis are orientated to ADXL335 (images from ADXL335 datasheet)

Readings from X,Y,Z axis reflects an orientation of an accelerometer.

Для реализации задуманного нам понадобятся следующие комплектующие:

Самой необходимой деталью для нашей установки является акселерометр. На просторах сети имеется множество предложения для Arduino в части акселерометров и самый доступный - это трех-осевой акселерометр MMA7260. В продаже он уже поставляется на плате, где уже имеется стабилизатор 3,3в 800мА для питания. На выходах осей X, Y, Z распаяны фильтры в виде RC-цепочек, а также сам он имеет фильтр низких частот (что не очень заметно при первом знакомстве).

В соответствии с документацией на устройство, имеется возможность выбора чувствительности (4 режима), а также включение и выключения режима сна. Настройка чувствительности датчика осуществляется с помощью входов G1 и G2. Максимальный уровень чувствительности 1.5g (00), минимальный 6g (11).

Схема подключения

Подключение акселерометра к плате Arduino осуществляется по схеме, представленной ниже:


Чтобы акселерометр всегда выдавал значения его принудительно необходимо вывести из режима сна, просто подав на выход SL (Sleep) питание. Выбор режима чувствительности осуществляется с помощью перемычек J1 и J2.

Первоначально данную схему можно собрать на макетной плате, после чего можно распаять длинные проводки. Перемычки ставятся на контактную площадку 3х2, где крайние левые два контакта 3.3в, центральные G1 и G2 соответственно, а крайние правые контакты «земля».

В результате после сборки получается следующая конструкция:

Подключение трехстепенной платформы к Arduino осуществляется на три выхода с поддержкой ШИМ, а входы от датчика положения на три аналоговых входа A0...A2. Пример подключения узлов к Arduino:

Программирование

Тестирование и проверка работоспособности акселерометра осуществляется с помощью следующей программы:
  1. int x, y, z;
  2. // Калибровка датчика
  3. int dx=357;
  4. int dy=385;
  5. int dz=178;
  6. void setup()
  7. Serial.begin(9600);
  8. void loop()
  9. // Значения осей с датчика
  10. x = analogRead(A0) - dx;
  11. y = analogRead(A1) - dy;
  12. z = analogRead(A2) - dz;
  13. // Вывод в Serial monitor
  14. Serial.print("X: " );
  15. Serial.print(x);
  16. Serial.print("Y:" );
  17. Serial.print(y);
  18. Serial.print("Z:" );
  19. Serial.println(z);
  20. //Период опроса
  21. delay(100);

Тестирование

Перед началом работы, необходимо произвести калибровку датчика, по следующей методике:
1) Установить в прошивке контроллера значения калибровки равные нулю, записать прошивку в контроллер;
2) Зафиксировать датчик в горизонтальном положении;
3) Снять показания с датчика в течение 3 секунд;
4) Вычислить средние значения в горизонтальном положении и внести поправку в калибровочные значения.

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

До проведения калибровки датчик выдавал следующие значения:

По графику видно, что завяленные фильтры низких частот работают довольно странно, так как шумят оси очень сильно. Для подавления шума необходимо или писать свой фильтр низких частот или нормировать шкалу измерений к меньшей (в случае с сервоприводами шум частично подавляется таким методом).

В результате полученных измерений были получены следующие поправки:

  • dx = 357
  • dy = 385
  • dz = 178
После калибровки показания датчика стали показывать реальную картину:

В таком виде уже можно использовать показания датчика в своих проектах.

Результат

UPD. Программная фильтрация сигнала осуществляется по алгоритму, представленному в википедии :

function lowpass(real x, real dt, real RC)
var real y
var real α:= dt / (RC + dt)
y := x
for i from 1 to n
y[i] := α * x[i] + (1-α) * y
return y

* This source code was highlighted with Source Code Highlighter .


хотя второй вариант мне нравится больше:
for i from 1 to n
y[i] := y + α * (x[i] - y)

* This source code was highlighted with Source Code Highlighter .

Файл, приведенный ниже, будет работать с цифровыми датчиками ускорения MPU6050, которые подключены к плате Arduino через I2C протокол по адресу 0x68. Работоспособность проверена на платах Arduino Uno и Arduino Mega. Данный файл заголовка требует файл Wire.h перед добавлением “gyro_Accel.h”. Кроме того, перед вызовом встроенных функций, надо инициализировать шину I2Cс помощью команды Wire.begin();.

Программа для Arduino с файлом заголовка и примером расположены на Github

Логи версии:

Версия 0.1 beta (Дата:2014-06-22): Сам файл заголовка для калибровки и чтения данных с датчика MPU6050 через i2c протокол и пример использования заголовочного файла для расчета угла.

Версия 0.2 beta (Дата:2014-10-08): Исправлены баги в файле примера. “accel_x_scalled” и “accel_y_scalled” теперь возвращают корректные значения углов.

Глобальные переменные

Данный заголовочный файл включает в себя следующие глобальные переменные:

int accel_x_OC - Содержит измерения положения акселерометра относительно оси x при калибровке

int accel_y_OC - Содержит измерения положения акселерометра относительно оси y при калибровке

int accel_z_OC - Содержит измерения положения акселерометра относительно оси z при калибровке

int gyro_x_OC - Содержит измерения положения гироскопа относительно оси x

int gyro_y_OC - Содержит измерения положения гироскопа относительно оси y

int gyro_z_OC - Содержит измерения положения гироскопа относительно оси z

float temp_scalled - Содержит абсолютное значение температуры в градусах цельсия

float accel_x_scalled - данные оси x акселерометра минус данные калибровки

float accel_y_scalled - данные оси y акселерометра минус данные калибровки

float accel_z_scalled - данные оси z акселерометра минус данные калибровки

float gyro_x_scalled - данные гироскопа относительно оси x минус данные калибровки

float gyro_y_scalled - данные гироскопа относительно оси y минус данные калибровки

float gyro_z_scalled - данные гироскопа относительно оси z минус данные калибровки

Функции в программе Arduino для работы с mpu6050

MPU6050_ReadData()

Эта функция считывает данные с акселлерометра, гироскопа и датчика температуры. После считывания данных, значения переменных (temp_scalled, accel_x_scalled, accel_y_scalled, accel_z_scalled, gyro_x_scalled, gyro_y_scalled and gyro_z_scalled) обновляются.

MPU6050_ResetWake()

Эта функция сбрасывает настройки чипа на значения по-умолчанию. Рекомендуется использовать сброс настроек перед настройкой чипа на выполнения определенной задачи.

MPU6050_SetDLPF(int BW)

Эта функция настраивает встроенный фильтр низких частот. Переменная int BW должна содержать значения (0-6). Пропускная способность фильтра будет изменяться в соответствии с представленной ниже таблицей.

Если int BW не в диапазоне 0-6, фильтр низких частот отключается, что соответствует установке – бесконечность.

MPU6050_SetGains(int gyro,int accel)

Эта функция используется для установки максимального значения шкалы измерений

int gyro Макс. знач.[угол/с] int accel Макс. знач. [м/с 2 ]
0 250 0 2g
1 500 1 4g
2 1000 2 8g
3 2000 3 16g

MPU6050_ReadData()

Эта функция использует масштабные коэффициенты для расчета результата. Если не используются значения (0-3), MPU6050_ReadData() отобразит необработанные значения с датчика с погрешностью калибровки. Для получения обработанных значений, установите переменные для калибровки (accel_x_OC, accel_y_OC, accel_z_OC, gyro_x_OC, gyro_y_OC and gyro_z_OC) в нуль.

MPU6050_OffsetCal()

Эта функция позволяет откалибровать акселерометр и гироскоп. Рассчитанные значения записываются в переменные accel_x_OC, accel_y_OC, accel_z_OC, gyro_x_OC, gyro_y_OC и gyro_z_OC для дальнейшей коррекции. Для проведения калибровки необходимо расположить оси x и y axes платы MPU6050 в горизонтальной плоскости, а ось z – перпендикулярно к основанию. Даже незначительные перемещения платы во время калибровки понижают точность расчета базовой точки. Ось z калибруется относительно силя земного притяжения - 9.81 м/с 2 (1g), что учтено в коде.

Калибровка mpu6050

Калибровка гироскопа и акселерометра – это очень важный шаг. Приведенные значения для гироскопа имеют вид: “gyro_x_scalled = ”, так как для получения угла поворота относительно оси по данным угловой скорости, необходимо провести интегрирование. Если “gyro_x_scalled” содержит ошибку или неверно выбрана база, эта ошибка также интегрируется и превращается в значительную погрешность в результате. Так что в идеале измерения должны показывать нуль, если гироскоп не движется вокруг каких-либо осей координат. На практике добиться идеала практически невозможно, так что наша задача – минимизировать эту ошибку. Кроме того, для компенсации «дрифта», можно использовать акселерометр для расчета угла наклона, сравнения полученных данных с результатами гироскопа и последующей компенсацией данной погрешности. Расчет угла будет рассмотрен в этой статье отдельно ниже.

Скетч Arduino для калибровки платы акселерометра/гироскопа MPU6050:

Результат работы скетча для калибровки в серийном мониторе

Расчет угла с помощью гироскопа mpu6050

Данные с гироскопа имеют вид:

В дальнейшем в статье мы будем рассматривать все на примере оси x. Для расчета угла необходимо проинтегрировать переменную “gyro_x_scalled”

является количеством итераций

Так же стоит отметить, что на каждом временном промежутке цикла значение “gyro_x_scalled” остается одинаковым. Существует насколько подходов и методов интегрирования для компенсации и этой погрешности, но мы их детально не будем рассматривать.

Для реализации дискретного интегрирования, будем использовать метод Эйлера как один из самых популярных алгоритмов. Математически интегрирование методом Эйлера можно записать следующим образом:

Мы предполагаем, что начальные углы относительно осей x, y, z после калибровки равны 0, 0 и 90 градусов соответственно, так что для итерации n=0:

Значение T (время каждой итерации) и динамика самого гироскопа (как быстро и насколько нелинейно изменяются углы), значительным образом влияет на точность расчетов. Чем медленнее изменяются углы и чем меньше промежуток между итерациями, тем более точным будет результат. В этом смысле жаль, что платы Arduino достаточно медленные, кристаллы у них работают с частотой 16 МГц и снятие измерений каждые 10-20 мс становится достаточно затруднительным (учитывая тот факт, что процессор занят не только расчетом угла, но и другими параллельными задачами). Мы можем использовать T в виде переменной или константы, я, лично, предпочитаю использовать константу для каждого цикла. В проекте динамические факторы не учитывались, просто использовалась частота итераций с разрывом в 20 мс (0.02 с).

Погрешность гироскопа – «дрифт» (drift)

Из-зза неидеальной калибровки гироскопа, “gyro_x_scalled” никогда не равна нулю и со временем “angle_x_gyro” изменяет свои значения. Для решения данной проблемы, проводится расчет угла с помощью акселерометра и полученные значения сравнывиются с углом гироскопа. Так как модуль MPU6050 располагается горизонтально, ускорение по оси z равно 1g (то есть, 9.81) как это показано на рисунке. Мы можем использовать этот вектор ускорения и его проекцию на ось y для расчета угла между осями x и y.

Угол, который рассчитывается с помощью акселерометра, рассчитывается по зависимости:

Основными проблемами при определении угла наклона с помощью акселерометра являются: сильная зашумленность сигнала и очень сильная чувствительность к вибрациям, без которых ни один механизм не работает. Более того, еслипри перемещении MPU6050 вдоль одной из осей координат, полученные значения будут мешать расчету угла. Так что для лучшего результата, углы с гироскопа и акселерометра объединяются с помощью фильтра:

Окончательно уравнение для определения угла наклона принимает вид:

На рисунке ниже приведена имплементация полученных зависимостей в оболочке Arduino IDE

Окончательный расчет угла наклона и подбор коэффициентов усиления для фильтра

Результаты снимались для различных параметров коэффициентов усиления фильтра и приведены на рисунках по порядку. Коэффициент усиления 1 означает, что фактически идут измерения только с гироскопа. Можно заметить, что в конце angle_x и angle_y отклоняются от значений, рассчитанных с помощью значений с акселерометра.

В моем случае, для дальнейшего проекта использовался коэффициент усиления 0.95. В зависимости от динамики системы, можно его повышать, но не до 1, так как значения будут сильно отклоняться от истинных.

Позволяет определять ускорение действующее в направлении осей X, Y, Z и применяется для определения ориентации объекта в пространстве: углов крена и тангажа.

Видеообзор

Подключение и настройка

Акселерометр общается с управляющей электроникой по протоколу I²C / TWI . Для подключения используется два 3-проводных шлейфа . При подключении модуля к Arduino удобно использовать . Для получения данных с акселерометра мы написали библиотеку Troyka-IMU . Она скрывает в себе все тонкости протокола, через который передаются данные c акселерометра и предоставляет простые и понятные функции для вывода значений.

Пример работы

В качестве примера выведем в Serial порт направление и величину ускорения свободного падения по осям X, Y, Z.

accelerometer.ino // библиотека для работы I²C #include // библиотека для работы с модулями IMU #include // создаём объект для работы с акселерометром Accelerometer accel; void setup() { // открываем последовательный порт Serial.begin (115200 ) ; // выводим сообщение о начале инициализации Serial.println ("Accelerometer init..." ) ; // инициализация акселерометра accel.begin () ; // устанавливаем чувствительность акселерометра // 2g - по умолчанию, 4g, 8g accel.setRange (RANGE_2G) ; // выводим сообщение об удачной инициализации Serial.println ("Initialization completed" ) ; } void loop() { // вывод направления и величины ускорения в м/с² по оси X Serial.print (accel.readAX () ) ; Serial.print ("\t \t " ) ; // вывод направления и величины ускорения в м/с² по оси Y Serial.print (accel.readAY () ) ; Serial.print ("\t \t " ) ; // вывод направления и величины ускорения в м/с² по оси Z Serial.print (accel.readAZ () ) ; Serial.print ("\t \t " ) ; Serial.println ("" ) ; delay(100 ) ; /* // вывод направления и величины ускорения в м/с² по X, Y и Z float x, y, z; accel.readAXYZ(&x, &y, &z); Serial.print(x); Serial.print("\t\t"); Serial.print(y); Serial.print("\t\t"); Serial.print(z); Serial.println(""); delay(100); */ }

Элементы платы

Акселерометр на LIS331DLH

Акселерометр основан на чипе LIS331DLH и представляет собой миниатюрный датчик ускорения выполненный по технологии MEMS компании STMicroelectronicsd в корпусе LGA 16 (3x3x1 мм). Общение акселерометра с управляющей электроникой осуществляется по протоколу I²C / TWI . Адрес устройства равен 0b0011000.

Инструкция

Акселерометры используют для определения вектора ускорения. Акселерометр ADXL335 имеет три оси, и благодаря этому он может определять вектор ускорения в трёхмерном пространстве. Ввиду того, что сила земного притяжения - это тоже вектор, то акселерометр может определять свою собственную ориентацию в трёхмерном пространстве относительно центра Земли.
На иллюстрации приведены рисунки из паспорта (http://www.analog.com/static/imported-files/data_sheets/ADXL335.pdf) на акселерометр ADXL335. Здесь изображены координатные оси чувствительности акселерометра по отношению к геометрическому размещению корпуса устройства в пространстве, а также таблица значений напряжения с 3-х каналов акселерометра в зависимости от его ориентации в пространстве. Данные в таблице приводятся для находящегося в состоянии покоя датчика.
Рассмотрим подробнее, что же показывает нам акселерометр. Пусть датчик лежит горизонтально, например, на столе. Тогда проекция вектора ускорения будет равна 1g по оси Z, или Zout = 1g. По остальным двум осям будут нули: Xout = 0 и Yout = 0. При повороте датчика "на спину", он будет направлен в противоположную сторону относительно вектора силы тяжести, т.е. Zout = -1g. Аналогично измерения снимаются по всем трём осям. Понятно, что акселерометр может быть расположен как угодно в пространстве, поэтому со всех трёх каналов мы будем снимать отличные от нуля показания.
Если датчик сильно тряхнуть вдоль вертикальной оси Z, то значение Zout будет больше, чем "1g". Максимальное измеряемое ускорение - "3g" по каждой из осей в любом из направлений (т.е. как с "плюсом", так и с "минусом").

Думаю, с принципом работы акселерометра разобрались. Теперь рассмотрим схему подключения.
Чип аналогового акселерометра ADXL335 довольно мелкий и помещён в BGA корпус, и в домашних условиях его сложно смонтировать на плату. Поэтому я буду использовать готовый модуль GY-61 с акселерометром ADXL335. Такие модули в китайских интернет-магазинах стоят практически копейки.
Для питания акселерометра необходимо подать на вывод VCC модуля напряжение +3,3 В. Измерительные каналы датчика подключаются к аналоговым выводам Arduino, например, "A0", "A1" и "A2". Это вся схема:)

Загрузим вот такой скетч в память Arduino. Будем считывать с аналоговых входов показания по трём каналам, преобразовывать их в напряжение и выводить их в последовательный порт.
Arduino имеет 10-разрядный АЦП, а максимальное допустимое напряжение на выводе - 5 вольт. Измеренные напряжения кодируются битами, которые могут принимать только 2 значения - 0 или 1. Это значит, что весь диапазон измерений будет поделён на (1+1) в 10-ой степени, т.е. на 1024 равных отрезка.
Для того чтобы перевести снимаемые показания в вольты, нужно каждое измеренное на аналоговом входе значение поделить на 1024 (отрезка), а затем умножить на 5 (вольт).
Посмотрим, что же реально приходит с акселерометра на примере оси Z (последний столбец). Когда датчик расположен горизонтально и смотрит вверх, приходят числа (2,03 +/-0,01). Значит, это должно соответствовать ускорению "+1g" по оси Z и углу 0 градусов. Перевернём датчик. Приходят числа (1,69 +/-0,01), что должно соответствовать "-1g" и углу 180 градусов.

Снимем значения с акселерометра при углах 90 и 270 градусов и занесём в таблицу. Таблица показывает углы поворота акселерометра (столбец "A") и соответствующие им значения Zout в вольтах (столбец "B").
Для наглядности приведён график напряжений на выходе Zout в зависимости от угла поворота. Голубое поле - это область значений в спокойном состоянии (при ускорении 1g). Розовое поле на графике - это запас для того чтобы мы могли измерять ускорение до +3g и до -3g.
При повороте 90 градусов на ось Z приходится нулевое ускорение. Т.е. значение 1,67 вольт - это условный ноль Zo для оси Z. Тогда найти ускорение можно так:
g = Zout - Zo / sensitivity_z, здесь Zout - измеренное значение в милливольтах, Zo - значение при нулевом ускорении в милливольтах, sensitivity_z - чувствительность датчика по оси Z. Чувствительность приведена в паспорте и равна в среднем 300 мВ/g, но вообще лучше провести калибровку акселерометра и вычислить значение чувствительности конкретно для вашего датчика по формуле:
sensitivity_z = * 1000. В данном случае чувствительность акселерометра по оси Z = (2,03 - 1,68)*1000 = 350 мВ. Аналогично чувствительность нужно будет посчитать для осей X и Y.
В столбце "С" таблицы приводится ускорение, вычисленное для пяти углов при чувствительности, равной 350. Как видно, они практически совпадают с теми, которые показаны на рисунке 1.

Вспомнив базовый курс геометрии, получим формулу для вычисления углов поворота акселерометра:
angle_X = arctg[ sqrt(Gz^2 + Gy^2) / Gx ].
Значения получаются в радианах. Чтобы перевести их в градусы, поделим на число "Пи" и умножим на 180.
В итоге полный скетч, вычисляющий ускорения и углы поворота акселерометра по всем осям, приведён на иллюстрации. В комментариях даны пояснения к коду программы.
При выводе в порт "Serial.print()" символ "\t" обозначает знак табуляции, чтобы столбцы были ровные и значения располагались друг под другом. "+" означает конкатенацию (соединение) строк. Причём оператор "String()" явно указывает компилятору, что численное значение нужно преобразовать в строку. Оператор "round()" округляет угол с точностью до 1 градуса.

Итак, мы с вами научились снимать и обрабатывать данные с аналогового акселерометра ADXL335 при помощи Arduino. Теперь мы можем использовать акселерометр в своих разработках.