Как отслеживать ориентацию с помощью Arduino и акселерометра ADXL345
<основной класс="главный сайт" id="главный">
В этом уроке мы научимся измерять угол и отслеживать ориентацию с помощью Arduino и датчика акселерометра ADXL345. Вы можете посмотреть следующее видео или прочитать письменное руководство ниже для получения более подробной информации.
Обзор
Сначала я объясню, как работает датчик и как считывать с него данные, а затем с помощью среды разработки Processing мы сделаем 3D-визуализацию ориентации акселерометра.

Как работает акселерометр ADXL345
Для начала давайте посмотрим, как работает датчик ADXL345. Это 3-осевой акселерометр, который может измерять как статические, так и динамические силы ускорения. Сила земного притяжения является типичным примером статической силы, тогда как динамические силы могут быть вызваны вибрациями, движениями и т. д.

Единицей измерения ускорения является метр на секунду в квадрате (м/с^2). Однако датчики акселерометра обычно выражают измерения в «g» или силе тяжести. Один «g» — это значение силы земного притяжения, равное 9,8 метрам в секунду в квадрате.
Таким образом, если у нас есть акселерометр, расположенный горизонтально, с его осью Z, направленной вверх, в направлении, противоположном гравитационной силе, выход датчика по оси Z будет равен 1g. С другой стороны, выходы X и Y будут равны нулю, потому что гравитационная сила перпендикулярна этим осям и никак на них не влияет.

Если мы перевернем датчик вверх дном, то выход по оси Z будет равен -1 g. Это означает, что выходные данные датчика из-за его ориентации на гравитацию могут варьироваться от -1g до +1g.

Таким образом, в соответствии с этими данными и с помощью некоторой математики тригонометрии мы можем рассчитать угол, под которым расположен датчик.
Как читать данные акселерометра ADXL345 с помощью Arduino
Хорошо, теперь давайте посмотрим, как мы можем прочитать данные акселерометра ADXL345 с помощью Arduino. Этот датчик использует протокол I2C для связи с Arduino, поэтому нам нужно всего два провода для его подключения, а также два провода для его питания.

Компоненты, необходимые для этого руководства по Arduino, можно получить по ссылкам ниже:
- Акселерометр ADXL345 ………………. Амазонка / Бангуд / AliExpress
- Плата Arduino ……………………………..
- Макет и соединительные провода…………
Код акселерометра ADXL345 для Arduino
Вот код Arduino для чтения данных акселерометра ADXL345.
/*
Arduino and ADXL345 Accelerometer Tutorial
by Dejan, https://howtomechatronics.com
*/
#include <Wire.h> // Wire library - used for I2C communication
int ADXL345 = 0x53; // The ADXL345 sensor I2C address
float X_out, Y_out, Z_out; // Outputs
void setup() {
Serial.begin(9600); // Initiate serial communication for printing the results on the Serial monitor
Wire.begin(); // Initiate the Wire library
// Set ADXL345 in measuring mode
Wire.beginTransmission(ADXL345); // Start communicating with the device
Wire.write(0x2D); // Access/ talk to POWER_CTL Register - 0x2D
// Enable measurement
Wire.write(8); // (8dec -> 0000 1000 binary) Bit D3 High for measuring enable
Wire.endTransmission();
delay(10);
}
void loop() {
// === Read acceleromter data === //
Wire.beginTransmission(ADXL345);
Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
X_out = ( Wire.read()| Wire.read() << 8); // X-axis value
X_out = X_out/256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
Y_out = ( Wire.read()| Wire.read() << 8); // Y-axis value
Y_out = Y_out/256;
Z_out = ( Wire.read()| Wire.read() << 8); // Z-axis value
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);
}
Code language: Arduino (arduino)
Описание: Итак, сначала нам нужно включить библиотеку Wire.h, которая используется для связи I2C. Если вы хотите узнать больше о том, как работает связь I2C и как использовать ее с Arduino, вы можете посмотреть мой другой подробный учебник.
Каждое устройство, использующее связь I2C, имеет уникальный адрес I2C, и этот адрес можно найти в техническом описании датчика (техническое описание ADXL345). Итак, после того, как мы определили адрес и переменные для трех выходов, в разделе настройки нам сначала нужно инициализировать библиотеку проводов, а затем установить акселерометр в режим измерения. Для этого, если мы снова взглянем на таблицу данных, мы увидим, что нам нужно установить бит D3 регистра POWER_CTL в ВЫСОКОЕ значение.

Таким образом, с помощью функции beginTransmission() мы запускаем связь, затем с помощью функции write() мы сообщаем, к какому регистру мы хотим получить доступ, и снова с помощью функции write() мы устанавливаем бит D3 в ВЫСОКОЕ значение, записывая число 8 в десятичное число, которое соответствует установке бита D3 в ВЫСОКОЕ значение.
// Set ADXL345 in measuring mode
Wire.beginTransmission(ADXL345); // Start communicating with the device
Wire.write(0x2D); // Access/ talk to POWER_CTL Register - 0x2D
// Enable measurement
Wire.write(8); // (8dec -> 0000 1000 binary) Bit D3 High for measuring enable
Wire.endTransmission();
Code language: Arduino (arduino)
В секции цикла теперь мы считываем данные с датчика. Данные для каждой оси хранятся в двух байтах или регистрах. Адреса этих регистров мы можем увидеть из даташита.

Чтобы прочитать их все, мы начинаем с первого регистра и с помощью функции requestionFrom() просим прочитать 6 регистров. Затем, используя функцию read(), мы считываем данные из каждого регистра, и, поскольку выходные данные представляют собой дополнение до двух, мы соответствующим образом комбинируем их, чтобы получить правильные значения.
// === Read acceleromter data === //
Wire.beginTransmission(ADXL345);
Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
X_out = ( Wire.read()| Wire.read() << 8); // X-axis value
X_out = X_out/256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
Y_out = ( Wire.read()| Wire.read() << 8); // Y-axis value
Y_out = Y_out/256;
Z_out = ( Wire.read()| Wire.read() << 8); // Z-axis value
Z_out = Z_out/256;
Code language: Arduino (arduino)
Выходные значения датчика на самом деле зависят от выбранной чувствительности, которая может варьироваться от +-2g до +-16g. Чувствительность по умолчанию составляет +-2 г, поэтому нам нужно разделить вывод на 256, чтобы получить значения от -1 до +1 г. 256 LSB/g означает, что у нас есть 256 отсчетов на g.

В зависимости от приложения мы можем выбрать соответствующую чувствительность. В этом случае для отслеживания ориентации вполне достаточно чувствительности +-2g, но для приложений, где нам необходимо ощущать более высокую силу ускорения, например внезапные движения, удары и т. д., мы можем выбрать некоторые другие диапазоны чувствительности, используя регистр DATA_FORMAT и его биты D1 и D0.

Калибровка акселерометра ADXL345
Тем не менее, как только мы прочитаем данные, мы можем просто распечатать их на последовательном мониторе, чтобы проверить, соответствуют ли значения ожидаемым. В моем случае значения, которые я получал, были не совсем такими, какими должны быть, особенно по оси Z, которая имела заметную погрешность в 0,1 г.

Чтобы решить эту проблему, нам нужно откалибровать акселерометр, используя 3 регистра калибровки смещения, и вот как мы можем это сделать. Итак, нам нужно расположить датчик горизонтально и распечатать значения RAW, не разделяя их на 256.

Отсюда теперь мы можем заметить, насколько отключены выходы, в моем случае выход Z был около 283. Это разница 27 в плюсе. Теперь нам нужно разделить это значение на 4, и это даст нам число, которое нам нужно записать в регистр смещения оси Z. Если мы загрузим код сейчас, на выходе по оси Z будет ровно 256 или 1 г, как и должно быть.
// This code goes in the SETUP section
// Off-set Calibration
//X-axis
Wire.beginTransmission(ADXL345);
Wire.write(0x1E); // X-axis offset register
Wire.write(1);
Wire.endTransmission();
delay(10);
//Y-axis
Wire.beginTransmission(ADXL345);
Wire.write(0x1F); // Y-axis offset register
Wire.write(-2);
Wire.endTransmission();
delay(10);
//Z-axis
Wire.beginTransmission(ADXL345);
Wire.write(0x20); // Z-axis offset register
Wire.write(-7);
Wire.endTransmission();
delay(10);
Code language: Arduino (arduino)
При необходимости мы должны откалибровать другую ось, используя тот же метод. И сразу заметим, что эта калибровка не записывается в регистры постоянно. Нам нужно записывать эти значения в регистры при каждом включении датчика.
Когда мы закончим с калибровкой, теперь мы можем, наконец, рассчитать крен и тангаж, или вращение вокруг оси X и вращение вокруг оси Y в градусах, используя эти две формулы.
// Calculate Roll and Pitch (rotation around X-axis, rotation around Y-axis)
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;
Code language: Arduino (arduino)
Для получения более подробной информации о том, как работают эти формулы, вы можете ознакомиться с этим примечанием по применению Freescale Semiconductor.
Arduino и отслеживание ориентации акселерометра ADXL345 — 3D-визуализация
Хорошо, теперь давайте сделаем пример 3D-визуализации акселерометра.

Итак, мы используем тот же код, который отправляет значения Roll и Pitch через последовательный порт. Вот полный код Arduino:
/*
Arduino and ADXL345 Accelerometer - 3D Visualization Example
by Dejan, https://howtomechatronics.com
*/
#include <Wire.h> // Wire library - used for I2C communication
int ADXL345 = 0x53; // The ADXL345 sensor I2C address
float X_out, Y_out, Z_out; // Outputs
float roll,pitch,rollF,pitchF=0;
void setup() {
Serial.begin(9600); // Initiate serial communication for printing the results on the Serial monitor
Wire.begin(); // Initiate the Wire library
// Set ADXL345 in measuring mode
Wire.beginTransmission(ADXL345); // Start communicating with the device
Wire.write(0x2D); // Access/ talk to POWER_CTL Register - 0x2D
// Enable measurement
Wire.write(8); // Bit D3 High for measuring enable (8dec -> 0000 1000 binary)
Wire.endTransmission();
delay(10);
//Off-set Calibration
//X-axis
Wire.beginTransmission(ADXL345);
Wire.write(0x1E);
Wire.write(1);
Wire.endTransmission();
delay(10);
//Y-axis
Wire.beginTransmission(ADXL345);
Wire.write(0x1F);
Wire.write(-2);
Wire.endTransmission();
delay(10);
//Z-axis
Wire.beginTransmission(ADXL345);
Wire.write(0x20);
Wire.write(-9);
Wire.endTransmission();
delay(10);
}
void loop() {
// === Read acceleromter data === //
Wire.beginTransmission(ADXL345);
Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
X_out = ( Wire.read() | Wire.read() << 8); // X-axis value
X_out = X_out / 256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
Y_out = ( Wire.read() | Wire.read() << 8); // Y-axis value
Y_out = Y_out / 256;
Z_out = ( Wire.read() | Wire.read() << 8); // Z-axis value
Z_out = Z_out / 256;
// Calculate Roll and Pitch (rotation around X-axis, rotation around Y-axis)
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;
// Low-pass filter
rollF = 0.94 * rollF + 0.06 * roll;
pitchF = 0.94 * pitchF + 0.06 * pitch;
Serial.print(rollF);
Serial.print("/");
Serial.println(pitchF);
}
Code language: Arduino (arduino)
Теперь в среде разработки Processing нам нужно получить эти значения и использовать их для поворота 3D-объекта, который мы будем создавать. Вот полный код обработки:
/*
Arduino and ADXL345 Accelerometer - 3D Visualization Example
by Dejan, https://howtomechatronics.com
*/
import processing.serial.*;
import java.awt.event.KeyEvent;
import java.io.IOException;
Serial myPort;
String data="";
float roll, pitch;
void setup() {
size (960, 640, P3D);
myPort = new Serial(this, "COM8", 9600); // starts the serial communication
myPort.bufferUntil('\n');
}
void draw() {
translate(width/2, height/2, 0);
background(33);
textSize(22);
text("Roll: " + int(roll) + " Pitch: " + int(pitch), -100, 265);
// Rotate the object
rotateX(radians(roll));
rotateZ(radians(-pitch));
// 3D 0bject
textSize(30);
fill(0, 76, 153);
box (386, 40, 200); // Draw box
textSize(25);
fill(255, 255, 255);
text("www.HowToMechatronics.com", -183, 10, 101);
//delay(10);
//println("ypr:\t" + angleX + "\t" + angleY); // Print the values to check whether we are getting proper values
}
// Read data from the Serial Port
void serialEvent (Serial myPort) {
// reads the data from the Serial Port up to the character '.' and puts it into the String variable "data".
data = myPort.readStringUntil('\n');
// if you got any bytes other than the linefeed:
if (data != null) {
data = trim(data);
// split the string at "/"
String items[] = split(data, '/');
if (items.length > 1) {
//--- Roll,Pitch in degrees
roll = float(items[0]);
pitch = float(items[1]);
}
}
}
Code language: Arduino (arduino)
Описание: Итак, здесь нам нужно включить последовательную библиотеку, определить последовательный порт и скорость передачи данных, которая должна соответствовать скорости передачи загруженного скетча Arduino. Затем мы считываем поступающие данные и помещаем их в соответствующие переменные крена и тангажа. В основном цикле отрисовки мы используем эти значения для поворота 3D-объекта, и в данном случае это просто прямоугольник с определенным цветом и текстом на нем.
Если мы запустим скетч, появится 3D-объект, и он будет отслеживать ориентацию датчика акселерометра. Здесь мы можем заметить, что объект на самом деле немного шатается, потому что акселерометр улавливает не только гравитационную силу, но и небольшие силы, создаваемые движениями нашей руки. Чтобы получить более плавный результат, мы можем использовать простой фильтр нижних частот. Вот я реализовал в коде Arduino такой фильтр, который берет 94% предыдущего состояния и добавляет 6% текущего состояния или угла.
// Low-pass filter
rollF = 0.94 * rollF + 0.06 * roll;
pitchF = 0.94 * pitchF + 0.06 * pitch;
Code language: Arduino (arduino)
С помощью этого фильтра мы можем заметить, что объект теперь движется намного более плавно, но есть и побочный эффект — более медленная реакция. Мы также можем заметить, что нам не хватает Yaw или вращения вокруг оси Z. Используя только данные 3-осевого акселерометра, мы не можем рассчитать отклонение от курса.
Чтобы сделать это и улучшить общую производительность нашего датчика отслеживания ориентации, нам действительно нужно включить дополнительный датчик, гироскоп, и объединить его данные с акселерометром.

Таким образом, мы можем либо использовать акселерометр ADXL345 в сочетании с датчиком гироскопа, либо использовать IMU MPU6050, в котором 3-осевой акселерометр и 3-осевой гироскоп интегрированы в один чип. Вы можете найти более подробное руководство по этому датчику в моем следующем видео.
Надеюсь, вам понравился этот урок и вы узнали что-то новое. Не стесняйтесь задавать любые вопросы в разделе комментариев ниже и не забудьте проверить мою коллекцию проектов Arduino.
Производственный процесс
- Управление серводвигателем с помощью Arduino и MPU6050
- u-blox LEA-6H 02 GPS-модуль с Arduino и Python
- Как читать температуру и влажность на Blynk с DHT11
- Распознавание и синтез речи с помощью Arduino
- Как создавать музыку с помощью Arduino
- Как использовать NMEA-0183 с Arduino
- Как использовать Modbus с Arduino
- Умная кофемашина с Arduino и Bluetooth
- Анимированный умный свет с Alexa и Arduino
- Распознавание речи с помощью Arduino и BitVoicer Server