当谈到嵌入式电子设备和机器人的姿态控制和运动检测时,MPU6050往往是一个备受关注的传感器模块。它是一款小巧但功能强大的六轴传感器,集成了三轴加速度计和三轴陀螺仪。在本博客中,我们将详细介绍MPU6050的特点、工作原理以及与stm32配合的使用方法,后面看情况更新卡尔曼滤波。
目录
1. MPU6050的特点和功能
1.1 六轴传感器
MPU6050集成了三轴加速度传感器和三轴陀螺仪,六轴传感器的主要优势在于结合了加速度计和陀螺仪的测量结果,可以提供更全面和准确的物体运动和姿态信息。通过综合加速度计和陀螺仪的数据,六轴传感器可以估计物体的姿态(如欧拉角或四元数),以及物体在三个空间方向上的加速度和角速度。
1.2 数字运动处理器
MPU6050内置的数字运动处理器可以进行复杂的运动处理和数据处理,包括传感器数据的滤波、噪声消除、运动融合算法等。它通过处理器内部的算法,提供了方便快捷的姿态解算。
1.3 I2C通信接口
MPU6050传感器通信协议可以选择使用I2C或SPI。 I2C是最常用的通信协议,适用于大多数情况下。SPI是一种高速串行通信协议,更加适用于性能要求较高的应用场景。
支持的I2C通信模式中,包括两个信号线SCL(串行时钟线)和SDA(串行数据线)。通过I2C通信,可以与MPU6050进行数据的读取和写入,另一篇博客有讲到。I2C(IIC)通信协议详解与应用
如果选择使用SPI通信协议,可以参考MPU6050的数据手册以及您所使用的STM32的文档,配置和连接SPI总线,设置通信参数,并使用SPI协议与MPU6050进行数据交换,我是很少使用SPI通信的,详细的通信协议另一篇博客也有讲到,想了解的也可以去看一下。了解SPI通信:串行外设接口的基本工作原理,51、stm32实现SPI
1.4 高精度和低功耗
为了实现MPU6050的高精度,我们可以进行传感器校准、数据滤波、温度补偿、数据处理和算法优化,以及系统调优等步骤。通过这些措施,可以提高MPU6050的测量精度和稳定性,确保获得准确可靠的运动测量和姿态估计结果,同时通过智能功耗控制来延长电池寿命。
2. MPU6050的工作原理
2.1 加速度计原理
加速度计测量物体的加速度,其工作基于微小的质量和弹性部件组成的微型机械结构。加速度使得这些部件发生变形,并通过电容或压阻传感器转换成电信号。MPU6050的加速度计可以测量在三个轴上的线性加速度。
2.2 陀螺仪原理
陀螺仪测量物体的角速度,其工作基于角动量守恒定律。陀螺仪由旋转部件和感测部件组成,当物体发生旋转时,感测部件会受到力矩的作用而产生电信号。MPU6050的陀螺仪可以测量在三个轴上的旋转速率。
2.3 姿态解算
姿态解算是将加速度计和陀螺仪的数据进行融合,从而获得物体的姿态信息(例如欧拉角)。通过运动融合算法,可以综合利用两类传感器的优势,同时弥补它们的局限性。常用的算法包括卡尔曼滤波、互补滤波和四元数等,卡尔曼滤波后面会涉及。
3.模块电路图
4. 如何使用MPU6050
4.1 硬件连接
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 | 中断引脚,可用于连接到微控制器的中断引脚,用于传输中断信号 |
4.2 初始化设置
在开始使用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通信。
*/
4.3 读取传感器
一旦完成初始化设置,就可以开始读取传感器数据了。通过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`变量。
*/
4.4 数据处理和滤波
在获得原始数据后,您可以根据需求进行进一步的数据处理和滤波。例如,您可以使用滑动窗口平均或低通滤波器来减少噪声和不稳定性。此外,您还可以根据加速度计和陀螺仪数据,使用姿态解算算法计算物体的姿态,如俯仰角、横滚角和偏航角。
#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`函数进行滤波,并使用滤波后的数据进行后续处理。
*/