风筝
发表于: 2019-8-13 17:41:04 | 显示全部楼层

在本篇文章中,我们将学习如何使用Arduino开发板和ADXL345加速计传感器测量角度和跟踪方向。首先,我们将介绍传感器是如何工作以及如何从中读取数据,然后使用Processing开发环境,对加速度计方向进行3D可视化。

Arduino-and-ADXL345-Accelerometer-Tutorial.jpg


ADXL345加速度计的工作原理

首先,让我们来看看ADXL345传感器的工作原理。这是一个3轴加速度计,可以测量静态和动态加速度力。地球重力是静力的典型例子,而动态力可以由振动、运动等引起。

ADXL345-Acceleromter-How-It-Works.jpg

ADXL345加速度计的工作原理


加速度的测量单位是米每二次方秒(m/s^2)。但是,加速计传感器通常以“g”或重力表示测量值。一个“g”是地球重力的值,它等于每平方秒9.8米。

因此,如果我们将加速度计平放,其Z轴朝上,与重力相反,则传感器的Z轴输出将为1g。另一方面,X和Y输出将为零,因为重力垂直于这些轴并且根本不影响它们。

ADXL345-3-axis-acceleromter-output-data.jpg

ADXL345 3轴加速器输出数据


如果我们将传感器倒置,那么Z轴输出将为-1 g。这意味着传感器由于其重力方向的输出可以在-1g到+ 1g之间变化。

adxl345-z-axis-output.jpg

adxl345 z轴输出


因此,根据这些数据并使用一些三角函数数学,我们可以计算传感器定位的角度。


如何使用Arduino读取ADXL345加速度计数据

好的,现在让我们看看如何使用Arduino读取ADXL345加速度计数据。该传感器使用I2C协议与Arduino进行通信,因此我们只需要两条线连接它,另外还需要两条线来为它供电。

Arduino-and-ADXL345-Accelerometer-Circuit-Diagram.png

Arduino和ADXL345加速度计电路图


ADXL345加速度计的Arduino代码

以下是读取ADXL345加速度计数据的Arduino代码。

  1. /*
  2.     Arduino and ADXL345 Accelerometer Tutorial
  3.      by Dejan, https://howtomechatronics.com
  4. */
  5. #include <Wire.h>  // Wire library - used for I2C communication
  6. int ADXL345 = 0x53; // The ADXL345 sensor I2C address
  7. float X_out, Y_out, Z_out;  // Outputs
  8. void setup() {
  9.   Serial.begin(9600); // Initiate serial communication for printing the results on the Serial monitor
  10.   Wire.begin(); // Initiate the Wire library
  11.   // Set ADXL345 in measuring mode
  12.   Wire.beginTransmission(ADXL345); // Start communicating with the device
  13.   Wire.write(0x2D); // Access/ talk to POWER_CTL Register - 0x2D
  14.   // Enable measurement
  15.   Wire.write(8); // (8dec -> 0000 1000 binary) Bit D3 High for measuring enable
  16.   Wire.endTransmission();
  17.   delay(10);
  18. }
  19. void loop() {
  20.   // === Read acceleromter data === //
  21.   Wire.beginTransmission(ADXL345);
  22.   Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
  23.   Wire.endTransmission(false);
  24.   Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  25.   X_out = ( Wire.read()| Wire.read() << 8); // X-axis value
  26.   X_out = X_out/256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
  27.   Y_out = ( Wire.read()| Wire.read() << 8); // Y-axis value
  28.   Y_out = Y_out/256;
  29.   Z_out = ( Wire.read()| Wire.read() << 8); // Z-axis value
  30.   Z_out = Z_out/256;
  31.   Serial.print("Xa= ");
  32.   Serial.print(X_out);
  33.   Serial.print("   Ya= ");
  34.   Serial.print(Y_out);
  35.   Serial.print("   Za= ");
  36.   Serial.println(Z_out);
  37. }
复制代码

代码描述:首先我们需要包含用于I2C通信的Wire.h库。每个使用I2C通信的器件都有一个唯一的I2C地址,该地址可以在传感器的数据手册中找到(ADXL345数据手册)。因此,我们定义了三个输出的地址和变量之后,首先在setup函数部分中,我们需要初始化wire库,然后将加速度计设置为测量模式。为了实现这一点,如果我们再次查看数据手册,可以看到需要将POWER_CTL寄存器的位D3置为高电平。

adxl345-power-register-enabling-measuring-mode.png

adxl345电源寄存器 - 启用测量模式


接下来,使用beginTransmission()函数,我们开始通信,然后使用write()函数,我们告诉要访问哪个寄存器,再次使用write()函数,将D3位设置为HIGH,通过写入十进制的数字8,对应于将D3位设置为HIGH。

  1. // Set ADXL345 in measuring mode
  2.   Wire.beginTransmission(ADXL345); // Start communicating with the device
  3.   Wire.write(0x2D); // Access/ talk to POWER_CTL Register - 0x2D
  4.   // Enable measurement
  5.   Wire.write(8); // (8dec -> 0000 1000 binary) Bit D3 High for measuring enable
  6.   Wire.endTransmission();
复制代码

现在我们在loop()函数部分中读取传感器的数据。每个轴的数据存储在两个字节或寄存器中。我们可以从数据手册中看到这些寄存器的地址。

adxl345-acceleromter-x-y-z-data-registers.png

adxl345加速器x y z数据寄存器


为了全部读取它们,我们从第一个寄存器开始,并使用requestedtionFrom()函数,我们要求读取6个寄存器。然后使用read()函数,我们从每个寄存器读取数据,并且由于输出是两个补码,我们将它们适当地组合以获得正确的值。

  1. // === Read acceleromter data === //
  2.   Wire.beginTransmission(ADXL345);
  3.   Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
  4.   Wire.endTransmission(false);
  5.   Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  6.   X_out = ( Wire.read()| Wire.read() << 8); // X-axis value
  7.   X_out = X_out/256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
  8.   Y_out = ( Wire.read()| Wire.read() << 8); // Y-axis value
  9.   Y_out = Y_out/256;
  10.   Z_out = ( Wire.read()| Wire.read() << 8); // Z-axis value
  11.   Z_out = Z_out/256;
复制代码

传感器的输出值实际上取决于所选的灵敏度,灵敏度可在±2g到±16g之间变化。默认灵敏度为±2g,这就是为什么我们需要将输出除以256以获得-1到+1g的值。 256 LSB / g意味着我们每g有256个计数。

adxl345-sensitivity-range.png

adxl345灵敏度范围


根据应用,我们可以选择适当的灵敏度。在本例中,对于跟踪方向,±2g灵敏度很好,但对于需要从突然移动、冲击等感知更高加速力的应用,我们可以使用DATA_FORMAT寄存器和它的D1和D0位选择一些其他灵敏度范围。

adxl345-sensitivity-range-registers-and-truth-table.png

adxl345灵敏度范围寄存器和真值表


ADXL345加速器校准

然而,一旦我们读取数据,我们可以简单地在串行监视器上打印它以检查值是否符合预期。本例中,我得到的值并不完全正确,特别是Z轴有0.1g的明显误差。

adxl345-accelerometer-calibration.jpg

adxl345加速度计校准


为了解决这个问题,我们需要使用3个偏移校准寄存器来校准加速度计,以下是如何做到这一点。我们需要将传感器平放,并打印RAW值而不将它们除以256。

adxl345-accelerometer-calibration-process.jpg

adxl345加速度计校准过程


从现在开始,我们可以注意到输出偏移了多少,在我的例子中,Z输出大约是283。这相差27,为正。现在我们需要将此值除以4,这将使用我们需要写入Z轴偏移寄存器的数字。如果我们现在上传代码,Z轴输出将正好是256或1g。

  1. // This code goes in the SETUP section
  2. // Off-set Calibration
  3.   //X-axis
  4.   Wire.beginTransmission(ADXL345);
  5.   Wire.write(0x1E);  // X-axis offset register
  6.   Wire.write(1);
  7.   Wire.endTransmission();
  8.   delay(10);
  9.   //Y-axis
  10.   Wire.beginTransmission(ADXL345);
  11.   Wire.write(0x1F); // Y-axis offset register
  12.   Wire.write(-2);
  13.   Wire.endTransmission();
  14.   delay(10);
  15.   
  16.   //Z-axis
  17.   Wire.beginTransmission(ADXL345);
  18.   Wire.write(0x20); // Z-axis offset register
  19.   Wire.write(-7);
  20.   Wire.endTransmission();
  21.   delay(10);
复制代码

如果需要,我们应该使用相同的方法校准另一个轴。 有一点需要注意的是,此校准不会永久写入寄存器。 我们需要在传感器每次上电时将这些值写入寄存器。

完成校准后,我们现在可以使用这两个公式计算出滚动和俯仰,或绕X轴旋转和围绕Y轴旋转的度数。

  1. // Calculate Roll and Pitch (rotation around X-axis, rotation around Y-axis)
  2.   roll = atan(Y_out / sqrt(pow(X_out, 2) + pow(Z_out, 2))) * 180 / PI;
  3.   pitch = atan(-1 * X_out / sqrt(pow(Y_out, 2) + pow(Z_out, 2))) * 180 / PI;
复制代码

有关这些公式如何工作的更多详细信息,请查看此飞思卡尔半导体应用说明


Arduino和ADXL345加速度计方向跟踪 - 三维可视化

好吧,让我们现在制作加速度计3D可视化示例。

Arduino-and-ADXL345-Accelerometer-Orientation-Tracking-3D-Visualization.jpg

Arduino和ADXL345加速度计方向跟踪 - 三维可视化


因此,我们使用相同的代码,通过串口发送Roll和Pitch值。 以下是完整的Arduino代码:

  1. /*
  2.     Arduino and ADXL345 Accelerometer - 3D Visualization Example
  3.      by Dejan, https://howtomechatronics.com
  4. */
  5. #include <Wire.h>  // Wire library - used for I2C communication
  6. int ADXL345 = 0x53; // The ADXL345 sensor I2C address
  7. float X_out, Y_out, Z_out;  // Outputs
  8. float roll,pitch,rollF,pitchF=0;
  9. void setup() {
  10.   Serial.begin(9600); // Initiate serial communication for printing the results on the Serial monitor

  11.   Wire.begin(); // Initiate the Wire library
  12.   // Set ADXL345 in measuring mode
  13.   Wire.beginTransmission(ADXL345); // Start communicating with the device
  14.   Wire.write(0x2D); // Access/ talk to POWER_CTL Register - 0x2D
  15.   // Enable measurement
  16.   Wire.write(8); // Bit D3 High for measuring enable (8dec -> 0000 1000 binary)
  17.   Wire.endTransmission();
  18.   delay(10);
  19.   //Off-set Calibration
  20.   //X-axis
  21.   Wire.beginTransmission(ADXL345);
  22.   Wire.write(0x1E);
  23.   Wire.write(1);
  24.   Wire.endTransmission();
  25.   delay(10);
  26.   //Y-axis
  27.   Wire.beginTransmission(ADXL345);
  28.   Wire.write(0x1F);
  29.   Wire.write(-2);
  30.   Wire.endTransmission();
  31.   delay(10);
  32.   //Z-axis
  33.   Wire.beginTransmission(ADXL345);
  34.   Wire.write(0x20);
  35.   Wire.write(-9);
  36.   Wire.endTransmission();
  37.   delay(10);
  38. }
  39. void loop() {
  40.   // === Read acceleromter data === //
  41.   Wire.beginTransmission(ADXL345);
  42.   Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
  43.   Wire.endTransmission(false);
  44.   Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  45.   X_out = ( Wire.read() | Wire.read() << 8); // X-axis value
  46.   X_out = X_out / 256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
  47.   Y_out = ( Wire.read() | Wire.read() << 8); // Y-axis value
  48.   Y_out = Y_out / 256;
  49.   Z_out = ( Wire.read() | Wire.read() << 8); // Z-axis value
  50.   Z_out = Z_out / 256;
  51.   // Calculate Roll and Pitch (rotation around X-axis, rotation around Y-axis)
  52.   roll = atan(Y_out / sqrt(pow(X_out, 2) + pow(Z_out, 2))) * 180 / PI;
  53.   pitch = atan(-1 * X_out / sqrt(pow(Y_out, 2) + pow(Z_out, 2))) * 180 / PI;
  54.   // Low-pass filter
  55.   rollF = 0.94 * rollF + 0.06 * roll;
  56.   pitchF = 0.94 * pitchF + 0.06 * pitch;
  57.   Serial.print(rollF);
  58.   Serial.print("/");
  59.   Serial.println(pitchF);
  60. }
复制代码

现在在Processing开发环境中,我们需要接收这些值并使用它们来旋转我们将创建的3D对象。 这是完整的Processing代码:

  1. /*
  2.     Arduino and ADXL345 Accelerometer - 3D Visualization Example
  3.      by Dejan, https://howtomechatronics.com
  4. */
  5. import processing.serial.*;
  6. import java.awt.event.KeyEvent;
  7. import java.io.IOException;
  8. Serial myPort;
  9. String data="";
  10. float roll, pitch;
  11. void setup() {
  12.   size (960, 640, P3D);
  13.   myPort = new Serial(this, "COM8", 9600); // starts the serial communication
  14.   myPort.bufferUntil('\n');
  15. }
  16. void draw() {
  17.   translate(width/2, height/2, 0);
  18.   background(33);
  19.   textSize(22);
  20.   text("Roll: " + int(roll) + "     Pitch: " + int(pitch), -100, 265);
  21.   // Rotate the object
  22.   rotateX(radians(roll));
  23.   rotateZ(radians(-pitch));
  24.   
  25.   // 3D 0bject
  26.   textSize(30);  
  27.   fill(0, 76, 153);
  28.   box (386, 40, 200); // Draw box
  29.   textSize(25);
  30.   fill(255, 255, 255);
  31.   text("www.HowToMechatronics.com", -183, 10, 101);
  32.   //delay(10);
  33.   //println("ypr:\t" + angleX + "\t" + angleY); // Print the values to check whether we are getting proper values
  34. }
  35. // Read data from the Serial Port
  36. void serialEvent (Serial myPort) {
  37.   // reads the data from the Serial Port up to the character '.' and puts it into the String variable "data".
  38.   data = myPort.readStringUntil('\n');
  39.   // if you got any bytes other than the linefeed:
  40.   if (data != null) {
  41.     data = trim(data);
  42.     // split the string at "/"
  43.     String items[] = split(data, '/');
  44.     if (items.length > 1) {
  45.       //--- Roll,Pitch in degrees
  46.       roll = float(items[0]);
  47.       pitch = float(items[1]);
  48.     }
  49.   }
  50. }
复制代码

代码描述:在这里,我们需要包括串行库,定义串口和波特率,这需要与上传的Arduino草图的波特率相匹配。然后我们读取输入数据并将其放入相应的roll和pitch变量中。在主draw循环中,我们使用这些值来旋转3D对象,本例中是一个具有特定颜色和文本的简单框。


如果我们运行草图,3D对象将出现,它将跟踪加速度计传感器的方向。我们在这里可以注意到,物体实际上有点不稳定,这是因为加速度计不仅捕获了重力,而且捕获了我们手的运动产生的小力。为了获得更平滑的结果,我们可以使用简单的低通滤波器。在这里,我在Arduino代码中实现了这样一个过滤器,它占用了之前状态的94%,并且增加了当前状态或角度的6%。

  1. // Low-pass filter
  2.   rollF = 0.94 * rollF + 0.06 * roll;
  3.   pitchF = 0.94 * pitchF + 0.06 * pitch;
复制代码

使用此过滤器,我们可以注意到对象现在移动得更顺畅,但也有副作用,响应速度较慢。我们还可以注意到我们确实Yaw,或绕Z轴的旋转值。仅使用3轴加速度计数据,我们无法计算偏航。

为了实现这一目标并提高定向跟踪传感器的整体性能,我们实际上需要包括一个额外的传感器,一个陀螺仪,并将其数据与加速度计融合。

adxl345-accelerometer-and-l3g4200d-gyrscope-or-mpu6050-6dof-module.jpg

adxl345加速度计和l3g4200d gyrscope或mpu6050 6dof模块


因此,我们可以将ADXL345加速度计与一些陀螺仪传感器结合使用,或者使用MPU6050 IMU,它将3轴加速度计和3轴陀螺仪集成在一个芯片上。我希望你喜欢这篇文章并学到新的东西。 如果遇到问题,请随时在本帖下面进行回复。

跳转到指定楼层
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题 700 | 回复: 1480



手机版|

GMT+8, 2024-4-19 10:32 , Processed in 0.141368 second(s), 6 queries , Gzip On, MemCache On. Powered by Discuz! X3.5

YiBoard一板网 © 2015-2022 地址:河北省石家庄市长安区高营大街 ( 冀ICP备18020117号 )

快速回复 返回顶部 返回列表