ESP8266/ESP32无人机系列(一)之MPU6050 您所在的位置:网站首页 如何提高浏览器运行速度快一些 ESP8266/ESP32无人机系列(一)之MPU6050

ESP8266/ESP32无人机系列(一)之MPU6050

2023-07-08 15:54| 来源: 网络整理| 查看: 265

ESP8266/ESP32无人机系列之MPU6050 MPU6050

在这里插入图片描述

MPU6050是一种非常流行的空间运动传感器,可以获取器件当前的三个加速度分量和三个旋转角速度。但是MPU6050的数据是有较大的噪声的,需要进行滤波,而MPU6050芯片内置了一个模块DMP(数字运动处理器),该模块可以对数据进行滤波再输出。故获取mpu6050数据(姿态角yaw pitch roll )有两种方法: (1)、通过原始数据的采集进行滤波算法(卡尔曼滤波)做数据融合得到较为稳定的三轴角度 (2)、通过mpu6050自带的DMP直接输出数据

1. 引脚说明*

**在这里插入图片描述 一般我们都是将MPU6050作从机方式,故我们需要用的引脚只有6个(VCC、GND、SCL、SDA、AD0、INT)。特别说明一下AD0与INT: AD0:决定了IIC地址的最低一位,用来区别多个从机同时使用,当AD0为低电平时/悬空时,从机的地址为0X68,当为高电平时,地址为0X69; INT:MPU6050具有一个可编程的中断系统,在INT脚上产生中断信号,不同的事件(包括DMP中断)产生不同的中断信号,状态可从寄存器中)读取。故使用DMP方式读取数据时,要使用该引脚,中断时,引脚变高。

2、读取数据

首先说一下与MPU相关或常听说的一些东西

1. 姿态角 yaw pitch roll 首先,什么是姿态角。为什么要将获取的数据加速度数据转换为姿态角数据输出。因为一般情况下,直接的加速度数据对于我们来说不是特定需求一般没什么用,我们需要的是描述物体的运动状态的数据,而姿态角就可以很好的描述一个物体空间中的运动状态,所以我们需要将加速度转换为姿态角数据输出。姿态角表示如下图:在这里插入图片描述

该模型是飞行器当前姿态的一个通用模型,Roll(横滚角)表示绕X轴旋转,Pitch(俯仰角)表示绕Y轴旋转,Yaw(偏航角)表示绕Z轴旋转。所以姿态角描述了物体在三维的运动状态。 这里有个大佬写了加速度如何转换成欧拉角,可以看看:https://zhuanlan.zhihu.com/p/20082486

2. 四元数 相信大家都听说过这个名字,而且大部分人也是看了一头雾水。个人理解:其实四元数就是4个数(x,y,z,w),它也可以表示物体在空间中的运动状态,存储了旋转轴和偏转角的信息,它能方便的描述刚体绕任意轴的旋转。最重要的是它与欧拉角的关系,它们之间存在公式的相互转换。这就是我们会用到四元数的原因,但MPU6050的DMP可以直接输出四元数的值以及欧拉角。

详情请看这个大佬写的:无人机姿态解算

3. 卡尔曼滤波 直接用原始数据加速度计算出来的姿态角是不能用的,因为实际上,mpu6050输出的加速度值易受外界干扰,比如运动中的加速度和飞机在振荡产生的加速度,噪声较大,所以我们是无法直接的运用加速度计算出来的姿态角,需要我们对数据进行一定的处理(滤波)才能使用。以下是一个大佬写的一些介绍:(出处忘记哪了,请原谅) 卡尔曼滤波算法是卡尔曼等 人在20世纪60年代提出的一种递推滤波算法。它的实质是以最小均方误差为估计的最佳准则,来寻求一套递推估计的算法。这套算法采用信号与噪声的状态空间模型,利用前一时刻地估计值和现时刻的观测值来更新对状态变量的估计,求出现时刻的估计值,在惯性导航系统中有非常广泛的应用。刚才说的噪声指的是计算得出的值与实际值的误差。 那么为什么Kalman滤波会应用到惯性导航系统中呢?这主要是因为惯性导航系统的“纯惯性”传感器不足以达到所需的导航精度,为了补偿导航系统的不足,常常使用其他导航设备来提高导航精度,以减小导航误差。所以利用Kalman滤波算法,可以将来自惯性导航系统与其他导航装置的数据(如惯性导航系统计算的位置对照GPS接收机给出的位置信息)加以混合利用,估计和校正未知的惯性导航系统误差。卡尔曼滤波算法广泛应用已经超过30年,包括机器人导航,控制, 传感器数据融合甚至军事方面的雷达系统以及导弹追踪等等。比如,在雷达中,人们感兴趣的是跟踪目标,但目标的位置、速度、加速度的测量值往往在任何时候都有噪声。卡尔曼滤波利用目标的动态信息,设法去掉噪声的影响,得到一个关于目标位置最优的估计。这个估计可以是对当前目标位置的估计(滤波),也可以是对于将来位置的估计(预测),也可以是对过去位置的估计(插值或平滑)。

在这里插入图片描述 卡尔曼滤波算法是一个非常复杂的计算,我们结合飞行器来简单的讲一下它的计算过程,比如飞行器想知道自己的一个状态,这个状态可以是姿态、速度或位置等信息,我们知道飞行器的传感器是可以得到这些信息的,通过惯性导航的数学模型也可以计算出这些信息,但这两个信息的值与实际值还是有一定的差距的,把这两个值放在若干数学公式里可以得到一个最优值,通过这个最优值与传感器和数学模型的值进行对比,我们可以知道哪个值与最优值比较接近,下次的计算我们应该较多的参考接近最优值的那个值,比如传感器的值最接近最优值,那我们就把传感器的值使用较大的权重,数学模型得到的值加使用较小的权重,所得到的这个权重不是随便给的,也是通过数学公式得也来的。那我下一个时间段再计算的时候这个权重就要起作用了,传感器的值和数学模型的值会带着权重放在数学公式里得到最优值,然后我们再把这个最优值与传感器和数学模型的值进行对比,再看一下哪个值与最优值接近,如果还是传感器的值比较接近,我们还是会通过公式生成一个权重,交给下个时间段的计算使用。通过不断重复这样的计算,我们就可以得到一个相对较优的值,这就是卡尔曼滤波算法的大概过程。这还有个不错的链接,可以看看:https://www.zhihu.com/question/23971601

4.DMP输出

为了DIY简单,我们可以直接利用MPU6050的DMP功能直接输出我需要用的欧拉角。以下是代码:

/* VCC VU (5V/3.3V ) GND G Ground SCL D1 (GPIO05) I2C clock SDA D2 (GPIO04) I2C data XDA not connected XCL not connected AD0 not connected INT D8 (GPIO15) Interrupt pin */ #include #include "I2Cdev.h" #include "MPU6050_6Axis_MotionApps20.h" #include //时间回调函数 //时间回调定义 Ticker ticker1; //声明Ticker对象 //默认I2C地址是0x68 //特定的I2C地址可以作为参数在这里传递 // AD0 low = 0x68 (默认值) // AD0 high = 0x69 MPU6050 mpu; //MPU6050 mpu(0x69); // 若AD0为1,则替换掉上面那条语句 bool dmpReady = false; // 如果DMP init成功,则设置为true uint8_t mpuIntStatus; // 保存 MPU6050 中断状态 uint8_t devStatus; // 每个设备操作后返回状态(0 =成功,!0 =错误) uint16_t packetSize; // 预期的DMP包大小(默认为42字节) uint16_t fifoCount; // 当前FIFO中所有字节的计数 uint8_t fifoBuffer[64]; // FIFO存储缓冲区 Quaternion q; // [w, x, y, z] quaternion container VectorInt16 aa; // [x, y, z] accel sensor measurements VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements VectorFloat gravity; // [x, y, z] gravity vector float ypr[3]; // [yaw, pitch, roll] 偏航/俯仰/滚动容器和重力矢量 int16_t Gx, Gy, Gz;// #define INTERRUPT_PIN 15 // use pin 15 on ESP8266 volatile bool mpuInterrupt = false; //指示中断引脚标志位 //中断回调函数 ICACHE_RAM_ATTR void dmpDataReady() { mpuInterrupt = true;//表示中断触发 } void setup(void) { Serial.begin(115200); Wire.begin(); Wire.setClock(400000); // 400千赫I2C时钟。如果有编译困难,请注释这一行 //初始化设备 Serial.println("Init device ..."); mpu.initialize(); pinMode(INTERRUPT_PIN, INPUT);//中断脚定义输入 // 验证连接 Serial.println("Testing device connections..."); if(mpu.testConnection()) Serial.println("MPU6050 connection successful"); else Serial.println("MPU6050 connection failed"); // 配置DMP Serial.println("Init DMP..."); devStatus = mpu.dmpInitialize(); // 如果工作正常返回0 if (devStatus == 0) { //使能DMP Serial.println("DMP is OK"); mpu.setDMPEnabled(true); // 配置中断检测 Serial.println("INIT Interrupt"); //中断则进入中断函数dmpDataReady attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);//从低到高则触发模式 mpuIntStatus = mpu.getIntStatus();//获取中断状态 //设置我们的DMP就绪标志 dmpReady = true; // 获取预期的DMP包大小,以便以后进行比较 packetSize = mpu.dmpGetFIFOPacketSize(); } //错误则进入,打印错误原因 else { /* * code=1 初始内存加载失败 * code=2 DMP配置更新失败 */ printf("DMP Initialization failed (code:%d )",devStatus); } ticker1.detach(); //停止ticker1 //循环询问功能 ticker1.attach(1, callback1);//循环 } void mpu_data_get() { //如果dmp没有初始化好,则直接退出 if (!dmpReady) return; //等待MPU中断或额外的包可用 if (!mpuInterrupt && fifoCount


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有