MEMS加速度计陀螺磁力计&Arduino

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

概述


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

MEMS加速度计


它通过测量电容变化来测量加速度。它的微结构看起来像这样。它具有附接到弹簧的质量,该弹簧被限制在一个方向上移动和固定的外板。因此,当施加特定方向上的加速度时,质量将移动并且板之间的电容和质量将改变。将测量电容的这种电容变化,处理,并且它将对应于特定的加速度值。

MEMS-Accelerometer-How-It-Works

MEMS陀螺仪


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

陀螺仪 - 如何 - 工作

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

陀螺微观结构

MEMS磁力计


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

霍尔效应-01

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

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

Arduino和MEMs传感器


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

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

yaboAG娱乐城披露:这些是联盟链接。作为亚马逊助理,我从合格购买中获得。

GY-80-LOAD-AND-ARDUINO

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

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

源代码


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

Arduino加速度计代码

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

在循环部分,我们需要读取每个轴的数据。我们将从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陀螺仪代码

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

在循环部分类似于加速度计,我们将读取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磁力计代码

我们再次将类似的技术与前一个类似的技术一起使用。首先,我们需要定义寄存器地址,并将其设置为连续测量模式的传感器。在循环部分中,我们将获得具有与前一个传感器相同的方法的每个轴的原始数据。

然后我们需要将原始数据转换为磁场值或高斯单元。从传感器的数据表中,我们可以看到默认灵敏度模式为0.92mg /数字。这意味着我们需要将原始数据乘以0.00092,以便在高斯单元中获取地球磁场。最后,我们将在串行监视器上打印值。

#include  // i2c arduino library #define magnetometer_mx0 0x03 #define magnetometer_mx1 0x04 #define magnetometer_mz0 0x05 #define magnetomet_mz1 0x06 #define magnetomet_my0 0x07 #define magnetomet_my1 0x08 int mx0,mx1,mx_out;int my0,my1,my_out;int mz0,mz1,mz_out;Float XM,YM,ZM;#define磁力计0x1e // i2c 7bit hmc5883 void setup(){//初始化串口和i2c通信串行.begin(9600);Wire.begin();延迟(100);Wire.Begintroansmission(磁力计);Wire.write(0x02);//选择模式寄存器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

    嗨,德扬,
    非常感谢您的工作。这对我帮助很大。
    我使用GY 273断路器,它只有磁强计MC5883L。有两个问题需要理解:
    1)即使我在所有方向上旋转传感器,关于Z轴的测量值约为零。原始数据较少。因此,当我将其乘以0.00092串行端口时,请显示ME值0.00或-0.00。
    但关于x和y轴的测量似乎可以。当这两个轴铺设在水平面上时,我计算标题角度时,它的值看起来非常可靠。如果我旋转90度,它会显示大约90个退化等。
    你有什么想法为什么我的传感器不想要测量关于z轴的数据?
    2)在第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位?你能用Numbers.Can举例说明你解释MSB更详细吗?

    回复
  4. Zoltan.

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

    回复
  5. 闭塞

    你好,我知道你是IMU或运动传感器领域的专家。如果可能的话,你能给我一个IMU的框图,可以测量运动中的身体运动。框图应该包括连接和组件的细节,如过滤器(如果需要),微控制器和通信。帮助我如何根据运动的采样率选择传感器。我是这门课的新手,我想了解一下,因为它看起来很有趣。
    谢谢你。

    回复
      • 闭塞

        嗨Dejan,谢谢你的快速回复。您可以为我提供基本想法,以如何为高性能运动设计原型IMU。据我所知,我们需要IMU传感器(加速度计,陀螺仪,磁力计或GPS)微控制器和通信系统。只是不知道如何连接它以及如何定义采样率(可能是我们需要一些公式来查找采样率),以便选择传感器。只是通过你的主席先生引导我。谢谢

    • Dejan Nedelkovski.

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

      //

      Dt = 0.015只是初始值,除了0,其他都可以。然而dt变量在每次迭代中都被计算出来,它的值大约是0.015(每次迭代执行所需的时间)

      回复
  6. 尼克

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

    回复
  7. ogar.

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

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

    你好德州,

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

    我问它是因为我有相同的模块(GY 80),在这里有相同的陀螺和加速度计,我已经实现了互补滤波器来获得正确的姿态测量,即使我仍然有陀螺漂移的问题,随着时间的推移,我不知道我是否还需要执行另一种调整,或者这可能是硬件问题。

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

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

    最好的问候,

    亚历山大

    回复
    • Dejan Nedelkovski.

      你好呀,
      嗯,这不是硬件问题,陀螺仪趋于漂移。所以是的,您必须找到与加速度计或/和磁力计结合它的方法,以纠正漂移。

      回复
    • sania

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

      回复
  9. Goos.

    你好,
    感谢您对模块和代码的清晰描述。
    我完全复制了加速器代码的代码,但它给了我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值,如在陀螺仪的代码中。陀螺仪和磁力计你得到正确的值吗?

      回复
  10. 亚马尔

    你好德扬!
    你真的帮了我大忙。但有个问题我做不到
    您使用ITG3200为Gyro.i的编写了0.07。我在哪里可以找到ITG3200的0.07值。

    回复
  11. 肖恩

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

    回复
  12. 安德烈

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

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

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

    谢谢!

    布尔

    回复
    • Dejan Nedelkovski.

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

      回复

留下一个回复

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

受到推崇的

2019年初学者和爱好者的最佳进入级示波器

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

受到推崇的

2019年最佳Arduino入门套件

初学者的8个最佳Arduino Starter Kits

受到推崇的

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

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