В этой статье мы узнаем, как измерять угол и отслеживать ориентацию с помощью Arduino и датчика акселерометра ADXL345.
Как работает акселерометр ADXL345
ADXL345 — это 3-осевой акселерометр, который может измерять как статические, так и динамические силы ускорения. Сила земного притяжения является типичным примером статической силы, в то время как динамические силы могут быть вызваны вибрациями, движениями и так далее.
Общеизвестная единица измерения ускорения — метр на секунду в квадрате (м/с2). Однако акселерометры обычно выдают значения в «g» или гравитационное ускорение. Один «g» — это величина ускорения придаваемое телу силой тяжести на поверхности земли и в среднем равна 9,8 м/с2.
Итак, если у нас акселерометр, расположенный горизонтально, с его осью Z, направленной вверх, (противоположной силе тяжести) выходной сигнал по оси Z будет равен 1g. Выходные сигналы по осям X и Y будут равны нулю, потому что гравитационная сила перпендикулярна к этим осям и никак на них не влияет.
Если перевернуть датчик вверх ногами, то выходной сигнал по оси Z будет -1g. Это означает, что выходные данные датчика из-за его ориентации под действием силы тяжести могут варьироваться от -1g до +1g.
Таким образом, на основании этих данных и используя некоторые тригонометрические формулы мы можем вычислить угол, под которым расположен датчик.
Как считать данные акселерометра ADXL345 с помощью Arduino
Хорошо, теперь давайте посмотрим, как мы можем прочитать данные акселерометра ADXL345 с помощью Arduino. Для связи с Arduino датчик ADXL345 использует I2C протокол. Следовательно, нам нужны только два провода для его подключения (линии данных) и два провода для его питания.
Скетч для акселерометра ADXL345
Ниже приведен код для чтения данных акселерометра ADXL345:
#include <Wire.h> // подключение библиотеки Wire int ADXL345 = 0x53; // Адрес I2C датчика ADXL345 float X_out, Y_out, Z_out; // Выходы void setup() { Serial.begin(9600); Wire.begin(); // Инициализация библиотеки Wire // Установите ADXL345 в режим измерения Wire.beginTransmission(ADXL345); // Начать общение с устройством Wire.write(0x2D); // работа с регистром POWER_CTL - 0x2D // Включить измерение Wire.write(8); // (8dec -> 0000 1000 двоичный) Бит D3 High для разрешения измерения Wire.endTransmission(); delay(10); } void loop() { // === Считать данные акселерометра === // Wire.beginTransmission(ADXL345); Wire.write(0x32); // Начать с регистра 0x32 (ACCEL_XOUT_H) Wire.endTransmission(false); Wire.requestFrom(ADXL345, 6, true); // Чтение всех 6 регистров, значение каждой оси сохраняется в 2 регистрах X_out = ( Wire.read()| Wire.read() << 8); // Значение по оси X X_out = X_out/256; //Для диапазона + -2g нам нужно разделить необработанные значения на 256 в соответствии с datasheet Y_out = ( Wire.read()| Wire.read() << 8); // Значение по оси Y Y_out = Y_out/256; Z_out = ( Wire.read()| Wire.read() << 8); // Значение по оси Z Z_out = Z_out/256; Serial.print("Xa= "); Serial.print(X_out); Serial.print(" Ya= "); Serial.print(Y_out); Serial.print(" Za= "); Serial.println(Z_out); }
Описание: Итак, сначала нам нужно подключить библиотеку Wire.h, которая используется для связи по I2C. Каждое устройство, использующее I2C связь, имеет уникальный I2C адрес, и этот адрес можно найти в datasheet на ADXL345.
Итак, после того как мы определили адрес и переменные для трех выходов (X, Y, Z), в функции setup() нам нужно инициализировать библиотеку Wire, а затем перевести акселерометр в режим измерения. Для этого, если мы еще раз взглянем в datasheet, мы увидим, что нам нужно установить бит D3 в регистре POWER_CTL в HIGH.
Итак, с помощью функции beginTransmission() мы запускаем подключение, затем с помощью функции write() сообщаем, к какому регистру хотим получить доступ, и снова с помощью функции write() устанавливаем бит D3 в HIGH, записывая в регистр число 8 в десятичной системе счисления, которое соответствует установке высокого бита D3.
// Установите ADXL345 в режим измерения Wire.beginTransmission(ADXL345); // Начать общение с устройством Wire.write(0x2D); // работа с регистром POWER_CTL - 0x2D // Включить измерение Wire.write(8); // (8dec -> 0000 1000 двоичный) Бит D3 High для разрешения измерения Wire.endTransmission();
Теперь в функции loop() мы читаем данные с датчика. Данные для каждой оси хранятся в двух байтах или регистрах. Адреса этих регистров можно также посмотреть в datasheet:
Чтобы прочитать их все, начинаем с первого регистра, а при использовании функции RequestionFrom() мы считываем все 6 регистров. Затем, используя функцию read(), мы выбираем данные из каждого регистра по отдельности. Поскольку данные для каждой оси состоят из старшего и младшего байта, то для получения правильного значения мы объединяем их.
// === Считать данные акселерометра === // Wire.beginTransmission(ADXL345); Wire.write(0x32); // Начать с регистра 0x32 (ACCEL_XOUT_H) Wire.endTransmission(false); Wire.requestFrom(ADXL345, 6, true); // Чтение всех 6 регистров, значение каждой оси сохраняется в 2 регистрах X_out = ( Wire.read()| Wire.read() << 8); // Значение по оси X X_out = X_out/256; //Для диапазона + -2g нам нужно разделить необработанные значения на 256 в соответствии с datasheet Y_out = ( Wire.read()| Wire.read() << 8); // Значение по оси Y Y_out = Y_out/256; Z_out = ( Wire.read()| Wire.read() << 8); // Значение по оси Z Z_out = Z_out/256;
Выходные данные датчика фактически зависят от выбранной чувствительности, которая может варьироваться от + -2g до + -16g. Чувствительность по умолчанию составляет + -2g, поэтому нам нужно разделить результат на 256, чтобы получить значения от -1 до + 1g. 256 LSB/g означает, что у нас 256 отсчетов на 1g.
В зависимости от нашей потребности мы можем выбрать подходящую чувствительность. В случае для слежения ориентации вполне подойдет чувствительность + -2g, но для приложений, где нам нужно фиксировать более высокую силу ускорения, например, от внезапных движений, ударов и т. д., мы можем выбрать другие диапазоны чувствительности, используя регистр DATA_FORMAT и его биты D1 и D0.
Калибровка акселерометра ADXL345
После того как мы прочитали данные, мы можем просто вывести их в мониторе последовательного порта, чтобы проверить, соответствуют ли значения ожидаемым. В нашем случае значения, которые мы получили, были не совсем такими, какими они должны быть, особенно по оси Z, которая имела заметную ошибку в 0,1g.
Чтобы решить эту проблему, нам нужно откалибровать акселерометр, используя 3 регистра калибровки смещения, и вот как мы можем это сделать. Итак, нам нужно расположить датчик ровно и распечатать значения RAW, не деля их на 256.
Здесь мы можем видеть отклонение от нормы, в нашем случае значение по оси Z было около 283. Это положительная разница в 27. Теперь нам нужно разделить это значение на 4, и это даст нам число, которое нам нужно записать в регистр смещения оси Z. Если после этого мы загрузим скетч, то значение по оси Z будет ровно 256, или 1g, как и должно быть.
// Этот код должен находится в разделе setup() // Калибровка смещения // ось X Wire.beginTransmission(ADXL345); Wire.write(0x1E); // Регистр смещения оси X Wire.write(1); Wire.endTransmission(); delay(10); // ось Y Wire.beginTransmission(ADXL345); Wire.write(0x1F); //Регистр смещения оси Y Wire.write(-2); Wire.endTransmission(); delay(10); // ось Z Wire.beginTransmission(ADXL345); Wire.write(0x20); // Регистр смещения оси Z Wire.write(-7); Wire.endTransmission(); delay(10);
При необходимости нам следует откалибровать и другие оси, используя тот же метод. И сразу заметим, что эта калибровка не записывается в регистры постоянно. Нам нужно записывать эти значения в регистры при каждом включении датчика.
После того, как мы завершили калибровку, мы наконец можем вычислить крен и тангаж, используя две формулы:
// Расчет крена и тангажа (вращение вокруг оси X, вращение вокруг оси Y) roll = atan(Y_out / sqrt(pow(X_out, 2) + pow(Z_out, 2))) * 180 / PI; pitch = atan(-1 * X_out / sqrt(pow(Y_out, 2) + pow(Z_out, 2))) * 180 / PI;