Пример подключения акселерометр гироскоп модуль для arduino. Arduino и MPU6050 для определения угла наклона. Расчет угла с помощью гироскопа mpu6050
Достаточно давно я ничего не писал в свой блог. Вместо бессмысленных оправданий - лучше сразу перейти к делу.
В этом посте я постараюсь рассказать, как можно используя arduino и датчик ADXL335 получить угол относительного отклонения. Начиная писать этот пост, я думал, что он будет самым простым - здесь не понадобится никакой обвязки в схеме. Но, так как пришлось вспомнить немного физики, немного геометрии и вообще пошевелить мозгами - пост набрал вес и теперь по сложности сопоставим со всеми остальными, разве что здесь будет побольше теории.
ADXL335
Итак, давайте для начала немного разберемся в том, что же такое ADXL335 . ADXL335 - это аналоговый трехосный акселерометр.Акселерометр - приспособление для измерения ускорения. Один важный момент, до которого я сам не сразу дошел, важно понимать, что ускорение может быть двух видов: динамическое и статическое.
С динамическим ускорением все должно быть более-менее понятно. Лично я, пока не начал разбираться с акселерометром, только про о нем и знал. Толкнули датчик, предали ему ускорение, он должен это зафиксировать.
Статическое ускорение - это воздействие силы тяжести на наш датчик. Дело в том, что к датчику даже в состоянии полного покоя приложено статическое ускорение равное g .
UPD: Совершенно правильно было замечено в комментариях. Термины динамическое и статическое ускорение перекачевали из англоязычной системы образования и спецификиций на приборы (static & dynamic acceleration). На самом деле, более правильно говорить о силах приложеных к датчику. Я не стал переписывать поста, так как возможно на данном этапе будет проще проводить аналогию со спецификацией, однако следует учитывать, что это лишь подущения и "вольности" перевода.Напомню, из школьного курса физики: g - это ускорение свободного падения. И это именно то самое g которое не раз упоминается в спецификации нашего устройства.
Так вот, датчик ADXL335 умеет улавливать оба этих ускорения. Самое интересно для меня, как оказалось, именно возможность измерения статического ускорения, так как зная проекции его вектора можно спокойно вычислить угол на который отклонился наш датчик относительно некого нулевого значения.
Лучше всего воздействие статического ускорения на показания датчика показаны в спецификации самого датчика.
Фактически в этой картинке кроется основная тайна этого статического ускорения. Я, однако, игнорировал её очень долго. На ней показано, какими показаниями буду, если датчик ворочать разными способами. Главное - не обращайте особенного внимания на надпись TOP
, она несколько вводит в заблуждение. Относительное положение датчика необходимо отслеживать по маленькому беленькому кружечку-метке.
Итак, для начала разберемся с правой частью картинки, на которой меняет свое значение Zout. Согласно этой картинке, если мы положим наш датчик контактами вниз, то значение по оси Z будет равно единице (точнее одному g). Как я уже говорил - это значение ни что иное как проекция вектора статического ускорения на ось нашего датчика. Так как в данном случае вектор совпадает с осью Z, а ускорения свободного падения равняется g
, мы и имеет значение Zout = 1g.
Если же мы перевернем датчик контактами вверх, то значение Zout изменится на противоположное.
Стоит отметить, что все остальные ускорения равны нулю, связано это с уже упомянутым совпадением вектора статического ускорения с осью Z, а так же состоянием покоя всего датчика.
Аналогично можно разобрать все остальные пары. Единственное отличие - то, что датчик будет находится на ребре или боку.
Важно так же понимать, что длинна этого вектора в состоянии покоя датчика всегда будет равняться единице. Вектор далеко не всегда будет совпадать с какой-либо из осей - скорее такой вариант, это исключение из правил. Чаще всего этот вектор будет размазан по всем трем осям одновременно. Все же в трехмерном пространстве живем.
ВАЖНО!
В отличии от всех рассмотренных ранее, наш сегодняшний испытуемый питается от 3-х вольт. Максимально допустимым значение напряжения для ADXL335
является 3,6 вольта, так что для опыта я буду использовать выход +3,3В
на самой плате Arduino, и т.к. это несколько выше рекомендуемого напряжения, нам придется немного подкорректировать некоторые параметры в расчетах.
ADXL335 - трехосный датчик. Фактически, это три разных акселерометра в одном корпусе, каждый из которых отвечает за свою собственную ось X, Y либо же Z.
Помимо обозначенного статического ускорения наш датчик запросто будет справляться и с обычными ускорениями, до 3g . Это можно использовать например, чтобы определить находится ли вся конструкция в движении, и даже в каком направлении она двигается. Например, ускорение в -3g по оси Z скорее всего скажет нам, что мы только что неплохо шандарахнулись об пол, упав откуда-то. Можно измерять ускорение при начале движения и тем самым делать его более плавным, без резких рывков.
Достаточно часто в интернете в ходе поисков нарывался на обсуждения акселерометра в качестве датчика для системы автопилота. Это не лучшая идея, скажу вам сразу. Во всяком случае, могу совершенно точно сказать, используя только акселерометр в качестве датчика сделать это не получится. Дело в том, что угол наклона в пространстве мы можем нормально вычислить только в состоянии покоя всего устройства. А какой может быть покой у авиамодели? Однако, акселерометр будет отличным дополнением к электрокомпасу в купе с гироскопом.
Подключаемся
Описание теоретических основ - это безусловно здорово, но пора бы и к практике перейти. Я использовал датчик ADXL335 уже купленный с необходимой обвязкой, так как запаять его в ручную слишком сложно, да и нет такой необходимости, если честно. Сегодняшняя схема, до неприличия простая.Единственное, на что стоит обратить внимание еще раз - мы используем для питания выход 3v3
Arduino, а не 5v
, как это было во всех остальных случаях. Выходы X
, Y
, Z
нашего датчика соединены с входами ANALOG IN 0
, 1
, 2
нашей Arduino.
В этом проекте я использовал Arduino UNO, так как все остальные были заняты.
Вот так все выглядит у меня.
Пишем код
Перейдем собственно к самой интересной части, в которой мы объединим всю теорию и железо описанное выше в одно целое. Помимо этого, это будет первый пост, где мы напишем целых две прошивки. Первая из них призвана помочь нам в калибровке сенсора. Как показала практика - это будет не лишним. Итак, приступим:
#define ADC_ref 5.0 unsigned int value_x; unsigned int value_y; unsigned int value_z; void setup() { Serial.begin(9600 ); } void loop() { value_x = analogRead(0 ); value_y = analogRead(1 ); value_z = analogRead(2 ); Serial.print(value_x/analog_resolution*ADC_ref, 5 ); Serial.print(" " ); Serial.print(value_y/analog_resolution*ADC_ref, 5 ); Serial.print(" " ); Serial.println(value_z/analog_resolution*ADC_ref, 5 ); delay(500 ); }
Итак, давайте разберемся, что тут происходит.
#define ADC_ref 5.0
#define analog_resolution 1024.0
Обозначаем пару констант. ADC_ref
- это максимальное значение в вольтах которое может снять аналоговый вход. analog_resolution
- это разрешающая способность нашего аналогового входа. На arduino она равна 2 10 или 1024.
После объявления констант идет пара переменных в которых мы будем хранить показания снятые с нашего датчика и инициализация серийного порта, чтобы можно было получить какие-то данные на компьютере.
В функции loop мы в начале получаем данные с трех наших аналоговых пинов, к которым и подключен наш датчик, а после этого пересчитываем полученное число в вольты и выводим их на серийный порт.
Зальем эту прошивку в нашу Arduino UNO, откроем серийный монитор (ctrl+shift+m) и соберем кое какие данные. Нам понадобятся данные шести положений, как на уже упомянутом рисунке.
В фотографиях это выглядит как-то так.
На самом деле я делал замеры на более ровной поверхности, но мне не хватило рук заснять это. Так что не думайте, что я сделал все так криво. Просто мне хотелось показать вам идею как это делать. На включенном серийном мониторе можно видеть что-то вроде.
Где первый столбец - показания по оси X, второй - Y, третий - Z. Полученные в результате замеров данные я свел в одну таблицу:
zero | avr. deviation |
|||
1.60156 | 1.92871 | 1.26465 | 0.33203 |
|
1.60156 | 1.92871 | 1.26465 | 0.33203 |
|
1.64551 | 1.94336 | 1.31836 | 0.3125 |
В столбец zero занесены значения out от нуля, во-втором столбце с заголовком g занесены значения, когда ускорение свободного падения (оно же статическое) - совпадает с вектором, так же как и в столбце -g .
Напишем вторую прошивку, которая будет собираться показания с нашего датчика и преобразовывать их в проекции ускорений по трем осям, а потом еще и высчитаем относительный угол отклонения нашего датчика в пространстве, используя данные о статическом ускорении, о котором я долго распинался в начале поста. Большое спасибо за основу для кода посту на http://www.electronicsblog.net .
#define ADC_ref 5.0 #define analog_resolution 1024.0 #define zero_x 1.60156 #define zero_y 1.60156 #define zero_z 1.64551 #define sensitivity_x 0.33 #define sensitivity_y 0.33 #define sensitivity_z 0.31 unsigned int value_x; unsigned int value_y; unsigned int value_z; float xv; float yv; float zv; float angle_x; float angle_y; float angle_z; void setup() { Serial.begin(9600 ); } void loop() { value_x = analogRead(0 ); value_y = analogRead(1 ); value_z = analogRead(2 ); xv = (value_x/analog_resolution*ADC_ref-zero_x)/sensitivity_x; yv = (value_y/analog_resolution*ADC_ref-zero_y)/sensitivity_y; zv = (value_z/analog_resolution*ADC_ref-zero_z)/sensitivity_z; Serial.print("x = " ); Serial.print(xv); Serial.print("g y = " ); Serial.print(yv); Serial.print("g z = " ); Serial.print(zv); Serial.println("g" ); angle_x =atan2(-yv,-zv)*RAD_TO_DEG; angle_y =atan2(-xv,-zv)*RAD_TO_DEG; angle_z =atan2(-yv,-xv)*RAD_TO_DEG; Serial.print(angle_x); Serial.print(" deg " ); Serial.print(angle_y); Serial.print(" deg " ); Serial.print(angle_z); Serial.println(" deg" ); delay(1500 ); } Рассмотрим основные части записанного кода. Части которые отвечают за вывод на серийный порт я буду буду пропускать, так как это мы делали уже множество раз.
#define zero_x 1.60156
#define zero_y 1.60156
#define zero_z 1.64551
Значение нуля из нашей таблицы для каждой оси. Получены нами в результате проделанной ранее калибровки. Вообще, в идеале, при питании микросхемы ADXL335 ровными 3в, это значение должно быть равно 1.5, но из-за увеличившегося напряжения питания значение несколько увеличилось. Если не проводить калибровку, то это значение можно принять как напряжение питания деленное на 2. Интересным фактом является то, что значение сильнее увеличилось именно по оси Z. Согласно спецификации - это нормально.
#define sensitivity_x 0.33 #define sensitivity_y 0.33 #define sensitivity_z 0.31 Чувствительность нашего сенсора. Если пренебречь калибровкой, то можно принимать это значение как 1/10 напряжения питания микросхемы ADXL335 . А вот теперь перейдем к магии.
Xv = (value_x/analog_resolution*ADC_ref-zero_x)/sensitivity_x;
yv = (value_y/analog_resolution*ADC_ref-zero_y)/sensitivity_y;
zv = (value_z/analog_resolution*ADC_ref-zero_z)/sensitivity_z;
Такой не слишком хитрой формулой мы вычисляем проекцию вектора ускорения на оси координат, причем единицей деления на осях будет значение ускорения свободного падения g (благодаря делению на значение sensitivity_*
). Теперь зная эти проекции мы можем вычислить угол отклонения датчика в каждой полскости. Для этого возьмем арктангенс от проекции вектора на определенную плоскость.
Angle_x =atan2(-yv,-zv)*RAD_TO_DEG;
angle_y =atan2(-xv,-zv)*RAD_TO_DEG;
angle_z =atan2(-yv,-xv)*RAD_TO_DEG;
Значение RAD_TO_DEG является константой при умножении на которую значение угла в радианах преобразуется в более привычные нам углы в градусах. Вот кажется и все, остается только глянуть видео демонстрацию того, что у нас получилось.
Небольшой шум в конце видео - это кот прыгнул на стол прямо рядом с камерой:)
Напоследок хотелось бы пояснить - формулы используемые в примерах далеки от совершенства в плане производительности. Их все можно очень неплохо оптимизировать, но тогда они потеряли свою наглядность. Основной целью поста было показать возможности и немного разобраться в новом датчике.
ADXL337 и ADXL377 - это компактные, тонкие, маломощные 3-х осевые акселерометры, которые на выходе дают аналоговый сигнал в вольтах.
Основная разница между этими акселерометрами - диапазон измерений. ADXL337 измеряет ускорения в диапазоне ±3 g, а ADXL377 работает в диапазоне ±200 g и может использоваться для измерения более резких изменений движений контролируемого объекта, может использоваться для оценки вибраций.
Эта статья поможет вам разобраться с особенностями использования данных акселерометров. Будут раскрыты вопросы подключения акселерометров к Arduino. В результате вы легко и непринужденно сможете их интегрировать в любой ваш проект.
На фото ниже приведены платы с установленными акселерометрами:
Обзор и технические характеристики акселерометров ADXL337 и ADXL377
Акселерометры ADXL337 и ADXL377 можно (и рекомендуется!) покупать уже интегрированными в отдельный модуль. На модуле предусмотрена необходимая минимальная обвязка и готовые контакты для подключения к Arduino или другому микроконтроллеру.
Как видите, на обоих модулях одинаковое количество контактов. В таблице ниже приведена краткая информация о каждом из контактов на модулях:
Питание акселерометров ADXL337 и ADXL377
Здесь надо быть предельно осторожным. ADXL337 и ADXL377 оба должны запитываться максимальным напряжением 3.6 В! Это напряжение подается к контакту питания и к контакту Self Test. Можно использовать Arduino 5 В или 3.3 В для считывания значений с отдельных осей акселерометра, а запитывать сам датчик ускорения от 3.3 В. Но не забывайте, что значения, которые вы получите с сенсора после аналогово-цифрового преобразования будут разными при 5 В и при 3.3 В! Поэтому надо уточнять диапазоны в зависимости от сигнала преобразования.
Одним из достоинств акселерометров ADXL337 и ADXL377 является то, что они потребляют мало тока для работы. Обычно это около 300 мА.
Необходимые пояснения по использованию ADXL337 и ADXL377
Если вы запитываете ADXL337 или ADXL377 от 3.3 В, значение 1.65 В на контакте оси X будет соответствовать ускорению 0 g. Если на пине X у вас показания напряжения составляют 3.3 В, то на ADXL337 это значит, что сила составляет 3g. В то время как при показаниях 3.3 В на ADXL377 означают, что нагрузка составляет 200g. По сути оба чипа используются и подключаются одинаково, но показания будут разными, так как они зависят от максимально допустимых значений, которые считывает акселерометр.
На модуле ADXL377 предусмотрены 4 отверстия для крепежа, так как этот датчик ускорения рассчитан на более экстремальные условия нагрузок.
На обоих модулях установлены конденсаторы на 0.01 мкФ возле выходов на оси X, Y, и Z. То есть, максимальные частоты, в пределах которых вы можете оценивать ускорение составляет 500 Гц.
Пример подключения к Arduino
Распайка
Перед тем как устанавливать акселерометр на макетную плату и подключать к Arduino , вам надо припаять контакты. Это могут быть отдельные рельсы или просто провода. Что именно распаивать зависит исключительно от того, где в дальнейшем вы планируете использовать датчик ускорения.
Если вы планируете использовать макетную плату или монтажную плату с расстоянием между контактами 0.1", рекомендуем припаять прямую рельсу контактов с выходом типа папа. Если в планах у вас подключать акселерометр сразу к контроллеру, без макеток и монтажных плат, используйте провода.
Подключаем акселерометр к Arduino
В этом примере мы рассмотрим как можно использовать контроллер Arduino Uno для сбора и обработки данных с модуля акселерометра ADXL337 или ADXL377. Так как выходной сигнал с датчика аналоговый, нам надо подключить три контакта с отдельных осей координат к контактам "Analog In" на Arduino. На рисунке ниже показана схема подключения модуля ADXL337. ADXL377 подключается так же.
Запитать акселерометр можно с помощью контакт 3.3 В и GND на Arduino. Контакты осей X, Y, и Z с датчика подключаются к ADC пинам (A0, A1, и A2 в рассматриваемом примере). Пин self test (ST) можно не подключать, а можно подключить к земле (GND). Если вы хотите использовать ST контакт для двойной проверки функционирования сенсора, подключите его к 3.3 В. Для дополнительной информации по этому поводу можете ознакомится с даташитами сенсоров: ADXL377 и ADXL377 .
Программа для Arduino
После того как вы подключили акселерометр к Arduino, можно перейти к программированию. Полный скетч вы можете скачать с Github. Ссылка для ADXL337 и для ADXL377 . Единственное отличие в этих скетчах - значение переменной scale.
Первые две строки кода в скетче служат для настройки параметров под ваш модуль датчика ускорения:
int scale = 3; boolean micro_is_5V = true;
Значение переменной scale устанавливается равным максимальному значению измеряемой силы g. Для ADXL337 это значение устанавливается равным 3, а для модели ADXL377 переменная принимает значение 200, так как сенсоры обеспечивают диапазоны измерений ±3g и ±200g соответственно. Переменной micro_is_5V присваивается значение true, если используется контроллер с 5 В (например, Arduino Uno) и значение false,using если вы используете контроллер на 3.3 В (например, Arduino Pro Mini). Это важный параметр, который напрямую влияет на дальнейшую интерпретацию данных с сенсора.
После этого используем функцию setup() для инициализации серийного соединения. Благодаря этому мы сможем выводить показания в окно серийного монитора Arduino IDE.
void setup() { // устанавливаем связь по серийному протоколу со скоростью передачи данных 115200 baud
Serial.begin(115200); }
В пределах функции loop(), мы собираем данные с датчика, масштабируем их для отображения в единицах измерения силы g и отображаем в окне серийного монитора изначальные и преобразованные данные. Для начала давайте взглянем на то, как считываются данные с датчика ускорения.
void loop() { // получаем данные с акселерометра для каждой отдельной оси координат
int rawX = analogRead(A0);
int rawY = analogRead(A1);
int rawZ = analogRead(A2);
Для того, чтобы получить числовое значение в диапазоне от 0 до 1023, которые соответствуют напряжению на входах Arduino, мы используем аналоговые входы A0, A1, и A2 и несколько считываемых значений. Эти значения напряжений отражают последнее измеренное значение ускорения с сенсора. Например, если ADXL337 показывает 3.3 В на контакте X, это означает, что сейчас ускорение вдоль оси X составляет +3g. Причем зависит это от вашей модели контроллера. Если вы используете микроконтроллер 3.3 В, считываемые аналоговые значения будут возвращать 1023 и храниться в переменной rawX. Если вы используете микроконтроллер 5 В, возвращаемые аналоговые значения будут равны 675. Храниться они будут в той же переменной. Именно поэтому важно корректно настроить переменную micro_is_5V, чтобы мы знали как правильно интерпретировать текущие показания.
Зная напряжение вашей платы Arduino, мы можем масштабировать полученные int значения и получить показания измерений в единицах измерения g. Ниже приведен кусок куда, с помощью которого мы приводим полученные показания к необходимым единицам измерения:
float scaledX, scaledY, scaledZ; // масштабированные значения для каждой оси
if (micro_is_5V) // микроконтроллер работает с 5 В
{ scaledX = mapf(rawX, 0, 675, -scale, scale); // 3.3/5 * 1023 =~ 675
} else // микроконтроллер 3.3 В
{ scaledX = mapf(rawX, 0, 1023, -scale, scale); }
Масштабированные значения хранятся в виде типа данных float. После этого мы проверяем, какой у нас контроллер (3.3 В или 5 В), с помощью булевой переменной micro_is_5V. По результатам проверки мы масштабируем целое значение x - rawX и превращаем его в значение со знаком после запятой, которое соответствует силе g. Переменная для хранения новых значений называется scaledX. То же самое мы делаем для осей Y и Z. Детально рассматривать эти оси мы не будем, так как процесс преобразования совершенно идентичный. Важно запомнить, что на Arduino 5 В мы получаем 675 при напряжении на пине 3.3 В, а Arduino 3.3 В интерпретирует измерения, которые соответствуют 3.3 В в виде значения 1023.
Функция mapf(), которая используется в скетче, работает так же как и стандартная Arduino функция map(). Основная причина, по которой используется именно mapf() - она может работать с десятичными значениями, а стандартная функция - только с целыми типа int.
После преобразования, мы выводим в окно серийного монитора текущие и преобразованные данные. Вероятно, вас будут интересовать только преобразованные, масштабированные значения ускорения, но потоковые данные оставлены специально, чтобы вы могли их сравнить с результатом и лучше понять принцип работы акселерометра. Часть кода, которая отвечает за вывод данных по каждой из осей чувствительности акселерометра ADXL337 или ADXL377 приведена ниже:
// выводим в окно серийного монитора текущие показания акселерометра по осям чувствительности X,Y,Z
Serial.print("X: ");
Serial.println(rawX); // выводим преобразованные показания с акселерометра по осям X,Y,Z
Serial.print("X: ");
Serial.print(scaledX);
Serial.println(" g");
Это позволяет нам увидеть данные в двух видах.
Перед снятием новых показаний, делаем задержку:
В примере выставлена задержка в 2 секунды (2000 миллисекунды), так как мы просто выводим показания сенсора в окно серийного монитора в целях ознакомления с акселерометром. В реальных проектах вы можете считывать данные с датчика с частотой 500 Гц. То есть, значение задержки можно сократить вплоть до 2 миллисекунд.
После этого возвращаемся в начало нашего цикла loop().
Надеемся, что эта статья поможет вам освоить работу акселерометров в связке с Arduino и вы используете полученные знания в ваших личных проектах. Кстати, с помощью акселерометра вы можете определять не только ускорение, но и перемещения, которое совершает объект. Подробная статья по определению угловых перемещений с помощью акселерометра и гироскопа: Arduino и MPU6050 для определения угла наклона .
Оставляйте Ваши комментарии, вопросы и делитесь личным опытом ниже. В дискуссии часто рождаются новые идеи и проекты!
Акселерометр - это устройство, которое позволяет измерить динамическое и статическое ускорение по трём осям X, Y и Z. Благодаря статическому ускорению можно определить положение в пространстве (акселерометр как датчик поворота), а благодаря динамическому (движение или встряска) - направление ускорения.
Цифровой акселерометр ADXL345 - это 3-осевой акселерометр с высоким разрешением (13 бит) по осям с пределом до ±16g. Модуль обладает пониженным энергопотреблением и малыми размерами. Информационный обмен с модулем осуществляется по последовательным интерфейсам I2C или SPI (3- или 4-проводной).
Существует множество модулей для Arduino с акселерометром ADXL345. Модуль может выглядеть, например, так:
Показанный модуль имеет название GY-291. У модуля имеются следующие выводы:
Вывод модуля | Назначение | Подключать к выводу Arduino | |
---|---|---|---|
SPI | I2C | ||
GND | Земля | GND | GND |
VCC | Питание | +3,3V | +3,3V |
CS | Выбор ведомого интерфейса SPI | 10 | - |
INT1 | Выход прерывания 1 (*) | - | - |
INT2 | Выход прерывания 2 (*) | - | - |
SDO | Данные от ведомого | 12 | - |
SDA | Данные от мастера интерфейса SPI Шина данных интерфейса I2C | 11 | A4 |
SCL | Шина тактирования | 13 | A5 |
(*) Работы с прерываниями ADXL345 касаться в этой статье не будем. Вот есть хорошая , в которой достаточно подробно описан вопрос работы с прерываниями.
В зависимости от выбранного интерфейса - SPI или I2C - подключение модуля будет соответствующим, как показано в таблице. Но в обоих случаях очень простым.
Рассмотрим структуру регистров микросхемы ADXL345:
Кроме того, нас интересует регистр управления питанием, т.к. он отвечает за режим работы устройства:
Как видим, бит D3 (Measure ) переключает акселерометр в режим измерения.
2 по интерфейсу SPI
Акселерометр ADXL345 поддерживает 3- и 4-проводные варианты интерфейса SPI. Мы рассмотрим только 4-проводное подключение. Кроме того, акселерометр работает в режиме 3 интерфейса SPI (помните, мы уже обсуждали: CPOL =1, CPHA =1). Диаграмма, показывающая обмен с акселерометром ADXL345 по 4-проводному интерфейсу SPI:
Здесь бит MB - это признак того, что мы собираемся читать много байтов за раз (если бит установлен в 1). Для тестирования работы с SPI устройствами и быстрого освоения порядка обмена с ними я обычно использую отладочную плату с микросхемой FT2232H. Эта микросхема поддерживает множество режимов, в том числе I2C и SPI. Управление работой микросхемы FT2232H - с помощью программы SPI via FTDI , о которой я уже неоднократно рассказывал.
Подключим акселерометр к отладочной плате и прочитаем регистр DEVID , в котором хранится постоянное значение-идентификатор акселерометра ADXL345. Значение идентификатора должно быть 0xE5.
Не забудем перед чтением записать команду 0x80, которая укажет акселерометру, что мы собираемся читать, начиная с регистра по адресу 0x0 (см. диаграмму выше, рисунок 38 - SPI 4-Wire Read):
Видно, что в регистре содержится число 0xE5, которое и является значением идентификатора акселерометра ADXL345, согласно техническому описанию (datasheet). Вот как это выглядит на временной диаграмме:
Устройство отвечает, всё нормально. Теперь нам нужно перевести акселерометр в режим измерений. Для этого необходимо записать в регистр POWER_CTL (адрес регистра 0x2D) число 0x08 (установить бит Measure в HIGH). После этого можно начинать читать регистры с 0x32 по 0x37, в которых хранятся данные об ускорениях по трём осям. Сделаем это с помощью Arduino. Напишем такой скетч:
Скетч для чтения данных ADXL345 по SPI (разворачивается) #includeВот так выглядит временная диаграмма работы этого скетча:
Ясно, почему первый байт передачи от Arduino при чтении значений ускорений по осям - число 0xF2? Это адрес первого регистра, с которого начинаем чтение (0x32), объединённый по ИЛИ с 0x80 - маркером чтения READ - и с 0x40 - маркером многобайтовой передачи MB : 0x32 OR 0x80 OR 0x40 = 0011_0010 OR 1000_0000 OR 0100_0000 = 1110_1101 = 0xF2
Что означают считанные значения? Этот вопрос рассматривается в последнем разделе статьи. Кроме того, существует ряд библиотек для Arduino, которые упрощают настройку и чтение данных с акселерометра, позволяя не думать о таких низкоуровневых вещах как регистры, биты и байты. Ссылки на библиотеки также приведены в конце статьи.
3 Работа с цифровым акселерометром ADXL345 по интерфейсу I2C
Временная диаграмма информационного обмена с ADXL345 по интерфейсу I2C выглядит так:
Давайте перепишем скетч для Arduino, который будет делать всё то же самое, только с обменом по интерфейсу I2C:
Скетч для чтения данных ADXL345 по I2C (разворачивается) #includeДиаграмма чтения регистра DEVID цифрового акселерометра ADXL345 при обмене по последовательному интерфейсу I2C будет в этом случае такой:
Как видно, ADXL345 возвращает нам ожидаемое значение 0xE5. А вот так будет выглядеть диаграмма чтения регистров, в которых хранятся данные по осям XYZ:
Тут всё ещё проще, чем при работе с интерфейсом SPI.
4 Разбор показаний цифрового акселерометра ADXL345
Посмотрите на фотографию ниже. На плате модуля нарисованы три оси: X, Y и Z. Они показывают направление осей акселерометра. Направления осей обусловлены расположением микросхемы ADXL345 на плате. В данном случае ось X акселерометра направлена горизонтально вправо, ось Z направлена горизонтально на нас, ось Y - вертикально вверх.
А вот что выводит наш скетч в монитор последовательного порта среды Arduino IDE (надо уточнить, что данный вывод наблюдается в режиме покоя - акселерометр неподвижно лежит на столе в положении, как на приведённом фото):
В трёх столбцах представлено значение статического ускорения, измеренное акселерометром по осям X, Y и Z, соответственно. В среднем столбце - показания оси Y - значения больше, чем в двух других. Эти значения даны в условных отсчётах, как они записаны в регистрах акселерометра. Акселерометр ADXL345 имеет несколько диапазонов измерений. Давайте посмотрим на сводную таблицу диапазонов и разрешений датчика акселерометра:
Напомню, что g - это ускорение свободного падения, численно равное примерно 9,81 метр в секунду за секунду (м/с 2).
Диапазон по умолчанию - от −16g до +16g (размах 32g ). Согласно таблице, на этот диапазон ускорений приходится 13 бит точности или 2 13 = 8192 отсчёта. Таким образом, на 1 отсчёт приходится ускорение 32g/8192 = 0,00390625g=0,00390625×9,81 ≈ 0,038 м/с 2 . Имея это в виду, получается, что в данном выводе скетча ускорение составляет:
Что ж, вполне логично. Ось Y направлена вертикально, т.е. вдоль вектора силы земного притяжения, и значение ускорения примерно равно константе g . Ускорения по осям X и Z, которые лежат в одной горизонтальной плоскости, примерно одинаковы и находятся около 0. Из-за кривизны стола, на котором стоит датчик, значения немного отличаются. Если бы я выровнял акселерометр по уровню, то его показания были бы более точные. В идеальном случае по оси Y должно быть ускорение 9,8 м/с 2 , а по осям X и Z - 0.
Кроме того, датчик ADXL345 имеет возможность тонкой настройки и калибровки. В данном примере мы этого не делали, а использовали акселерометр с заводскими настройками, как есть. Отсутствие калибровки также может вносить некоторые искажения в показания датчика. Рекомендую применять специальные библиотеки Arduino, которые упрощают взаимодействие с акселерометром ADXL345, в частности, позволяют проводить тонкую настройку.
Скачать библиотеку ADXL345
В архиве также лежит техническое описание (datasheet) на цифровой акселерометр ADXL345. Установка библиотеки производится путём копирования разархивированной папки с библиотекой в директорию libraries среды Arduino IDE.
- скачать библиотеку Arduino для акселерометра ADXL345 с Depositfiles.
Акселерометры используют для определения вектора ускорения. Акселерометр ADXL335 имеет три оси, и благодаря этому он может определять вектор ускорения в трёхмерном пространстве. Ввиду того, что сила земного притяжения - это тоже вектор, то акселерометр может определять свою собственную ориентацию в трёхмерном пространстве относительно центра Земли.
На иллюстрации приведены рисунки из паспорта на акселерометр ADXL335.
Здесь изображены координатные оси чувствительности акселерометра по отношению к геометрическому размещению корпуса устройства в пространстве, а также таблица значений напряжения с 3-х каналов акселерометра в зависимости от его ориентации в пространстве. Данные в таблице приводятся для находящегося в состоянии покоя датчика.
Рассмотрим подробнее, что же показывает нам акселерометр.
Пусть датчик лежит горизонтально, например, на столе. Тогда проекция вектора ускорения будет равна 1g по оси Z, или Zout = 1g. По остальным двум осям будут нули: Xout = 0 и Yout = 0.
При повороте датчика "на спину", он будет направлен в противоположную сторону относительно вектора силы тяжести, т.е. Zout = -1g.
Аналогично измерения снимаются по всем трём осям.
Понятно, что акселерометр может быть расположен как угодно в пространстве, поэтому со всех трёх каналов мы будем снимать отличные от нуля показания.
Если датчик сильно тряхнуть вдоль вертикальной оси Z, то значение Zout будет больше, чем "1g". Диапазон измерения от 2g до 16g по каждой из осей в любом из направлений (т.е. как с "плюсом", так и с "минусом").
Чип аналогового акселерометра ADXL335 довольно мелкий и помещён в BGA корпус, и в домашних условиях его сложно смонтировать на плату.
Рассмотрим подключение акселерометра ADXL335 на примере готового модуля GY-291.
Принципиальная схема модуля GY-291 на базе микросхемы ADXL345.
Для питания акселерометра необходимо подать на вывод VCC модуля напряжение 3,3 В или 5 В. Измерительные каналы датчика подключаются к аналоговым выводам Arduino.
Библиотеки для подключения 3-осевого акселерометра GY-291 на чипе ADXL345.
Файл, приведенный ниже, будет работать с цифровыми датчиками ускорения 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, так как значения будут сильно отклоняться от истинных.