Cómo rastrear la orientación con Arduino y el acelerómetro ADXL345
En este tutorial, aprenderemos a medir el ángulo y la orientación del seguimiento con Arduino y el sensor del acelerómetro ADXL345. Puede ver el siguiente video o leer el tutorial escrito a continuación para obtener más detalles.
Resumen
Primero, explicaré cómo funciona el sensor y cómo leer los datos de él, y luego, usando el entorno de desarrollo Processing, haremos una visualización en 3D de la orientación del acelerómetro.
Cómo funciona el acelerómetro ADXL345
Para empezar, veamos cómo funciona el sensor ADXL345. Este es un acelerómetro de 3 ejes que puede medir fuerzas de aceleración tanto estáticas como dinámicas. La fuerza gravitatoria terrestre es un ejemplo típico de fuerza estática, mientras que las fuerzas dinámicas pueden ser causadas por vibraciones, movimientos, etc.
La unidad de medida de la aceleración es el metro por segundo al cuadrado (m/s^2). Sin embargo, los sensores de acelerómetro suelen expresar las medidas en “g” o gravedad. Una "g" es el valor de la fuerza gravitacional de la tierra que es igual a 9,8 metros por segundo al cuadrado.
Entonces, si tenemos un acelerómetro colocado plano, con su eje Z apuntando hacia arriba, opuesto a la fuerza gravitacional, la salida del eje Z del sensor será de 1 g. Por otro lado, las salidas X e Y serán cero, porque la fuerza gravitacional es perpendicular a estos ejes y no los afecta en absoluto.
Si volteamos el sensor boca abajo, la salida del eje Z será -1 g. Esto significa que las salidas del sensor debido a su orientación a la gravedad pueden variar de -1g a +1g.
Entonces, de acuerdo con estos datos y usando algunas matemáticas de trigonometría, podemos calcular el ángulo en el que se coloca el sensor.
Cómo leer los datos del acelerómetro ADXL345 con Arduino
Ok, ahora veamos cómo podemos leer los datos del acelerómetro ADXL345 usando el Arduino. Este sensor utiliza el protocolo I2C para la comunicación con Arduino, por lo que solo necesitamos dos cables para conectarlo, más los dos cables para alimentarlo.
Puede obtener los componentes necesarios para este tutorial de Arduino desde los siguientes enlaces:
Acelerómetro ADXL345 ………………. Amazonas / Banggood / AliExpress
Placa Arduino …………………………..
Protoboard y cables de salto …………
Código Arduino del acelerómetro ADXL345
Aquí está el código Arduino para leer los datos del acelerómetro 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)
Descripción: Entonces, primero debemos incluir la biblioteca Wire.h que se usa para la comunicación I2C. Si desea obtener más información sobre cómo funciona la comunicación I2C y cómo usarla con Arduino, puede consultar mi otro tutorial detallado.
Cada dispositivo que utiliza la comunicación I2C tiene una dirección I2C única, y esta dirección se puede encontrar en la hoja de datos del sensor (hoja de datos ADXL345). Entonces, una vez que definimos la dirección y las variables para las tres salidas, en la sección de configuración, primero debemos inicializar la biblioteca de cables y luego configurar el acelerómetro en modo de medición. Para hacer eso, si volvemos a mirar la hoja de datos, podemos ver que necesitamos establecer el bit D3 del registro POWER_CTL en ALTO.
Entonces, usando la función beginTransmission() comenzamos la comunicación, luego usando la función write() le decimos a qué registro queremos acceder, y nuevamente usando la función write() establecemos el bit D3 en ALTO, escribiendo el número 8 en decimal que corresponde a poner el bit D3 en ALTO.
// 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)
En la sección de bucle ahora leemos los datos del sensor. Los datos de cada eje se almacenan en dos bytes o registros. Podemos ver las direcciones de estos registros en la hoja de datos.
Para leerlos todos, comenzamos con el primer registro, y luego, usando la función requestionFrom(), solicitamos leer los 6 registros. Luego, utilizando la función read(), leemos los datos de cada registro y, dado que las salidas son complementos a dos, las combinamos adecuadamente para obtener los valores correctos.
// === 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)
Los valores de salida del sensor en realidad dependen de la sensibilidad seleccionada, que puede variar de +- 2 g a +-16 g. La sensibilidad predeterminada es +-2g, por eso necesitamos dividir la salida por 256 para obtener valores de -1 a +1g. El 256 LSB/g significa que tenemos 256 cuentas por g.
Dependiendo de la aplicación podemos seleccionar la sensibilidad adecuada. En este caso, para la orientación del seguimiento, una sensibilidad de +-2g está bien, pero para aplicaciones en las que necesitamos detectar una mayor fuerza de aceleración de movimientos repentinos, golpes, etc., podemos elegir algunos de los otros rangos de sensibilidad usando el registro DATA_FORMAT y sus bits D1 y D0.
Calibración del acelerómetro ADXL345
Sin embargo, una vez que leemos los datos, simplemente podemos imprimirlos en el monitor serie para verificar si los valores son los esperados. En mi caso, los valores que estaba obteniendo no eran exactamente como deberían ser, especialmente el eje Z que tenía un error notable de 0,1 g.
Para resolver este problema, necesitamos calibrar el acelerómetro usando los 3 registros de calibración compensados, y así es como podemos hacerlo. Por lo tanto, debemos colocar el sensor plano e imprimir los valores RAW sin dividirlos por 256.
Desde aquí ahora podemos notar cuánto están apagadas las salidas, en mi caso, la salida Z estaba alrededor de 283. Esa es una diferencia de 27 en positivo. Ahora necesitamos dividir este valor por 4, y eso nos dará el número que necesitamos para escribir en el registro de compensación del eje Z. Si cargamos el código ahora, la salida del eje Z será exactamente 256, o 1 g, como debería ser.
// 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)
Si es necesario, debemos calibrar el otro eje usando el mismo método. Y solo una nota rápida de que esta calibración no se escribe permanentemente en los registros. Necesitamos escribir estos valores en los registros en cada encendido del sensor.
Una vez que hayamos terminado con la calibración, ahora podemos finalmente calcular el Roll and Pitch, o la rotación alrededor del eje X y la rotación alrededor del eje Y en grados, usando estas dos fórmulas.
// 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)
Para obtener más detalles sobre cómo funcionan estas fórmulas, puede consultar esta nota de aplicación de Freescale Semiconductor.
Rastreo de orientación del acelerómetro Arduino y ADXL345:visualización 3D
Bien, ahora hagamos el ejemplo de visualización 3D del acelerómetro.
Entonces, estamos usando el mismo código, que envía los valores de Roll y Pitch a través del puerto serial. Aquí está el código completo de 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)
Ahora, en el entorno de desarrollo de procesamiento, necesitamos recibir estos valores y usarlos para rotar el objeto 3D que crearemos. Aquí está el código de procesamiento completo:
/*
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)
Descripción: Entonces, aquí, debemos incluir la biblioteca en serie, definir el puerto en serie y la velocidad en baudios que debe coincidir con la velocidad en baudios del boceto Arduino cargado. Luego leemos los datos entrantes y los colocamos en las variables de balanceo y cabeceo apropiadas. En el ciclo de dibujo principal, usamos estos valores para rotar el objeto 3D, y en este caso es un cuadro simple con un color particular y un texto en él.
Si ejecutamos el boceto, aparecerá el objeto 3D y seguirá la orientación del sensor del acelerómetro. Podemos notar aquí que el objeto en realidad está un poco inestable y eso se debe a que el acelerómetro captura no solo la fuerza gravitatoria, sino también pequeñas fuerzas generadas por los movimientos de nuestra mano. Para obtener un resultado más suave, podemos usar un filtro de paso bajo simple. Aquí implementé un filtro de este tipo en el código Arduino, que toma el 94 % del estado anterior y agrega el 6 % del estado o ángulo actual.
// Low-pass filter
rollF = 0.94 * rollF + 0.06 * roll;
pitchF = 0.94 * pitchF + 0.06 * pitch;
Code language: Arduino (arduino)
Con este filtro, podemos notar que el objeto se mueve mucho más suave ahora, pero también hay un efecto secundario y es una respuesta más lenta. También podemos notar que nos falta el Yaw, o la rotación alrededor del eje Z. Usando solo los datos del acelerómetro de 3 ejes, no podemos calcular la Yaw.
Para hacer eso y mejorar el rendimiento general de nuestro sensor de seguimiento de orientación, necesitamos incluir un sensor adicional, un giroscopio, y fusionar sus datos con el acelerómetro.
Entonces, podemos usar el acelerómetro ADXL345 en combinación con un sensor de giroscopio, o usar la IMU MPU6050 que tiene un acelerómetro de 3 ejes y un giroscopio de 3 ejes integrados en un solo chip. Puede encontrar un tutorial más detallado sobre este sensor en mi próximo video.
Espero que hayas disfrutado este tutorial y hayas aprendido algo nuevo. Siéntase libre de hacer cualquier pregunta en la sección de comentarios a continuación y no olvide consultar mi colección de proyectos Arduino.