赞
踩
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
Wire.beginTransmission(0x68); //开启MPU6050的传输
Wire.write(0x3B); //指定寄存器地址
Wire.requestFrom(0x68, 2, true); //将输据读出到缓存
Wire.endTransmission(true); //关闭传输模式
int val = Wire.read() << 8 | Wire.read(); //两个字节组成一个16位整数
1.3 具体实现
通常应当在setup函数中对Wire库进行初始化:
Wire.begin();
在对MPU6050进行各项操作前,必须启动该器件,向它的0x6B写入一个字节0即可启动。通常也是在setup函数完成,代码见1.1节。 二、 MPU6050的数据格式 我们感兴趣的数据位于0x3B到0x48这14个字节的寄存器中。这些数据会被动态更新,更新频率最高可达1000HZ。下面列出相关寄存器的地址,数据的名称。注意,每个数据都是2个字节。 * 0x3B,加速度计的X轴分量ACC\_X * 0x3D,加速度计的Y轴分量ACC\_Y * 0x3F,加速度计的Z轴分量ACC\_Z * 0x41,当前温度TEMP * 0x43,绕X轴旋转的角速度GYR\_X * 0x45,绕Y轴旋转的角速度GYR\_Y * 0x47,绕Z轴旋转的角速度GYR\_Z MPU6050芯片的座标系是这样定义的:令芯片表面朝向自己,将其表面文字转至正确角度,此时,以芯片内部中心为原点,水平向右的为X轴,竖直向上的为Y轴,指向自己的为Z轴。见下图:![](https://pic1.zhimg.com/a980247c82e3dba5ff39e3c6bd896ed4_b.jpg)我们只关心加速度计和角速度计数据的含义,下面分别介绍。 2.1 加速度计 加速度计的三轴分量ACC\_X、ACC\_Y和ACC\_Z均为16位有符号整数,分别表示器件在三个轴向上的加速度,取负值时加速度沿座标轴负向,取正值时沿正向。 三个加速度分量均以重力加速度g的倍数为单位,能够表示的加速度范围,即倍率可以统一设定,有4个可选倍率:2g、4g、8g、16g。以ACC\_X为例,若倍率设定为2g(默认),则意味着ACC\_X取最小值-32768时,当前加速度为沿X轴正方向2倍的重力加速度;若设定为4g,取-32768时表示沿X轴正方向4倍的重力加速度,以此类推。显然,倍率越低精度越好,倍率越高表示的范围越大,这要根据具体的应用来设定。 我们用f表示倍率,f=0为2g,f=3为16g,设定加速度倍率的代码如下:
Wire.beginTransmission(0x68); //开启MPU-6050的传输
Wire.write(0x1C); //加速度倍率寄存器的地址
Wire.requestFrom(0x68, 1, true); //先读出原配置
unsigned char acc_conf = Wire.read();
acc_conf = ((acc_conf & 0xE7) | (f << 3));
Wire.write(acc_conf);
Wire.endTransmission(true); //结束传输,true表示释放总线
再以ACC\_X为例,若当前设定的加速度倍率为4g,那么将ACC\_X读数换算为加速度的公式为: ![a_x=4g\times \text{ACC\_X}/32768](http://zhihu.com/equation?tex=a_x%3D4g%5Ctimes+%5Ctext%7BACC%5C_X%7D%2F32768) ,g可取当地重力加速度。 2.2 角速度计 绕X、Y和Z三个座标轴旋转的角速度分量GYR\_X、GYR\_Y和GYR\_Z均为16位有符号整数。从原点向旋转轴方向看去,取正值时为顺时针旋转,取负值时为逆时针旋转。 三个角速度分量均以“度/秒”为单位,能够表示的角速度范围,即倍率可统一设定,有4个可选倍率:250度/秒、500度/秒、1000度/秒、2000度/秒。以GYR\_X为例,若倍率设定为250度/秒,则意味着GYR取正最大值32768时,当前角速度为顺时针250度/秒;若设定为500度/秒,取32768时表示当前角速度为顺时针500度/秒。显然,倍率越低精度越好,倍率越高表示的范围越大。 我们用f表示倍率,f=0为250度/秒,f=3为2000度/秒,除角速度倍率寄存器的地址为0x1B之外,设定加速度倍率的代码与2.1节代码一致。 以GYR\_X为例,若当前设定的角速度倍率为1000度/秒,那么将GRY\_X读数换算为角速度(顺时针)的公式为:![g_x=1000\times \text{GYR\_X} / 32768](http://zhihu.com/equation?tex=g_x%3D1000%5Ctimes+%5Ctext%7BGYR%5C_X%7D+%2F+32768)。 三、运动数据 在读取加速度计和角速度计的数据并换算为物理值后,根据不同的应用,数据有不同的解译方式。本章将以飞行器运动模型为例,根据加速度和角速度来算出当前的飞行姿态。 3.1 加速度计模型 我们可以把加速度计想象为一个正立方体盒子里放着一个球,这个球被弹簧固定在立方体的中心。当盒子运动时,根据假想球的位置即可算出当前加速度的值。想象如果在太空中,盒子没有任何受力时,假想球将处于正中心的位置,三个轴的加速度均为0。见下图: ![](https://pic4.zhimg.com/4947f925172461770a3b067d2a66412b_b.jpg)如果我们给盒子施加一个水平向左的力,那么显然盒子就会有一个向左的加速度,此时盒内的假想球会因为惯性作用贴向盒内的右侧面。如下图所示: ![](https://pic1.zhimg.com/7a7c373a4500d3c6e4c2250bc203a2d0_b.jpg)为了保证数据的物理意义,MPU6050的加速度计是以假想球在三轴上座标值的相反数作为三个轴的加速度值。当假想球的位置偏向一个轴的正向时,该轴的加速度读数为负值,当假想球的位置偏向一个轴的负向时,该轴的加速度读数为正值。 根据以上分析,当我们把MPU6050芯片水平放于地方,芯片表面朝向天空,此时由于受到地球重力的作用, 假想球的位置偏向Z轴的负向,因此Z轴的加速度读数应为正,且在理想情况下应为g。注意,此加速度的物理意义并不是重力加速度,而是自身运动的加速度,可以这样理解:正因为其自身运动的加速度与重力加速度大小相等方向相反,芯片才能保持静止。 3.2 Roll-pitch-yaw模型与姿态计算 表示飞行器当前飞行姿态的一个通用模型就是建立下图所示坐标系,并用Roll表示绕X轴的旋转,Pitch表示绕Y轴的旋转,Yaw表示绕Z轴的旋转。 ![](https://pic4.zhimg.com/bb0b0678c9b8ebea10e41331e84d9533_b.jpg)由于MPU6050可以获取三个轴向上的加速度,而地球重力则是长期存在且永远竖直向下,因此我们可以根据重力加速度相对于芯片的指向为参考算得当前姿态。 为方便起见,我们让芯片正面朝下固定在上图飞机上,且座标系与飞机的坐标系完全重合,以三个轴向上的加速度为分量,可构成加速度向量![a(x,y,z)](http://zhihu.com/equation?tex=a%28x%2Cy%2Cz%29)。假设当前芯片处于匀速直线运动状态,那么![a](http://zhihu.com/equation?tex=a)应垂直于地面上向,即指向Z轴负方向,模长为![|a|=g=\sqrt{x^2+y^2+z^2}](http://zhihu.com/equation?tex=%7Ca%7C%3Dg%3D%5Csqrt%7Bx%5E2%2By%5E2%2Bz%5E2%7D)(与重力加速度大小相等,方向相反,见3.1节)。若芯片(座标系)发生旋转,由于加速度向量![a](http://zhihu.com/equation?tex=a)仍然竖直向上,所以Z轴负方向将不再与![a](http://zhihu.com/equation?tex=a)重合。见下图。 ![](https://pic2.zhimg.com/dd818cd0ef514049fc92614d2adac435_b.jpg)为了方便表示,上图坐标系的Z轴正方向(机腹以及芯片正面)向下,X轴正方向(飞机前进方向)向右。此时芯片的Roll角![\phi](http://zhihu.com/equation?tex=%5Cphi)(黄色)为加速度向量与其在XZ平面上投影![(x,0,z)](http://zhihu.com/equation?tex=%28x%2C0%2Cz%29)的夹角,Pitch角![\omega](http://zhihu.com/equation?tex=%5Comega)(绿色)与其在YZ平面上投影![(0,y,z)](http://zhihu.com/equation?tex=%280%2Cy%2Cz%29)的夹角。求两个向量的夹角可用点乘公式:![a \cdot b=|a|\cdot|b|\cdot\cos\theta](http://zhihu.com/equation?tex=a+%5Ccdot+b%3D%7Ca%7C%5Ccdot%7Cb%7C%5Ccdot%5Ccos%5Ctheta) ,简单推导可得: ![\phi=\cos^{-1}(\sqrt{x^2+z^2}/g)](http://zhihu.com/equation?tex=%5Cphi%3D%5Ccos%5E%7B-1%7D%28%5Csqrt%7Bx%5E2%2Bz%5E2%7D%2Fg%29),以及![\omega=\cos^{-1}(\sqrt{y^2+z^2}/g)](http://zhihu.com/equation?tex=%5Comega%3D%5Ccos%5E%7B-1%7D%28%5Csqrt%7By%5E2%2Bz%5E2%7D%2Fg%29) 注意,因为arccos函数只能返回正值角度,因此还需要根据不同情况来取角度的正负值。当y值为正时,Roll角要取负值,当x轴为负时,Pitch角要取负值。 3.4 Yaw角的问题 因为没有参考量,所以无法求出当前的Yaw角的绝对角度,只能得到Yaw的变化量,也就是角速度GYR\_Z。当然,我们可以通过对GYR\_Z积分的方法来推算当前Yaw角(以初始值为准),但由于测量精度的问题,推算值会发生漂移,一段时间后就完全失去意义了。然而在大多数应用中,比如无人机,只需要获得GRY\_Z就可以了。 如果必须要获得绝对的Yaw角,那么应当选用MPU9250这款九轴运动跟踪芯片,它可以提供额外的三轴罗盘数据,这样我们就可以根据地球磁场方向来计算Yaw角了,具体方法此处不再赘述。 四、数据处理与实现 MPU6050芯片提供的数据夹杂有较严重的噪音,在芯片处理静止状态时数据摆动都可能超过2%。除了噪音,各项数据还会有偏移的现象,也就是说数据并不是围绕静止工作点摆动,因此要先对数据偏移进行校准 ,再通过滤波算法消除噪音。 4.1 校准 校准是比较简单的工作,我们只需要找出摆动的数据围绕的中心点即可。我们以GRY\_X为例,在芯片处理静止状态时,这个读数理论上讲应当为0,但它往往会存在偏移量,比如我们以10ms的间隔读取了10个值如下:
-158.4, -172.9, -134.2, -155.1, -131.2, -146.8, -173.1, -188.6, -142.7, -179.5
这10个值的均值,也就是这个读数的偏移量为-158.25。在获取偏移量后,每次的读数都减去偏移量就可以得到校准后的读数了。当然这个偏移量只是估计值,比较准确的偏移量要对大量的数据进行统计才能获知,数据量越大越准,但统计的时间也就越慢。一般校准可以在每次启动系统时进行,那么你应当在准确度和启动时间之间做一个权衡。 三个角速度读数GYR\_X、GYR\_Y和GYR\_Z均可通过统计求平均的方法来获得,但三个加速度分量就不能这样简单的完成了,因为芯片静止时的加速度并不为0。 加速度值的偏移来自两个方面,一是由于芯片的测量精度,导至它测得的加速度向量并不垂直于大地;二是芯片在整个系统(如无人机)上安装的精度是有限的,系统与芯片的座标系很难达到完美重合。前者我们称为读数偏移,后者我们称为角度偏移。因为读数和角度之间是非线性关系,所以要想以高精度进行校准必须先单独校准读数偏移,再把芯片固定在系统中后校准角度偏移。然而,由于校准角度偏移需要专业设备,且对于一般应用来说,两步校准带来的精度提升并不大,因此通常只进行读数校准即可。下面介绍读数校准的方法。我们还3.2节的飞机为例,分以下几个步骤: 1. 首先要确定飞机的坐标系,对于多轴飞行器来说这非常重要。如果坐标系原点的位置或坐标轴的方向存在较大偏差,将会给后面的飞控造成不良影响。 2. 在确定了飞机的坐标系后,为了尽量避免读数偏移带来的影响,首先将MPU6050牢牢地固定在飞机上,并使二者座标系尽可能的重合。当然把Z轴反过来装也是可以的,就是需要重新推算一套角度换算公式。 3. 将飞机置于水平、坚固的平面上,并充分预热。对于多轴无人机而言,空中悬停时的XY平面应当平行于校准时的XY平面。此时,我们认为芯片的加速度方向应当与Z轴负方向重合,且加速度向量的模长为g,因此ACC\_X和ACC\_Y的理论值应为0,ACC\_Z的理论值应为-16384(假设我们设定2g的倍率,1g的加速度的读数应为最大值-32768的一半)。 4. 由于ACC\_X和ACC\_Y的理论值应为0,与角速度量的校准类似,这两个读数偏移量可用统计均值的方式校准。ACC\_Z则需要多一步处理,即在统计偏移量的过程中,每次读数都要加上16384,再进行统计均值校准。 4.2 卡尔曼滤波 对于夹杂了大量噪音的数据,卡尔曼滤波器的效果无疑是最好的。如果不想考虑算法细节,可以直接使用Arduino的Klaman Filter库完成。在我们的模型中,一个卡尔曼滤波器接受一个轴上的角度值、角速度值以及时间增量,估计出一个消除噪音的角度值。跟据当前的角度值和上一轮估计的角度值,以及这两轮估计的间隔时间,我们还可以反推出消除噪音的角速度。 实现代码见4.3节。下面介绍卡尔曼滤波算法细节,不感兴趣的可跳过。 (想看的人多了再写) 4.3 实现代码 以下代码在Arduino软件1.65版本中编译、烧写以及测试通过。
// 本代码版权归Devymex所有,以GNU GENERAL PUBLIC LICENSE V3.0发布
// http://www.gnu.org/licenses/gpl-3.0.en.html
// 相关文档参见作者于知乎专栏发表的原创文章:
// http://zhuanlan.zhihu.com/devymex/20082486
//连线方法
//MPU-UNO
//VCC-VCC
//GND-GND
//SCL-A5
//SDA-A4
//INT-2 (Optional)
#include <Kalman.h>
#include <Wire.h>
#include <Math.h>
float fRad2Deg = 57.295779513f; //将弧度转为角度的乘数
const int MPU = 0x68; //MPU-6050的I2C地址
const int nValCnt = 7; //一次读取寄存器的数量
const int nCalibTimes = 1000; //校准时读数的次数
int calibData[nValCnt]; //校准数据
unsigned long nLastTime = 0; //上一次读数的时间
float fLastRoll = 0.0f; //上一次滤波得到的Roll角
float fLastPitch = 0.0f; //上一次滤波得到的Pitch角
Kalman kalmanRoll; //Roll角滤波器
Kalman kalmanPitch; //Pitch角滤波器
void setup() {
Serial.begin(9600); //初始化串口,指定波特率
Wire.begin(); //初始化Wire库
WriteMPUReg(0x6B, 0); //启动MPU6050设备
Calibration(); //执行校准
nLastTime = micros(); //记录当前时间
}
void loop() {
int readouts[nValCnt];
ReadAccGyr(readouts); //读出测量值
float realVals[7];
Rectify(readouts, realVals); //根据校准的偏移量进行纠正
//计算加速度向量的模长,均以g为单位
float fNorm = sqrt(realVals[0] * realVals[0] + realVals[1] * realVals[1] + realVals[2] * realVals[2]);
float fRoll = GetRoll(realVals, fNorm); //计算Roll角
if (realVals[1] > 0) {
fRoll = -fRoll;
}
float fPitch = GetPitch(realVals, fNorm); //计算Pitch角
if (realVals[0] < 0) {
fPitch = -fPitch;
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
715889709579)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。