Mpu 6050 подключение к роботу на arduino. Как подключить цифровой акселерометр ADXL345 к Arduino

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

Собственно, о них и хочу упомянуть.

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

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

Гребеночку уже напаял, да

Поэтому я и сделал выбор в пользу аналогового акселерометра ADXL335 ( для любопытных). Ведь с ним можно и память сэкономить, и общаться гораздо проще - достаточно банального analogRead().

Т.е. простейший код для чтения показаний действительно прост, как амеба:

Unsigned int x, y, z; void setup() { pinMode(A0, INPUT); pinMode(A1, INPUT); pinMode(A2, INPUT); Serial.begin(9600); } void loop() { x = analogRead(A0); y = analogRead(A1); z = analogRead(A2); Serial.println("xxxx | yyyy | zzzz"); Serial.print(x, DEC); Serial.print(" | "); Serial.print(y, DEC); Serial.print(" | "); Serial.print(z, DEC); Serial.println(" | "); delay(2000); }

В итоге пришло ровно то, что на картинке, и я с упоением занялся макетированием, причем сначала - на полноразмерной плате Arduino Mega 2560. И сильно удивился, поскольку поведение акселерометра не очень укладывалось в рамки здравого смысла. Ну, по крайней мере, пока здравый смысл не сказал что-то вроде «окей, даташит я не читал, но будем считать, что акселерометр так и работает».

Вот крупнее, если не верите, что это ADXL335

Иными словами, я ожидал увидеть на выходах акселерометра значения в диапазоне от 0В до верхнего предела питания, т.е. 3,3В или 5В, поскольку акселерометр работает с обоими, но об этом позже.

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

Значения при максимуме по оси X:
X 405 Y 331 Z 344

Значения при минимуме по оси X:
X 268 Y 333 Z 344

Значения при максимуме по оси Y:
Y 400 X 338 Z 346

Значения при минимуме по оси Y:
Y 264 X 334 Z 346

Значения при максимуме по оси Z:
Z 410 X 337 Y 329

Значения при минимуме по оси Z:
Z 275 X 335 Y 331

Показания эти идентичны что для 3В, что для 5В.

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

Второе - упомянутый выше дуализм в смысле питания. Суть в том, что ADXL335 рассчитан на диапазон напряжений от 1,8В до 3,6В. А до 5В его дотянули очень простым способом - поставили стабилизатор на 3,3В ко входу питания.

Решение несколько варварское, поскольку вне зависимости от входного напряжения, пусть даже оно и в допустимых для акселерометра 1,8-3,6В, ток пойдет через стабилизатор. А это довольно критично, если задаться целью сделать максимально экономичное устройство.

На этот случай, как выяснилось, есть более подходящие кандидаты. Именно: макетные платки с питанием от 3,3В или же гибридные, где 5В идет через стабилизатор, а 3,3В - напрямую к чипу. В любом случае, при покупке рекомендую внимательно изучать, что собрались приобретать и соотносить увиденное с желаемым.

Что касается меня, то я решил вопрос со стабилизатором просто. Так как питаться планировал от 3В, то и питающую линию подключил сразу после стабилизатора, и на этом закрыл вопрос.

Подключился вот в эту точку

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

Выглядит не очень презентабельно, поскольку корпуса - то, что я никогда делать не умел.

Батарейка - для масштаба

.

Ничего, скоро сошью чехольчик, и будет лучше.

А вот так работает макет, где как раз видно работящий акселерометр:

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

1) Безопасно для макетных плат с напряжением 3.3В и 5В, поскольку вход питания один и защищен стабилизатором;

2) Стабильность показаний;

3) Просто в использовании за счет аналогового интерфейса - analogRead() и никакой черной магии.

Что касается минусов, то они вытекают из плюсов:

1) Чтобы экономить энергию, придется поработать руками - припаяться напрямую к чипу;

2) Существует возможность поймать помехи на аналоговой линии.

Доклад закончил. Скоро буду писать про шорты брюки.

Планирую купить +21 Добавить в избранное Обзор понравился +22 +39

Прежде чем приступить к рассмотрению модуля гироскопа и акселерометра, думаю, будет не лишним коротко разобраться что это такое. Гироскоп представляет собой устройство, реагирующее на изменение углов ориентации контролируемого тела. В классическом представлении это какой-то инерционный предмет, который быстро вращается на подвесах. Как результат вращающийся предмет всегда будет сохранять свое направление, а по положению подвесов можно определить угол отклонения. На самом же деле электронные гироскопы построены по другой схеме и устроены немного сложнее (вращающийся волчок впихнуть в микросхему было бы не просто). Акселерометр - это устройство, которое измеряет проекцию кажущегося ускорения, то есть разницы между истинным ускорением объекта и гравитационным ускорением. На простом примере такая система представляет собой некоторую массу, закрепленную на подвесе, обладающим упругостью (пружина для хорошего примера). Так вот если такую систему повернуть под каким-то углом, или бросить, или предать линейное ускорение, то упругий подвес отреагирует на движение под действием массы и отклонится и вот по этому отклонению определяется ускорение. Таким образом, гироскоп реагирует на изменение в пространстве независимо от направление движения, с помощью акселерометра же может измерять линейные ускорения предмета, а так же и искусственно рассчитываемое расположение предмета в пространстве. Каждое устройство имеет свои достоинства и недостатки.

Микросхема MPU6050 содержит на борту как акселерометр, так и гироскоп, а помимо этого еще и температурный сенсор. MPU6050 является главным элементом модуля GY-531. Помимо этой микросхемы на плате модуля расположена необходимая обвязка MPU6050, в том числе подтягивающие резисторы интерфейса I 2 C, а также стабилизатор напряжения на 3,3 вольта с малым падением напряжения (при питании уже в 3,3 вольта на выходе стабилизатора будет 3 ровно вольта) с фильтрующими конденсаторами. Ну и бонусом на плате распаян SMD светодиод с ограничивающим резистором как индикатор питающего напряжения. Размер платы модуля GY-521 10 х 20 мм.

Схема модуля представлена ниже (номиналы могут немного отличаться в разных версиях модуля):

Характеристики MPU6050 :

  • напряжения питания 2,375 - 3,46 вольт
  • потребляемый ток до 4 мА
  • интерфейс передачи данных - I2C
  • максимальная скорость I2C - 400 кГц
  • вход для других датчиков I2C
  • внутренний генератор на 8 МГц (вне модуля возможность подключить внешний кварцевый резонатор на 32,768 кГц или 19,2 МГц)

Нужно отметить возможность MPU6050 работать в мастер режиме I2C для AUX выводов, к которым можно подключить еще один внешний датчик (например магнитометр). Честно говоря, я не понимаю для чего это вообще нужно, если проще подключать дополнительные датчики к общей шине I2C микроконтроллера.

Функции MPU6050 :

  • трех осевой MEMS гироскоп с 16 битным АЦП
  • трех осевой MEMS акселерометр с 16 битным АЦП
  • Digital Motion Processor (DMP)
  • slave I 2 C для подключения к микроконтроллеру
  • master I 2 C для подключения к микросхеме дополнительного датчика
  • регистры данных датчиков
  • прерывания
  • температурный сенсор
  • самопроверка гироскопа и акселерометра
  • регистр идентификации устройства

Внешний вид модуля GY-521:

В комплекте идут штыревые соединения угловые и прямые. Припаян был прямой штыревой разъем.

Данные измерений датчиков можно считывать как из регистров хранения, так и пользоваться функциями FIFO. Имеется отдельный регистр под названием Who am I, значение, записанное в этом регистре постоянно и его можно только считать, можно использовать как идентификатор устройства, значение в регистре 104 или 0х68. Отдельным выводом является выход прерываний, который настраивается регистрами настройки под определенные события.

Датчики гироскопа и акселерометра изготовлены как MEMS (микроэлектромеханическая система) - внешнее воздействие на датчик сначала изменяет состояние механической части, затем изменение состояния механической части приводит к изменению сигнала электрической части. Одним словом в одном корпусе собрана не только электроника, но и механика. В микросхеме MPU6050 содержится сразу два MEMS датчика, производитель утверждает, что их взаимное воздействие друг на друга сведено к минимуму. Ну что же, совсем не плохо за цену готового модуля порядка 2 уе. Между прочим эти модули можно приобрести на торговых площадках aliexpress или ebay.

Разберемся как можно использовать датчики акселерометра и гироскопа. Температурный датчик трогать даже не будем - данные о температуре прочитали, перевели в человеческие значения и наслаждаемся. Гироскоп выдает значения мгновенной угловой скорости с разрешением, заданным в настройках, например 2000 градусов в секунду. Если прошить микроконтроллер и смотреть на получаемые данные, то увидим только нули. Если начать крутить датчик, то получим мгновенные значения угловой скорости. Заметьте, что скорость мы получаем в градусах в секунду, а это значит, что линейные скорости не влияют на эти показания - показания будут изменяться только при повороте датчика в пространстве. Далее с помощью этих данных можно получить ориентацию объекта в пространстве. Для этого нужно получить мгновенное значение угловой скорости и умножить его на промежуток времени между опросами датчика гироскопа. Пример разрешение 2000 градусов в секунду, промежуток между опросами датчика 0,1 секунда, значение мгновенной скорости 300, значит 300*0,1=30 - за это время ось гироскопа была повернута на 30 градусов. Далее каждое полученное значение нужно сложить с предыдущим. Если ось двигалась в одном направлении - значение 30 градусов, если в другом, то -30, таким образом, при возвращении датчика в исходное положение всегда (в идеале) будет 0, при отклонении от исходного положения, при выполнении вышеописанных действий, получим угол отклонения. Обрабатывая углы трех осей гироскопа можно получить ориентацию объекта в пространстве.

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

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

Данные от акселерометра получаем всегда достаточно точные, то есть нуль всегда остается нулем ни при каких воздействиях (имеется ввиду не зависит ни от времени, ни от характера воздействия), однако недостаток кроется в том, что данные идут шумом в некотором диапазоне данных, то есть до десятых долей градуса точно измерять угол не получится. Зато исходя из экспериментальных данных, точность до целых значений градуса держится вполне стабильно. Не забываем про влияние линейных ускорений.

Если датчик приобрели, можно переходить к рассмотрению внутренностей модуля, а именно главного элемента - микросхемы MPU6050. Информация хранится в регистрах микросхемы, которых более 100 (!). И вот тут то и кроется огромный подводный камень. производитель не утрудился расписать в документации всю информацию, а привел лишь информацию о самом необходимом. На самом деле не известно даже сколько же всего там регистров, доступных для чтения или записи или того и другого. Также информации на некоторые регистры попросту нет, кроме его названия. Ну что же, придется экспериментально определять влияния значений, записанных в некоторые регистры.

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

Интерфейс I 2 C работает по стандартной схеме. Адрес микросхемы может быть двух значений (без бита чтения / записи) в зависимости от состояния вывода AD0 - b1101000, если AD0 соединен с землей и b1101001, если AD0 соединен с источником питания. Соответственно плюс бит чтения или записи.

Микросхема содержит Digital Motion Processor (DMP), он необходим для того, чтобы обрабатывать данные, получаемые из датчиков гироскопа и акселерометра. Все это делается для того, чтобы повысить точность получаемых данных, так как при обработке данных на микроконтроллере точность может пострадать из-за снижения скорости их обработки. Как правило, алгоритмы обработки движения должны работать с достаточно высокой частотой, обычно 200 Гц, как утверждает документация.

Что касается регистров, то их достаточно большое количество, необходимая информация находится в карте регистров на MPU6050, документ прилагается к статье. Помимо этого прилагается исходник с настройками этих регистров.

Для демонстрации работы модуля была собрана схема:

Здесь использован микроконтроллер Atmega8, данные выводятся на ЖК дисплей 2004А (4 строки по 20 символов). На экран выводится следующая информация, полученная и преобразованная от микросхемы MPU6050 модуля: 1. значения по трем осям акселерометра, 2. значения по трем осям гироскопа, 3. температура, 4. углы отклонения по данным акселерометра (рассчитаны ресурсами микроконтроллера), 5. поворот по оси Z по данным гироскопа (также путем подсчета микроконтроллером). В первом и втором пункте данные имеют мгновенный характер - то есть именно то, что считывается из регистров хранения, это значит, что для гироскопа это скорость, в состоянии покоя все значения будут равны нулю.

Помимо этого, имеется 6 светодиодов, которые загораются в зависимости от положения датчика по оси Y акселерометра.

Модуль датчиков содержит уже стабилизатор на 3,3 вольта, поэтому его можно подключать как к 5 вольта, так и к 3,3 вольтам. Микроконтроллер запитывается от напряжения 3,3 вольта, чтобы не делать согласование уровней I 2 C.

Собранное устройство на макетной плате:

Для программирования микроконтроллера конфигурация фьюз битов (Atmega8):

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

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

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

Список радиоэлементов

Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
MOD1 Модуль GU-521 1 На базе MPU6050 В блокнот
IC1 МК AVR 8-бит

ATmega8

1 В блокнот
VR1 Линейный регулятор

L7805AB

1 В блокнот
VR2 Линейный регулятор

AMS1117-3.3

1 В блокнот
HG1 LCD-дисплей 2004A 1 В блокнот
C1 470 мкФ 1 В блокнот
C2, C3, C5 Конденсатор 100 нФ 3 В блокнот
C4 Электролитический конденсатор 220 мкФ 1 В блокнот
C6 Электролитический конденсатор 10 мкФ 1

Файл, приведенный ниже, будет работать с цифровыми датчиками ускорения 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, так как значения будут сильно отклоняться от истинных.

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

В этом посте я постараюсь рассказать, как можно используя 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 является константой при умножении на которую значение угла в радианах преобразуется в более привычные нам углы в градусах. Вот кажется и все, остается только глянуть видео демонстрацию того, что у нас получилось.

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

Акселерометр - это устройство, которое позволяет измерить динамическое и статическое ускорение по трём осям 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 const byte READ = 0x80; // бит маркер чтения const byte MB = 0x40; // бит MB (многобайтовая передача) const int CS = 10; // пин выбора ведомого void setup() { Serial.begin(115200); SPI.begin(); SPI.setClockDivider(SPI_CLOCK_DIV32); // делитель частоты 500 кГц SPI.setDataMode(SPI_MODE3); // задаём 3-ий режим SPI byte id; readRegister(0x00, 1, id); // читаем регистр DEVID Serial.print("ID = "); Serial.println(id, HEX); writeRegister(0x2D, 0x08); // переводим ADXL345 в режим измерения } void loop() { byte buff; readRegister(0x32, 6, buff); // читаем значения по осям X, Y, Z int x = ((int)buff } // записывает значение в регистр void writeRegister(byte reg, byte value) { digitalWrite(CS, LOW); SPI.transfer(reg); SPI.transfer(value); digitalWrite(CS, HIGH); } // читает из регистра заданное число байтов void readRegister(byte reg, int bytesToRead, byte *outBuff) { digitalWrite(CS, LOW); reg = reg | READ; // покажем акселерометру, что хотим из него читать if (bytesToRead > 1) { reg = reg | MB; // и читать хотим много байтов } SPI.transfer(reg); // записываем адрес регистра, с которого начинаем чтение for (int i=0; i}

Вот так выглядит временная диаграмма работы этого скетча:


Ясно, почему первый байт передачи от 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 const int adxl345 = 0x53; // I2C адрес ADXL345 void setup() { Serial.begin(9600); Wire.begin(); // запишем адрес регистра DEVID Wire.beginTransmission(adxl345); Wire.write(byte(0x00)); Wire.endTransmission(); // прочитаем регистр DEVID: Wire.requestFrom(adxl345, 1); while (Wire.available()) { byte c = Wire.read(); Serial.print("ID = "); Serial.println(c, HEX); } // переведём акселерометр в режим измерений Wire.beginTransmission(adxl345); Wire.write(byte(0x2D)); Wire.write(byte(0x08)); Wire.endTransmission(); } void loop() { // запишем адрес начала данных по осям X, Y и Z: Wire.beginTransmission(adxl345); Wire.write(byte(0x32)); Wire.endTransmission(); // прочитаем 6 байтов значений XYZ: int i = 0; byte xyz; Wire.requestFrom(adxl345, 6); while (Wire.available()) { byte c = Wire.read(); xyz[i] = c; i++; } // посчитаем и отобразим значения X, Y, Z: int x = word(xyz, xyz); int y = word(xyz, xyz); int z = word(xyz, xyz); Serial.print(x); Serial.print("\t"); Serial.print(y); Serial.print("\t"); Serial.println(z); delay(100); }

Диаграмма чтения регистра 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.
Поделиться: