MPU6050用法

前言:

我想把值通过OLED显示出来,以及用串口把数据发送到VOFA+生成3D图像,所以会先讲解这部分的内容,大家可以根据目录直接跳到MPU6050部分。

直接跳到扩展部分!!封装的更完整,两种我亲测过都可以,看你们喜好。

注意!!本OLED的代码移植于江协科技的代码,如果有什么不理解的,欢迎大家观看江协科技的OLED课程,这里我只将如何使用。

1.OLED

配置PB8、PB9为开漏输出,上拉电阻

修改GPIO口输出函数,参数也要修改。

初始化OLED,写输出函数,更新OLED,即可显示。

2.串口

int my_printf(UART_HandleTypeDef *huart, const char *format, ...)
{
	char buffer[512]; 
	va_list arg;      
	int len;         

	va_start(arg, format);
	
	len = vsnprintf(buffer, sizeof(buffer), format, arg);
	va_end(arg);
	
	
	HAL_UART_Transmit(huart, (uint8_t *)buffer, (uint16_t)len, 0xFF);
	return len;
}

只需这段代码即可,串口重定向

1.MPU6050介绍

MPU6050是一种广泛应用于姿态检测的6轴运动处理传感器,由InvenSense公司研发生产。它集成了以下测量功能:

  1. 三轴加速度测量(X/Y/Z轴)

    • 测量范围可配置(±2g、±4g、±8g、±16g)
    • 典型应用:检测设备的倾斜角度、运动方向
    • 示例:手机自动旋转屏幕的横竖屏切换功能
  2. 三轴角速度测量(陀螺仪)

    • 测量范围可配置(±250°/s、±500°/s、±1000°/s、±2000°/s)
    • 典型应用:检测设备的旋转速度
    • 示例:无人机飞控系统的姿态稳定

该芯片采用I2C通信协议,工作电压3.3V-5V,内部还集成了:

  • 数字运动处理器(DMP)
  • 1024字节的FIFO缓冲区
  • 温度传感器
  • 内置时钟发生器

应用场景包括:

  1. 四轴飞行器姿态控制
  2. 智能手机运动检测
  3. 游戏控制器动作捕捉
  4. 智能手环计步功能
  5. 机器人平衡系统

典型工作流程:

  1. 初始化I2C通信
  2. 配置传感器量程
  3. 校准传感器(去除零点偏移)
  4. 读取原始数据
  5. 通过DMP或算法计算姿态角
  6. 应用数据处理(滤波、融合等)

2.读取MPU6050数据

1.写寄存器函数

	HAL_I2C_Mem_Write(hi2c,DevAddress,MemAddress, MemAddSize, pData, Size,Timeout);

hi2c:操作句柄指针

DevAddress:从机设备地址,7位,但是填入时记得左移一位

MemAddress:要读取的寄存器地址

MemAddSize:地址长度

pData:发送数据

Size:数据长度

Timeout:超时参数

/**
  * 函    数:MPU6050写寄存器
  * 返 回 值:无
  */

void MPU6050_WriteReg(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t pData, uint16_t Size, uint32_t Timeout)
{
	HAL_I2C_Mem_Write(hi2c,DevAddress,MemAddress, MemAddSize, &pData, Size,Timeout);
}

2.获取寄存器值函数

HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)

从一个特定的内存地址以阻塞模式读取一定量的数据

hi2c:操作句柄指针

DevAddress:从机地址

MemAddress:寄存器地址

MemAddSize:地址长度

pData:数据

Size:数据长度

Timeout:超时时间

int16_t MPU6050_ReadData(I2C_HandleTypeDef *hi2c,uint8_t RegAddress)
{
	uint8_t Data[2];
	HAL_I2C_Mem_Read(hi2c,MPU6050_ADDRESS,RegAddress,1,Data,2,HAL_MAX_DELAY);
	return Data[0]<<8 | Data[1];
}

3.封装各数据

 /* 函    数:MPU6050获取数据
  * 参    数:AccX AccY AccZ 加速度计X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767
  * 参    数:GyroX GyroY GyroZ 陀螺仪X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767
  * 返 回 值:无
  */
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	*AccX = MPU6050_ReadData(&hi2c2,MPU6050_ACCEL_XOUT_H);				
	
	*AccY = MPU6050_ReadData(&hi2c2,MPU6050_ACCEL_YOUT_H);
	
	*AccZ = MPU6050_ReadData(&hi2c2,MPU6050_ACCEL_ZOUT_H);
	
	*GyroX = MPU6050_ReadData(&hi2c2,MPU6050_GYRO_XOUT_H);
	
	*GyroY =MPU6050_ReadData(&hi2c2,MPU6050_GYRO_YOUT_H);
	
	*GyroZ =MPU6050_ReadData(&hi2c2,MPU6050_GYRO_ZOUT_H);
}

4.获取设备ID

/**
  * 函    数:MPU6050获取ID号
  * 参    数:无
  * 返 回 值:无
  */
void MPU6050_GetID(uint8_t *Data)
{
	HAL_I2C_Mem_Read(&hi2c2,MPU6050_ADDRESS,MPU6050_WHO_AM_I,1,Data,1,HAL_MAX_DELAY);
}

5.初始化MPU6050

/**
  * 函    数:MPU6050初始化
  * 参    数:无
  * 返 回 值:无
  */
void MPU6050_Init(void)
{

	MPU6050_WriteReg(&hi2c2,MPU6050_ADDRESS,MPU6050_PWR_MGMT_1, 1,0x01,1,HAL_MAX_DELAY);				//电源管理寄存器1,取消睡眠模式,选择时钟源为X轴陀螺仪
	MPU6050_WriteReg(&hi2c2,MPU6050_ADDRESS,MPU6050_PWR_MGMT_2, 1,0x00,1,HAL_MAX_DELAY);				//电源管理寄存器2,保持默认值0,所有轴均不待机
	MPU6050_WriteReg(&hi2c2,MPU6050_ADDRESS,MPU6050_SMPLRT_DIV, 1,0x09,1,HAL_MAX_DELAY);				//采样率分频寄存器,配置采样率
	MPU6050_WriteReg(&hi2c2,MPU6050_ADDRESS,MPU6050_CONFIG, 1,0x06,1,HAL_MAX_DELAY);					//配置寄存器,配置DLPF
	MPU6050_WriteReg(&hi2c2,MPU6050_ADDRESS,MPU6050_GYRO_CONFIG, 1,0x18,1,HAL_MAX_DELAY);			//陀螺仪配置寄存器,选择满量程为±2000°/s
	MPU6050_WriteReg(&hi2c2,MPU6050_ADDRESS,MPU6050_ACCEL_CONFIG, 1,0x18,1,HAL_MAX_DELAY);
}

电源管理器1

设备复位 | 睡眠 | 循环模式 | 无关位 | 温度传感器使能 | 选择时钟

电源管理器2

循环模式唤醒频率 | 每个轴的待机位

采样率分频

值越小越快

配置寄存器

外部同步 | 数字低通滤波器

陀螺仪配置寄存器

自测使能 | 满量程选择

加速度计配置寄存器

自测使能 | 满量程选择

6.定义变量

/* USER CODE BEGIN PV */
uint8_t IDData;
int16_t AX, AY, AZ, GX, GY, GZ;			//定义用于存放各个数据的变量

/* USER CODE END PV */

7.main函数

int main(void)
{

  /* USER CODE BEGIN 1 */
		
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_I2C2_Init();
  /* USER CODE BEGIN 2 */
	OLED_Init();//��ʼ��OLED
	MPU6050_Init();
	MPU6050_GetID(&IDData);
	OLED_ShowHexNum(0, 50,  IDData, 2, OLED_8X16);  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		
		MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
		OLED_ShowSignedNum(0, 0,  AX, 4, OLED_8X16);
		OLED_ShowSignedNum(0, 16, AY, 4, OLED_8X16);
		OLED_ShowSignedNum(0, 33, AZ, 4, OLED_8X16);
	  OLED_ShowSignedNum(50, 0,  GX, 4, OLED_8X16);
		OLED_ShowSignedNum(50, 16, GY, 4, OLED_8X16);
		OLED_ShowSignedNum(50, 33, GZ, 4, OLED_8X16);
		OLED_Update(); 
		
		
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	}
  /* USER CODE END 3 */
}

扩展—读取MPU6050升级版

mpu6050.c

#include "stm32f1xx_hal.h"              // HAL库头文件
#include "MPU6050_Reg.h"

#define MPU6050_ADDRESS    0xD0        // I2C从机地址(含读写位)
#define I2C_TIMEOUT        100         // I2C操作超时时间(ms)

extern I2C_HandleTypeDef hi2c2;        // 在外部定义I2C句柄(根据实际使用的I2C修改)

/**
  * 函    数:MPU6050写寄存器
  * 参    数:RegAddress 寄存器地址
  * 参    数:Data 要写入的数据
  * 返 回 值:HAL状态
  */
HAL_StatusTypeDef MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
    return HAL_I2C_Mem_Write(&hi2c2, MPU6050_ADDRESS, RegAddress, 
                             I2C_MEMADD_SIZE_8BIT, &Data, 1, I2C_TIMEOUT);
}

/**
  * 函    数:MPU6050读寄存器
  * 参    数:RegAddress 寄存器地址
  * 返 回 值:读取的数据
  */
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
    uint8_t data;
    HAL_I2C_Mem_Read(&hi2c2, MPU6050_ADDRESS, RegAddress,
                     I2C_MEMADD_SIZE_8BIT, &data, 1, I2C_TIMEOUT);
    return data;
}

/**
  * 函    数:MPU6050初始化
  * 参    数:hi2c I2C句柄指针
  * 返 回 值:HAL状态
  */
HAL_StatusTypeDef MPU6050_Init(I2C_HandleTypeDef *hi2c)
{
    /* 检查I2C是否就绪 */
    if (hi2c->State != HAL_I2C_STATE_READY)
        return HAL_ERROR;
    
    /* 寄存器初始化(配置与原始代码相同) */
    if (MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01) != HAL_OK) return HAL_ERROR;
    if (MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00) != HAL_OK) return HAL_ERROR;
    if (MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09) != HAL_OK) return HAL_ERROR;
    if (MPU6050_WriteReg(MPU6050_CONFIG, 0x06) != HAL_OK) return HAL_ERROR;
    if (MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18) != HAL_OK) return HAL_ERROR;
    if (MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18) != HAL_OK) return HAL_ERROR;
    
    return HAL_OK;
}

/**
  * 函    数:MPU6050获取ID号
  * 返 回 值:ID号
  */
uint8_t MPU6050_GetID(void)
{
    return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}

/**
  * 函    数:MPU6050获取数据(单次读取优化)
  * 参    数:Acc/Gyro 各轴数据指针
  * 返 回 值:HAL状态
  */
HAL_StatusTypeDef MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
                                  int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
    uint8_t data[14];  // 存储14字节数据(加速度XYZ+温度+陀螺仪XYZ)
    
    /* 一次性读取所有传感器数据 */
    if (HAL_I2C_Mem_Read(&hi2c2, MPU6050_ADDRESS, MPU6050_ACCEL_XOUT_H,
                         I2C_MEMADD_SIZE_8BIT, data, 14, I2C_TIMEOUT) != HAL_OK)
        return HAL_ERROR;
    
    /* 拼接数据(注意高位在前) */
    *AccX  = (int16_t)((data[0]  << 8) | data[1]);
    *AccY  = (int16_t)((data[2]  << 8) | data[3]);
    *AccZ  = (int16_t)((data[4]  << 8) | data[5]);
    // 跳过温度数据 data[6]和data[7]
    *GyroX = (int16_t)((data[8]  << 8) | data[9]);
    *GyroY = (int16_t)((data[10] << 8) | data[11]);
    *GyroZ = (int16_t)((data[12] << 8) | data[13]);
    
    return HAL_OK;
}

mpu6050.h

#ifndef __MPU6050_H
#define __MPU6050_H
#include "mydefine.h"

HAL_StatusTypeDef MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);
HAL_StatusTypeDef MPU6050_Init(I2C_HandleTypeDef *hi2c);
uint8_t MPU6050_GetID(void);
HAL_StatusTypeDef MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
                                  int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ);




#endif

mpu6050_reg

#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H

#define	MPU6050_SMPLRT_DIV		0x19
#define	MPU6050_CONFIG			0x1A
#define	MPU6050_GYRO_CONFIG		0x1B
#define	MPU6050_ACCEL_CONFIG	0x1C

#define	MPU6050_ACCEL_XOUT_H	0x3B
#define	MPU6050_ACCEL_XOUT_L	0x3C
#define	MPU6050_ACCEL_YOUT_H	0x3D
#define	MPU6050_ACCEL_YOUT_L	0x3E
#define	MPU6050_ACCEL_ZOUT_H	0x3F
#define	MPU6050_ACCEL_ZOUT_L	0x40
#define	MPU6050_TEMP_OUT_H		0x41
#define	MPU6050_TEMP_OUT_L		0x42
#define	MPU6050_GYRO_XOUT_H		0x43
#define	MPU6050_GYRO_XOUT_L		0x44
#define	MPU6050_GYRO_YOUT_H		0x45
#define	MPU6050_GYRO_YOUT_L		0x46
#define	MPU6050_GYRO_ZOUT_H		0x47
#define	MPU6050_GYRO_ZOUT_L		0x48

#define	MPU6050_PWR_MGMT_1		0x6B
#define	MPU6050_PWR_MGMT_2		0x6C
#define	MPU6050_WHO_AM_I		0x75

#endif

2.解算

缺点:

使用陀螺仪解算随着时间的推移漂移会很大(也就是随着时间的增加误差会增大),因为我们使用陀螺仪解算欧拉角是用递推的方式,误差会被累积

使用加速度计测量受噪声影响大,因为姿态传感器在抖动,我们以重力为参考系,在各个方向上的加速度分量会一直变化。主要还是因为加速度计不累加,所以不会有漂移。

前置概念理解

如图,把MPU6050放到小车上

欧拉角如下

yaw偏航角:绕z轴旋转所产生的角度

pitch俯仰角:绕x轴旋转所产生的角度

roll翻滚角:绕y轴旋转所产生的角度

如图,我们从小车的上方看下去,发现小车偏移后的角度为10°,这就是偏航角

①陀螺仪解算

我们知道陀螺仪测到的是角速度,角速度对时间积分就是角度,我们可以通过当前的角速度去预期下一时刻的角速度,比如我们测得当前z轴的角速度为20°/s,当前的偏航角为10°,那下一时刻是不是就可以预测是

10+20*t,t越小越准确,所以我们可以得到

yaw[k] = yaw[k-1] + gz * t

同理,我们可以得到

pitch[k] =pitch[k-1] + gx * t

roll[k] = roll[k-1] - gy * t(为什么有负号,请看前面的图)

/**
  * 函    数:MPU6050获取数据(单次读取优化)
  * 参    数:Acc/Gyro 各轴数据指针
  * 返 回 值:HAL状态
  */
void MPU6050_proc(void)
{
	
	MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
	
	//计算欧拉角
	yaw = yaw + GZ * sampling_time;
	pitch = pitch + GX * sampling_time;
	roll = roll + GY * sampling_time;	
	

}

②加速度计解算

加速度计测量姿态(俯仰pitch和横滚roll)依赖于重力向量

使用重力的方向作为参考方向,绕Z轴旋转不会改变重力在X、Y、Z轴上的分量比例
因此无法检测偏航变化

总结:加速度计无法测量准确的yaw值

	yaw = 0.0f;
	pitch = atan2(AY,AZ) / 3.1415927f * 180.0f;
	roll = atan2(AX,AZ) / 3.1415927f * 180.0f;

就这三行代码,通过各方向加速度的正切,使用反三角函数,求解出欧拉角。

重力向下,理应来说重力加速度不应该和重力一样向下吗,z轴是竖直向上,加速度计测得的应该为-g才对,其实我们加速度计测得的是惯性力。

③互补滤波

陀螺仪使用递推的方式计算欧拉角,所以会有漂移。

加速度计易受噪声干扰。
本质上是根据权重来减小 影响,还是没有完全消除,但是这个滤波算法适合初学者理解和应用。

漂移属于低频,噪声属于高频

	yaw = yaw_g;
	pitch = 0.95238* pitch_g  + (1- 0.95238) *  pitch_a;
	roll = 0.95238* roll_g + (1- 0.95238) *  roll_g;

阿法 = 0.95238,根据上面的公式计算。

④卡尔曼滤波

⑤dmp库——四元数

说明:内容可以比较精简,因为本人刚开始学,还望各位大佬纠正!后续内容等我学完在更新。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值