MEMS加速度计、陀螺仪磁强计和Arduino

在本教程中,我们将学习MEMS加速度计,陀螺仪和磁力仪的工作以及如何将它们与Arduino板一起使用。还使用加工IDE,我们将使用传感器进行一些实际应用。您可以观看以下视频或阅读下面的书面教程。

概要


MEMS是一种非常小的系统或设备,由尺寸从0.001 mm到0.1 mm不等的微组件组成。这些组件由硅、聚合物、金属和/或陶瓷制成,它们通常与CPU(微控制器)结合在一起完成系统。现在我们将简要解释每一种微机电系统(MEMS)传感器的工作原理。

MEMS加速度计


它通过测量电容的变化来测量加速度。它的微观结构是这样的。它有一个连接在弹簧上的质量,弹簧被限制沿一个方向移动,固定的外部板。所以当在特定方向上施加加速度时,质量就会移动,平板之间的电容和质量就会改变。电容的变化将被测量,处理,它将对应于一个特定的加速度值。

MEMS-Accelerometer-How-It-Works

MEMS陀螺仪


陀螺仪使用科里奥利效应测量角度率。当质量在具有特定速度的特定方向上移动并且当外部角速率将被施加为与绿色箭头的显示时,将发生与蓝色红色箭头的示出,这将导致质量垂直位移。如此类似于加速度计,该位移将导致电容的变化,该电容将被测量,处理,并且它将对应于特定的角速率。

Gyroscope-How-It-Works

陀螺仪的微结构看起来像这样。一种恒定移动或振荡的质量,并且当外部角速率将施加肌肉的柔性部分将移动并制造垂直的位移。

Gyroscope-Microstructure

MEMS磁力计


它利用霍尔效应或磁阻效应来测量地球磁场。事实上,市场上几乎90%的传感器都使用霍尔效应,下面是它的工作原理。betway

霍尔效应-01

如果我们在照片中示出了类似的导电板,并且我们将电流设置为流过它,则电子将直接从板的另一侧流动。现在,如果我们在板附近带来一些磁场,我们会扰乱直流,并且电子将使板的一侧偏转到板的另一侧。这意味着如果我们现在在这两面之间放置仪表,我们将获得一些电压,这取决于磁场强度及其方向。

磁力计磁仪 - 工作 - 工作室效果 -
市场上另外10%的传感器使用磁阻效应。这些传感器使用对磁场敏感的材料,通常由铁(Fe)和镍(Ne)组成。所以当这些材料暴露在磁场中时,它们的电阻就会改变。

Arduino


好的,现在让我们将这些传感器连接到Arduino Board并进行一些使用它们。作为一个例子,我将使用具有以下传感器的GY-80分支板:ADXL345三轴加速度计, L3G4200D 3轴陀螺仪,MC5883L 3轴磁力仪,还有气压计和温度计,我们在本教程中不会使用。

你可以从下面的任何网站获得这些组件:

必威外围提钱披露:这些是联盟链接。作为亚马逊助理,我从合格购买中获得。

GY-80-LOAD-AND-ARDUINO

这个板使用I2C通信协议,这意味着我们可以用两根线使用所有传感器。所以为了在Arduino和传感器之间进行通信我们需要知道它们唯一的设备地址以及它们的内部寄存器地址以便从它们那里获取数据。这些地址可以从传感器的数据表中找到:

关于I2C通信如何工作的更多细节,您可以查看我的其他I2C通信协议教程

源代码


现在让我们看看要从传感器获取数据的代码。我们将从加速度计开始,每个代码之前都会有一些解释,以及代码的评论中的一些附加说明。

Arduino加速度计代码

首先,我们需要包括Arduino Wire库并定义传感器的寄存器地址。在设置部分中,我们需要启动线路库并开始串行通信,因为我们将使用串行监视器来显示结果。此外,我们还需要激活传感器,或通过向Power_ctl寄存器发送适当的字节来启用测量,这就是我们的方式。使用Wire.BegintRansmission()函数,我们选择我们将在这种情况下进行谈话的传感器,在这种情况下为3轴加速度计。然后使用Wire.Write()函数,我们告诉我们将谈论哪个内部寄存器。在此之后,我们将发送适当的字节来实现测量。使用Wire.endTransmission()函数我们将结束传输,并将数据传输到寄存器。

在loop部分中,我们需要读取每个轴的数据。我们从X轴开始。因此,首先我们将选择我们要讨论的寄存器,在本例中是两个X轴内部寄存器。然后使用Wire.requestFrom()函数从两个寄存器请求传输的数据或两个字节。的Wire.available ()函数将返回可用于检索的字节数,如果该数目与请求的字节数匹配(在本例中为2字节),则使用wire.read()功能我们将从X轴的两个寄存器中读取字节。

的output data from the registers is two’s complement, with X0 as the least significant byte and X1 as the most significant byte so we need to convert these bytes into float values from -1 to +1 depending on the direction of the X – Axis relative to the Earth acceleration or the gravity. We will repeat this procedure for the two other axis and at the end we will print these values on the serial monitor.

#include  // ---加速度计寄存器地址#define power_register 0x2d #define x_axis_register_datax0 0x32 // datax0内部寄存器的hexadecima地址。#define x_axis_register_datax1 0x33 // Datax1内部寄存器的HexadeCima地址。#define y_axis_register_datay0 0x34 #define y_axis_register_datay1 0x35 #define z_axis_register_dataz0 0x36 #dataz0 0x36 #define z_axis_register_dataz1 0x37 int adxaddress = 0x53;//设备地址,其中还包括用于选择模式的第8位,在这种情况下读取。int x0,x1,x_out;int y0,y1,y_out;int z1,z0,z_out;浮子Xa,ya,za;void setup(){wire.begin();//发起线条库序列号.Begin(9600); delay(100); Wire.beginTransmission(ADXAddress); Wire.write(Power_Register); // Power_CTL Register // Enable measurement Wire.write(8); // Bit D3 High for measuring enable (0000 1000) Wire.endTransmission(); } void loop() { // X-axis Wire.beginTransmission(ADXAddress); // Begin transmission to the Sensor //Ask the particular registers for data Wire.write(X_Axis_Register_DATAX0); Wire.write(X_Axis_Register_DATAX1); Wire.endTransmission(); // Ends the transmission and transmits the data from the two registers Wire.requestFrom(ADXAddress,2); // Request the transmitted two bytes from the two registers if(Wire.available()<=2) { // X0 = Wire.read(); // Reads the data from the register X1 = Wire.read(); /* Converting the raw data of the X-Axis into X-Axis Acceleration - The output data is Two's complement - X0 as the least significant byte - X1 as the most significant byte */ X1=X1<<8; X_out =X0+X1; Xa=X_out/256.0; // Xa = output value from -1 to +1, Gravity acceleration acting on the X-Axis } // Y-Axis Wire.beginTransmission(ADXAddress); Wire.write(Y_Axis_Register_DATAY0); Wire.write(Y_Axis_Register_DATAY1); Wire.endTransmission(); Wire.requestFrom(ADXAddress,2); if(Wire.available()<=2) { Y0 = Wire.read(); Y1 = Wire.read(); Y1=Y1<<8; Y_out =Y0+Y1; Ya=Y_out/256.0; } // Z-Axis Wire.beginTransmission(ADXAddress); Wire.write(Z_Axis_Register_DATAZ0); Wire.write(Z_Axis_Register_DATAZ1); Wire.endTransmission(); Wire.requestFrom(ADXAddress,2); if(Wire.available()<=2) { Z0 = Wire.read(); Z1 = Wire.read(); Z1=Z1<<8; Z_out =Z0+Z1; Za=Z_out/256.0; } // Prints the data on the Serial Monitor Serial.print("Xa= "); Serial.print(Xa); Serial.print(" Ya= "); Serial.print(Ya); Serial.print(" Za= "); Serial.println(Za); }

Arduino陀螺仪代码

为了从陀螺仪中获取数据,我们将使用与前一个相似的代码。因此,首先我们必须为数据定义寄存器地址和一些变量。在setup部分中,我们必须唤醒并使用CTRL_REG1将传感器置于正常模式,并选择传感器的灵敏度。在这个例子中,我将选择2000dps的灵敏度模式。

在loop部分类似于加速计,我们将读取X、Y和Z轴的数据。然后,原始数据必须转换成角度值。从传感器的数据表中我们可以看到,对于2000dps的灵敏度模式对应一个70 mdps/数字单元。这意味着我们必须将原始输出数据乘以0.07才能得到角速率(以度/秒为单位)。如果角速率乘以时间就会得到角的值。因此,我们需要计算每个循环段的时间间隔,我们可以通过在循环段的顶部和底部使用millis()函数来实现这一点,我们将其值存储到这个dt变量中。因此,对于每次执行的循环,我们将计算角度,并将其与最终的角度值相加。我们将对另外两个轴进行同样的操作,最后我们将在串行监视器中打印结果。

#include  // ---陀螺仪寄存器地址#define gyro_gx0 0x28 #define gyro_gx1 0x29 #define gyro_gy0 0x2a #define gyro_gy1 0x2b #define gyro_gz0 0x2c #define gyro_gz1 0x2d int gyro = 0x69;//设备地址,其中还包括用于选择模式的第8位,在这种情况下读取。int gx0,gx1,gx_out;int gy0,gy1,gy_out;int gz0,gz1,gz_out;Float XG,YG,ZG;浮动角度,角度尖锐,角度Z,Anglexc,Anstyc,Anstzc;毫无符号长开始,完成,经过;float dt = 0.015;void setup(){wire.begin(); Serial.begin(9600); delay(100); Wire.beginTransmission(Gyro); Wire.write(0x20); // CTRL_REG1 - Power Mode Wire.write(15); // Normal mode: 15d - 00001111b Wire.endTransmission(); Wire.beginTransmission(Gyro); Wire.write(0x23); // CTRL_REG4 - Sensitivity, Scale Selection Wire.write(48); // 2000dps: 48d - 00110000b Wire.endTransmission(); } void loop() { start=millis(); //---- X-Axis Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gX0); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gX0 = Wire.read(); } Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gX1); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gX1 = Wire.read(); } //---- Y-Axis Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gY0); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gY0 = Wire.read(); } Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gY1); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gY1 = Wire.read(); } //---- Z-Axis Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gZ0); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gZ0 = Wire.read(); } Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gZ1); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gZ1 = Wire.read(); } //---------- X - Axis // Raw Data gX1=gX1<<8; gX_out =gX0+gX1; // From the datasheet: 70 mdps/digit Xg=gX_out*0.07; // Angular rate // Angular_rate * dt = angle angleXc = Xg*dt; angleX = angleX + angleXc; //---------- Y - Axis gY1=gY1<<8; gY_out =gY0+gY1; Yg=gY_out*0.07; angleYc = Yg*dt; angleY = angleY + angleYc; //---------- Z - Axis gZ1=gZ1<<8; gZ_out =gZ0+gZ1; Zg=gZ_out*0.07; angleZc = Zg*dt; angleZ = angleZ + angleZc; // Prints the data on the Serial Monitor Serial.print("angleX= "); Serial.print(angleX); Serial.print(" angleY= "); Serial.print(angleY); Serial.print(" angleZ= "); Serial.println(angleZ); delay(10); // Calculating dt finished=millis(); elapsed=finished-start; dt=elapsed/1000.0; start = elapsed = 0; }

Arduino磁力计代码

同样,我们将使用与前一个类似的技术。首先,我们需要定义寄存器地址和它的设置部分设置传感器在连续测量模式。在loop部分,我们将使用与前面传感器相同的方法获取每个轴的原始数据。

然后我们需要将原始数据转换成磁场值或高斯单位。从传感器的数据表中我们可以看到,默认的灵敏度模式是0.92mG/digit。这意味着我们需要将原始数据乘以0。00092才能得到高斯单位的地球磁场。最后,我们将在串行监视器上打印这些值。

# include <线。h> //I2C Arduino库#define Magnetometer_mX0 0x03 #define Magnetometer_mX0 0x04 #define Magnetometer_mZ0 0x05 #define Magnetometer_mZ1 0x06 #define Magnetometer_mY0 0x07 #define Magnetometer_mY1 0x08 int mX0, mX1, mX_out;int mY0, mY1, mY_out;int mZ0, mZ1, mZ_out;Zm评选浮动Xm, Ym;//I2C的7位地址void setup(){//初始化串行和I2C通信串行.begin(9600);Wire.begin ();延迟(100);Wire.beginTransmission(磁强计);Wire.write (0 x02); // Select mode register Wire.write(0x00); // Continuous measurement mode Wire.endTransmission(); } void loop(){ //---- X-Axis Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mX1); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mX0 = Wire.read(); } Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mX0); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mX1 = Wire.read(); } //---- Y-Axis Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mY1); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mY0 = Wire.read(); } Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mY0); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mY1 = Wire.read(); } //---- Z-Axis Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mZ1); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mZ0 = Wire.read(); } Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mZ0); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mZ1 = Wire.read(); } //---- X-Axis mX1=mX1<<8; mX_out =mX0+mX1; // Raw data // From the datasheet: 0.92 mG/digit Xm = mX_out*0.00092; // Gauss unit //* Earth magnetic field ranges from 0.25 to 0.65 Gauss, so these are the values that we need to get approximately. //---- Y-Axis mY1=mY1<<8; mY_out =mY0+mY1; Ym = mY_out*0.00092; //---- Z-Axis mZ1=mZ1<<8; mZ_out =mZ0+mZ1; Zm = mZ_out*0.00092; //Print out values of each axis Serial.print("x: "); Serial.print(Xm); Serial.print(" y: "); Serial.print(Ym); Serial.print(" z: "); Serial.println(Zm); delay(50); }

以下是使用处理IDE制造的MEMS数字罗盘的传感器的酷炫应用。您可以在以下链接上找到更多详细信息和此示例的源代码:

Arduino-Compass

33的反应

  1. 汉斯

    嗨,
    很好的解释,做得很好。
    在移动MEMS加速度计陀螺仪磁仪时,我是否有一个关于处理草图的处理草图的链接?
    (@ 0:20在视频中,左图像,我找到了指南针的页面,但不是左侧的示例)
    汉斯

    回复
  2. Dmitrii

    嗨,德扬,
    非常感谢你的工作。这对我帮助很大。
    我使用只有MC5883L磁力计的GY 273断接器。有2个问题需要理解:
    1)即使我在所有方向上旋转传感器,关于Z轴的测量值约为零。原始数据较少。因此,当我将其乘以0.00092串行端口时,请显示ME值0.00或-0.00。
    但是关于X轴和Y轴的测量似乎是可以的。当这两个轴在水平面上时,我计算航向角,它的值看起来很可靠。如果我旋转90度,它会显示大约90度,以此类推。
    你知道为什么我的传感器不想测量z轴的数据吗?
    2)在磁强计datasheet(数据表)第2页有信息:
    8位读地址0x3D
    8位写入地址0x3C
    我试过了,但是不管用。I2C扫描器显示地址0x1E,所有的例子都使用这个地址。
    我没有正确理解数据表中关于设备地址的内容吗?
    谢谢!

    回复
    • Dejan Nedelkovski.

      0x1E是传感器地址,然后在第11页的每个轴上有单独的内部寄存器地址。你用过吗?
      你说这是一个奇怪的行为,因为x和y工作和z没有。最后有一个机会z轴根本不起作用(制造错误)。例如,我使用的传感器对于3轴具有比特不同的值(它们应该是相同的,我认为它是制造错误)。

      回复
      • Dmitrii

        谢谢您的回复!
        我认为这是它的制造错误和有序的新磁力计。
        至于0x1E,我发现了什么意思0x1c和0x1d在这种情况下,这是我的注意力:
        “这些指针位置从主母部发送到此
        从设备并成功7位地址(0x1e)加1位读/写标识符,即读取和0x3c for write ...“

  3. 穆拉特

    你好,
    你能解释一下,为什么我要把最有效位移到左边8位吗?你能用数字举例吗?你能更详细地解释一下移动msb吗?

    回复
  4. Zoltan.

    嗨,德扬!
    非常感谢,真的很有帮助。我正在做一个项目,我结合使用了陀螺仪和加速计。在你们的视频中有一个很酷的处理例子,有一个蓝色的盒子。你能把源代码发给我吗?这对我有很大的帮助!
    非常感谢你!

    回复
  5. 闭塞

    您好,我知道您是IMU或运动传感器领域的专家。如果可能的话,你能发给我一份IMU的框图吗? IMU可以测量运动中的身体动作。框图应包括连接和组件的细节,如过滤器(如果需要),微控制器和通信。帮助我如何根据运动的采样率选择传感器。我对这个科目很陌生,因为它看起来很有趣,所以我想学习一下。
    谢谢你!

    回复
      • 闭塞

        你好,Dejan,感谢您的及时回复。你能否给我一些基本的想法,关于如何为高性能运动设计一个IMU的原型。据我所知,我们需要IMU传感器(加速度计、陀螺仪、磁强计或GPS)微控制器和通信系统。只是不知道它将如何连接和如何定义采样率(可能我们需要一些公式来找到采样率),以选择传感器。请把你的想法告诉我,先生,谢谢

  6. 你好,Dejan,我不明白你为什么选择变量dt是0.015。请教导我。

    回复
    • Dejan Nedelkovski.

      //计算DT.
      完成=米尔斯();
      经过=完成 - 开始;
      dt =运行/ 1000.0;
      开始=经过= 0;

      //

      dt = 0。015只是起始值,除了0你可以取任何值。但每次迭代计算dt变量,其值约为0.015(每次迭代执行所需时间)

      回复
  7. 尼克

    嗨,
    你能解释一下如何结合陀螺仪和加速仪来得到准确的数值吗?
    我太愚蠢了,无法弄明白; _;

    回复
  8. ogar

    你好,谢谢你的教程,对我很有帮助。请帮助我用matlab代码读取这些加速度计的值。您给出的例子工作得很好,我能够在串行监视器上查看结果。我需要将数据导出到matlab中。谢谢

    回复
  9. 亚历山大·皮雷马丁斯

    你好德州,

    我的名字是亚历山大,我正在为我的航空航天工程毕业设计开发四轴飞行器。我刚刚看了你的教程,想问你在使用陀螺仪模块L3G4200D的过程中是否有漂移测量问题。

    我问,因为我也有同样的模块(GY 80),相同的陀螺和加速度计,我已经实现了互补滤波器得到正确的态度测量,甚至以为我还在吃陀螺漂移的问题随着时间的推移,我不知道我还需要实现另一种调整,或者它可能是一个硬件问题。

    如果你可以用这个陀螺模块与我分享你的经验,我会非常高兴,也许如果你可以和我谈谈一段时间。

    这是我的skpe号码,供您参考:ale_martins88

    最好的问候,

    亚历山大

    回复
    • Dejan Nedelkovski.

      嗨,您好,
      这不是硬件问题,陀螺仪会漂移。是的,你必须找到一种方法将它与加速度计或/和磁力计结合起来,以纠正漂移。

      回复
    • sania

      嗨,我正在研究MPU6050模块,以通过双重集成来检测Z轴上的振动并从加速度计数据中找到位置。您告诉您已实现互补过滤器以获得准确的值。所以请您协助我或为我提供源代码。我会非常感谢你。

      回复
  10. 美好的

    你好,
    感谢您对模块和代码的清晰描述。
    我完全复制了加速器的代码,但是它给了我X和Y在0和-2之间的值,而不是1和-1。Z几乎没有作用。
    我还打印了X0和X1。X0在0和2xx之间,X1是-512或-256。
    对于Y也是一样的。这是正确的还是我的GY-80坏了,因为Z不工作在你的演示。
    提前致谢,关于GOOS

    回复
    • Dejan Nedelkovski.

      嗨,您好,
      尝试在一个单独的“if(Wire.available()<=1)”中读取X0和X1值,就像陀螺仪的代码一样。那么陀螺仪和磁强计的值是正确的吗?

      回复
  11. Yasar

    你好德扬!
    你真的帮了我大忙。但有一个问题我做不到
    你给陀螺仪写了0。07。我用itg3200。在哪里可以找到itg3200的0.07值。

    回复
  12. 肖恩

    我非常感谢您的帮助,了解这个模块如何工作,但我认为当您靠近北方时,您的低通滤波器存在问题。让我们说它在一个大约2度的过程中,下一个读数是355度,低通滤波器将占据最新方向的85%,并增加最新标题的15%(355 * 15/200)约53并声明平滑标题现在为54.7。这与真正的标题非常不同,可能导致一些严重的导航问题。

    回复
  13. 安德烈

    很棒的文章谢谢你分享这个!我打算做一个项目来捕捉手机位置并读出数据。问题是我需要在运动中需要,所以我以为我在手上固定了传感器。在这种情况下,我需要一个带电池的Arduino,因为我将在不同的位置和高度上移动。您是否认为这可能使用您在本文中介绍的这样的设备?最好!

    回复
  14. 嗨!
    找到您的视频,因为我正在研究如何实施加速度计和陀螺仪,以获得详细的阅读并用它制作AWD控制器。
    但是,有一个问题困扰着我:
    如果你将这个模块设置在一个平面(例如水位)的表面,关闭传感器,将传感器移动到一个,例如45°的表面,然后重新通电。
    如果我理解正确的“工作原理”解释,则使用过的传感器应该能够识别此更改(由于内部结构始终“移动”)并显示重新为其供电后显示正确的值。
    我的假设正确吗?
    问的原因是,正如上面提到的,我将会实现这个传感器在汽车,它通常需要“聪明”你不能总是把它放到一个完全平坦的表面,如果传感器是用来控制的东西,它总是需要知道汽车的绝对位置,不能重置自己“0”每次是动力。

    我希望这个问题多少是可以理解的。

    谢谢!

    BR

    回复
    • Dejan Nedelkovski.

      嘿,好的,加速度计感应引力,所以随时你为设备供电,它会给你正确的结果。
      另一方面,陀螺仪感知角运动/速率,这是内部发生的,只取决于传感器本身。这意味着它不会给你正确的结果时,通电,而被定位在45度。但它们仍然可以结合在一起工作,并给你正确的编程结果。

      回复

留下一个回复

您的电子邮件地址不会被公开。

推荐

2019年最佳入门级示波器为初学者和爱好者

最好的示波器为初学者和爱好者

推荐

8个最好的Arduino入门工具包,适合2019年初学者

初学者的8个最佳Arduino Starter Kits

推荐

最好的3D打印机为初学者和爱好者- 3D打印

最好的3D打印机为初学者和业余爱好者