[Internet of Things] MPU6050 sensor data collection and filtering algorithm

When it comes to attitude control and motion detection of embedded electronic devices and robots, the MPU6050 is often a sensor module that attracts much attention. It is a small but powerful six-axis sensor that integrates a three-axis accelerometer and a three-axis gyroscope. In this blog, we will introduce in detail the characteristics, working principle and use method of MPU6050 in conjunction with stm32, and update the Kalman filter according to the situation later.

Table of contents

1. Features and functions of MPU6050

1.1 Six-axis sensor

1.2 Digital motion processor

1.3 I2C communication interface

1.4 High precision and low power consumption

2. Working principle of MPU6050

2.1 Accelerometer principle

2.2 Principle of Gyroscope

2.3 Attitude Calculation

3. Module circuit diagram

4. How to use MPU6050

4.1 Hardware connection

4.2 Initial settings

4.3 Reading sensors

4.4 Data processing and filtering

5. Kalman filter


1. Features and functions of MPU6050

1.1 Six-axis sensor

The MPU6050 integrates a three-axis acceleration sensor and a three-axis gyroscope. The main advantage of the six-axis sensor is that it combines the measurement results of the accelerometer and gyroscope to provide more comprehensive and accurate object motion and attitude information. By combining accelerometer and gyroscope data, a six-axis sensor can estimate an object's pose (such as Euler angles or quaternions), as well as the object's acceleration and angular velocity in three spatial directions.

1.2 Digital motion processor

The built-in digital motion processor of MPU6050 can perform complex motion processing and data processing, including sensor data filtering, noise elimination, motion fusion algorithm, etc. It provides convenient and fast attitude calculation through the algorithm inside the processor.

1.3 I2C communication interface

The MPU6050 sensor communication protocol can optionally use I2C or SPI. I2C is the most commonly used communication protocol and is suitable for most situations. SPI is a high-speed serial communication protocol, which is more suitable for application scenarios with higher performance requirements.

The supported I2C communication mode includes two signal lines SCL (serial clock line) and SDA (serial data line). Through I2C communication, data can be read and written with the MPU6050, as mentioned in another blog. Detailed explanation and application of I2C (IIC) communication protocol

If you choose to use the SPI communication protocol, you can refer to the data sheet of the MPU6050 and the documentation of the STM32 you are using to configure and connect the SPI bus, set the communication parameters, and use the SPI protocol to exchange data with the MPU6050. I rarely use SPI communication , the detailed communication protocol is also mentioned in another blog, if you want to know more, you can also read it. Understand SPI communication: the basic working principle of the serial peripheral interface, 51, stm32 implements SPI

1.4 High precision and low power consumption

In order to achieve the high precision of the MPU6050, we can perform steps such as sensor calibration, data filtering, temperature compensation, data processing and algorithm optimization, and system tuning. Through these measures, the measurement accuracy and stability of the MPU6050 can be improved to ensure accurate and reliable motion measurement and attitude estimation results, while extending battery life through intelligent power consumption control.

2. Working principle of MPU6050

2.1 Accelerometer principle

Accelerometers measure the acceleration of an object and work based on a micromechanical structure composed of tiny masses and elastic components. Acceleration causes these components to deform, which is converted into electrical signals via capacitive or piezoresistive sensors. The MPU6050's accelerometer can measure linear acceleration in three axes.

2.2 Principle of Gyroscope

Gyroscopes measure the angular velocity of an object and work based on the law of conservation of angular momentum. A gyroscope consists of a rotating component and a sensing component. When an object rotates, the sensing component is acted upon by a torque and generates an electrical signal. The MPU6050's gyroscope can measure rotation rate on three axes.

2.3 Attitude Calculation

Attitude solution is to fuse the data of accelerometer and gyroscope to obtain the attitude information of the object (such as Euler angle). Through motion fusion algorithms, the advantages of both types of sensors can be comprehensively utilized while compensating for their limitations. Commonly used algorithms include Kalman filtering, complementary filtering and quaternion, etc. Kalman filtering will be covered later.

3. Module circuit diagram

4. How to use MPU6050

4.1 Hardware connection

The function of each pin on the GY-521 module

VCC Power supply pin, connect to 3V-5V power supply, generally use 5V
GND Ground pin, connected to the ground of the power supply
SCL I2C clock line pin, used for clock synchronization with the main control device
SDA I2C data line pin, used for data communication with the main control device
XDA Can be used to interface other I2C modules with MPU6050
XCL Can be used to interface other I2C modules with MPU6050
AD0 I2C address select pin, you can select the I2C address of the MPU-6050 by connecting it to VCC or GND
INT Interrupt pin, which can be used to connect to the interrupt pin of the microcontroller for transmitting interrupt signals

4.2 Initial settings

Before starting to use the MPU6050, initialization settings are required. Through the I2C communication interface, write appropriate values ​​into the configuration register of the MPU6050 to configure parameters such as sampling rate, sensor range, and filter. You can refer to the MPU6050 data sheet or use the relevant development library to set it up.

The I2C communication pins initialized here are PB7 and PB6, and the clock uses 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 Reading sensors

Once the initial setup is complete, it is time to start reading sensor data. Read the raw data of accelerometer and gyroscope from MPU6050 through I2C interface. This data is provided as raw values, usually numerical representations, and requires some processing to yield useful information.


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 Data processing and filtering

After obtaining the raw data, you can perform further data processing and filtering according to your needs. For example, you can use sliding window averaging or low-pass filters to reduce noise and instability. In addition, you can use attitude solver algorithms to calculate an object's attitude, such as pitch, roll, and yaw, based on accelerometer and gyroscope data.

#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`函数进行滤波,并使用滤波后的数据进行后续处理。
*/

5. Kalman filter

A closer look at Kalman filtering: a mathematical artifact for optimal state estimation

MPU6050 chip manual, module circuit diagram, test program

Guess you like

Origin blog.csdn.net/Goforyouqp/article/details/131613938