当谈到嵌入式电子设备和机器人的姿态控制和运动检测时,MPU6050往往是一个备受关注的传感器模块。它是一款小巧但功能强大的六轴传感器,集成了三轴加速度计和三轴陀螺仪。在本博客中,我们将详细介绍MPU6050的特点、工作原理以及与stm32配合的使用方法,后面看情况更新卡尔曼滤波。
目录
1. MPU6050的特点和功能
1.1 六轴传感器
1.2 数字运动处理器
1.3 I2C通信接口
1.4 高精度和低功耗
2. MPU6050的工作原理
2.1 加速度计原理
2.2 陀螺仪原理
2.3 姿态解算
3.模块电路图
4. 如何使用MPU6050
4.1 硬件连接
4.2 初始化设置
4.3 读取传感器
4.4 数据处理和滤波
5. 卡尔曼滤波
MPU6050集成了三轴加速度传感器和三轴陀螺仪,六轴传感器的主要优势在于结合了加速度计和陀螺仪的测量结果,可以提供更全面和准确的物体运动和姿态信息。通过综合加速度计和陀螺仪的数据,六轴传感器可以估计物体的姿态(如欧拉角或四元数),以及物体在三个空间方向上的加速度和角速度。
MPU6050内置的数字运动处理器可以进行复杂的运动处理和数据处理,包括传感器数据的滤波、噪声消除、运动融合算法等。它通过处理器内部的算法,提供了方便快捷的姿态解算。
MPU6050传感器通信协议可以选择使用I2C或SPI。 I2C是最常用的通信协议,适用于大多数情况下。SPI是一种高速串行通信协议,更加适用于性能要求较高的应用场景。
支持的I2C通信模式中,包括两个信号线SCL(串行时钟线)和SDA(串行数据线)。通过I2C通信,可以与MPU6050进行数据的读取和写入,另一篇博客有讲到。I2C(IIC)通信协议详解与应用
如果选择使用SPI通信协议,可以参考MPU6050的数据手册以及您所使用的STM32的文档,配置和连接SPI总线,设置通信参数,并使用SPI协议与MPU6050进行数据交换,我是很少使用SPI通信的,详细的通信协议另一篇博客也有讲到,想了解的也可以去看一下。了解SPI通信:串行外设接口的基本工作原理,51、stm32实现SPI
为了实现MPU6050的高精度,我们可以进行传感器校准、数据滤波、温度补偿、数据处理和算法优化,以及系统调优等步骤。通过这些措施,可以提高MPU6050的测量精度和稳定性,确保获得准确可靠的运动测量和姿态估计结果,同时通过智能功耗控制来延长电池寿命。
加速度计测量物体的加速度,其工作基于微小的质量和弹性部件组成的微型机械结构。加速度使得这些部件发生变形,并通过电容或压阻传感器转换成电信号。MPU6050的加速度计可以测量在三个轴上的线性加速度。
陀螺仪测量物体的角速度,其工作基于角动量守恒定律。陀螺仪由旋转部件和感测部件组成,当物体发生旋转时,感测部件会受到力矩的作用而产生电信号。MPU6050的陀螺仪可以测量在三个轴上的旋转速率。
姿态解算是将加速度计和陀螺仪的数据进行融合,从而获得物体的姿态信息(例如欧拉角)。通过运动融合算法,可以综合利用两类传感器的优势,同时弥补它们的局限性。常用的算法包括卡尔曼滤波、互补滤波和四元数等,卡尔曼滤波后面会涉及。
GY-521模块上各个引脚的作用
VCC | 供电引脚,接3V-5V电源,一般使用5V |
GND | 接地引脚,连接到电源的地 |
SCL | I2C时钟线引脚,用于与主控器件进行时钟同步 |
SDA | I2C数据线引脚,用于与主控器件进行数据通信 |
XDA | 可用于将其他I2C模块与MPU6050连接 |
XCL | 可用于将其他I2C模块与MPU6050连接 |
AD0 | I2C地址选择引脚,可通过将其连接到VCC或GND,来选择MPU-6050的I2C地址 |
INT | 中断引脚,可用于连接到微控制器的中断引脚,用于传输中断信号 |
在开始使用MPU6050之前,需要进行初始化设置。通过I2C通信接口,将适当的值写入MPU6050的配置寄存器中,以配置采样率、传感器范围和滤波器等参数。您可以参考MPU6050的数据手册或使用相关的开发库来进行设置。
这里初始化的I2C通信引脚是PB7和PB6,时钟使用的是I2C1。
#include "stm32fxxx.h"
#include "stm32fxxx_i2c.h"
#define MPU6050_ADDRESS 0xD0 // MPU-6050 I2C地址
#define I2Cx I2C1 // 使用的I2C外设
void MPU6050_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
I2C_InitTypeDef I2C_InitStruct;
// 使能I2C时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2Cx, ENABLE);
// I2C引脚配置
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2Cx);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2Cx);
// I2C配置
I2C_StructInit(&I2C_InitStruct);
I2C_InitStruct.I2C_Timing = 0x40912732;
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStruct.I2C_OwnAddress1 = 0;
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_Init(I2Cx, &I2C_InitStruct);
I2C_Cmd(I2Cx, ENABLE);
// 初始化MPU-6050
I2C_GenerateSTART(I2Cx, ENABLE);
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2Cx, MPU6050_ADDRESS, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2Cx, 0x19); // 设置采样率和低通滤波器
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2Cx, 0x07); // 配置值
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2Cx, 0x1A); // 设置低通滤波器频率
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2Cx, 0x03); // 配置值
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2Cx, 0x1B); // 设置陀螺仪量程
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2Cx, 0x18); // 配置值(+/-2000°/s)
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2Cx, 0x1C); // 设置加速度计量程
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2Cx, 0x18); // 配置值(+/-16g)
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTOP(I2Cx, ENABLE);
}
/*
1. RCC_APB1PeriphClockCmd:使能I2C外设的时钟。
2. GPIO_InitTypeDef:配置I2C引脚的结构体。
3. GPIO_Init:初始化GPIO引脚。
4. GPIO_PinAFConfig:配置引脚的复用功能。
5. I2C_StructInit:将I2C_InitStruct结构体成员初始化为默认值。
6. I2C_Init:初始化I2C外设。
7. I2C_Cmd:使能I2C外设。
8. I2C_GenerateSTART:产生I2C起始信号。
9. I2C_CheckEvent:检查指定的I2C事件是否发生。
10. I2C_Send7bitAddress:发送7位设备地址给I2C外设。
11. I2C_SendData:发送数据给I2C外设。
12. I2C_GenerateSTOP:产生I2C停止信号。
在`MPU6050_Init`函数中,首先通过`RCC_APB1PeriphClockCmd`函数使能I2C外设的时钟。
然后,通过`GPIO_InitStruct`结构体和`GPIO_Init`函数配置I2C引脚,同时使用`GPIO_PinAFConfig`函数配置引脚的复用功能。
接下来,通过`I2C_InitStruct`结构体设置I2C相关参数,包括I2C时钟频率、模式、占空比等,并使用`I2C_Init`函数进行初始化。
通过`I2C_Cmd`函数使能I2C外设。
接下来,调用`I2C_GenerateSTART`函数产生I2C起始信号,并通过循环等待确认I2C通信是否就绪,然后发送设备地址。
然后,通过`I2C_SendData`函数依次发送要写入的寄存器地址和配置值,实现对MPU-6050的初始化设置。具体的寄存器地址和配置值请根据MPU-6050的数据手册进行配置。
最后,通过`I2C_GenerateSTOP`函数产生I2C停止信号,结束I2C通信。
*/
一旦完成初始化设置,就可以开始读取传感器数据了。通过I2C接口从MPU6050读取加速度计和陀螺仪的原始数据。这些数据以原始值的形式提供,通常是数字表示,并需要进行一些处理才能得到有用的信息。
void MPU6050_ReadAccelerometer(int16_t* acc_x, int16_t* acc_y, int16_t* acc_z)
{
uint8_t buffer[6];
// 发送起始信号开始读取加速度计数据
I2C_GenerateSTART(I2Cx, ENABLE);
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2Cx, MPU6050_ADDRESS, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2Cx, 0x3B); // 设置读取加速度计数据的寄存器地址
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
// 发送重复起始信号,开始接收数据
I2C_GenerateSTART(I2Cx, ENABLE);
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2Cx, MPU6050_ADDRESS, I2C_Direction_Receiver);
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
// 读取加速度计数据
for (int i = 0; i < 5; i++) // 读取6个字节,最后一个字节需要NACK
{
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED));
buffer[i] = I2C_ReceiveData(I2Cx);
}
I2C_AcknowledgeConfig(I2Cx, DISABLE); // 最后一个字节需要NACK
I2C_GenerateSTOP(I2Cx, ENABLE);
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED));
buffer[5] = I2C_ReceiveData(I2Cx);
// 将读取的数据转换为加速度计数据
*acc_x = (buffer[0] << 8) | buffer[1];
*acc_y = (buffer[2] << 8) | buffer[3];
*acc_z = (buffer[4] << 8) | buffer[5];
}
/*
在`MPU6050_ReadAccelerometer`函数中,我们首先定义了一个缓冲区`buffer`,用于存储读取的加速度计数据。
然后,通过发送起始信号开始读取加速度计数据。首先向MPU-6050发送写入加速度计数据的寄存器地址0x3B,然后通过发送重复起始信号,并切换为接收模式。
接下来,我们通过循环读取6个字节的加速度计数据。通过调用`I2C_CheckEvent`函数确认是否接收到了数据,并使用`I2C_ReceiveData`函数将数据存储到`buffer`数组中。
最后一个接收到的字节需要发送NACK,而不是ACK。通过调用`I2C_AcknowledgeConfig`函数禁用ACK,并通过`I2C_GenerateSTOP`发送停止信号。
最后,我们将读取到的加速度计数据从`buffer`数组中转换为`acc_x`、`acc_y`和`acc_z`变量。
*/
在获得原始数据后,您可以根据需求进行进一步的数据处理和滤波。例如,您可以使用滑动窗口平均或低通滤波器来减少噪声和不稳定性。此外,您还可以根据加速度计和陀螺仪数据,使用姿态解算算法计算物体的姿态,如俯仰角、横滚角和偏航角。
#include "stm32fxxx.h"
#include "stm32fxxx_i2c.h"
#include "kalman.h" // 引入卡尔曼滤波器库
#define MPU6050_ADDRESS 0xD0 // MPU-6050 I2C地址
#define I2Cx I2C1 // 使用的I2C外设
kalman_state acc_x_kf, acc_y_kf, acc_z_kf; // 卡尔曼滤波器状态变量
void MPU6050_ReadAccelerometer(int16_t* acc_x, int16_t* acc_y, int16_t* acc_z)
{
// 与前述示例代码相同的读取代码
// ...
}
void MPU6050_FilterAccelerometer()
{
int16_t acc_x_raw, acc_y_raw, acc_z_raw;
MPU6050_ReadAccelerometer(&acc_x_raw, &acc_y_raw, &acc_z_raw);
acc_x_kf = kalman_filter(&acc_x_kf, (float)acc_x_raw);
acc_y_kf = kalman_filter(&acc_y_kf, (float)acc_y_raw);
acc_z_kf = kalman_filter(&acc_z_kf, (float)acc_z_raw);
}
int main()
{
// 初始化I2C和MPU-6050
// ...
// 初始化卡尔曼滤波器
kalman_init(&acc_x_kf, 1, 1, 0.01); // 参数根据实际需要调整
kalman_init(&acc_y_kf, 1, 1, 0.01);
kalman_init(&acc_z_kf, 1, 1, 0.01);
while (1)
{
MPU6050_FilterAccelerometer();
// 使用过滤后的加速度计读数进行后续处理
// ...
}
}
/*
在上面的示例代码中,我们首先在全局定义了三个卡尔曼滤波器状态变量,分别对应X轴、Y轴和Z轴的加速度计数据。
然后,在`MPU6050_ReadAccelerometer`函数中,我们读取原始的加速度计数据。
接下来,在`MPU6050_FilterAccelerometer`函数中,我们将原始数据传入卡尔曼滤波器进行滤波计算。`kalman_filter`函数是卡尔曼滤波器库中实现的滤波函数,通过传入当前的滤波器状态和原始数据,返回经过滤波后的结果。
在主函数中,我们初始化了卡尔曼滤波器状态变量,然后在循环中不断调用`MPU6050_FilterAccelerometer`函数进行滤波,并使用滤波后的数据进行后续处理。
*/
深入了解卡尔曼滤波:最优状态估计的数学神器
MPU6050芯片手册、模块电路图、测试程序