一、mpu6050数据解析原理

1. 通过i2c驱动中API接口读取设备获取原始数据

具体参考I2C驱动文章。

2. 对mpu6050原始数据进行解析
  1. 如需把原始的 16 位数据用重力加速度单位 g 表示,需要注意传感器的量程。默认测量范围是 ±2g,一共是4g的宽度,16 位数据的最大读数为 65536,除 4 就是 16384,即1g加速度对应的数值。打印前把数据除以 16384 即可得到对应单位为 g 的数值,角速度的单位转换也同理。

  2. 由于每个芯片在制作时都不一样,芯片固定在 PCB 上也会有位置误差,就会导致读到的数据有偏差,对传感器进行零偏校准可一定程度上减小这种误差的影响。对程序作最后修改,在读到的数据基础上加上一个补偿值 OFFSET 对数据校准。

  3. 利用 C 语言 math.h 头文件提供的 acos() 库函数可以很方便计算反余弦函数,不过返回的是弧度值,要转换为角度的话需乘上 57.29577Tips:由于用到acos数学函数,数学函数单独放在libm库中,需在文件上面包含#include <math.h>,且在编译命令末尾加上-lm,默认情况下,编译器不会自动链接它,需手动添加 -lm

最终得到x、y、z方向的角度值,计算公式如下:
result->angle_x = (int32_t)(acos((double)((double)(AccX + MPU6050_X_ACCEL_OFFSET) / 16384.0)) * 57.29577)

3. 对解析后的数据进行滤波

LPF低通滤波算法如下: updata_data = ( alpha * raw_data + ( 1.0f - alpha ) * prev_data* ),其中滤波系数越小,截止频率越低,低通滤波效果越好。

最终编译烧录进开发板,通过串口将数据发送出来。

二、DMP

1. 程序架构

MPU6050软件程序架构如下所示。分为硬件层的mpu6050的物理设备,驱动层的i2c驱动文件,中间层的操作函数API接口文件(用于将操作函数集与DMP库的相关函数API链接)和DMP文件,应用层的mpu6050应用文件(调用DMP库进行mpu6050的初始化和内部dmp解析数据的初始化配置)和APP应用文件(最终应用程序)。
![[Pasted image 20250516141939.png]]在这里插入图片描述

分类 内核空间 (Kernel Space) 用户空间 (User Space)
权限 最高权限(Ring 0),可直接操作硬件和底层资源 低权限(Ring 3),无法直接访问硬件
运行内容 操作系统内核、驱动程序、关键服务(如进程调度、内存管理) 应用程序(如浏览器、文本编辑器)
崩溃影响 内核崩溃会导致系统宕机 应用程序崩溃通常不影响其他程序或系统
交互方式 直接控制硬件 通过系统调用(如 read(), write())请求内核服务
2. 操作函数API接口文件

使用mpu6050的官方库DMP库的重点在于操作函数API接口文件,接下来先从驱动层的mpu6050驱动文件中的字符设备操作函数集开始分析。

字符设备操作函数集,供用户使用的API接口。应用程序工作在用户空间,应用层APP不可直接访问内核空间,可通过操作函数集的API接口去访问内核空间。

/*字符设备操作函数集*/
static struct file_operations mpu6050_chr_dev_fops =
	{
		.owner = THIS_MODULE,
		.open = mpu6050_open,
		.write = mpu6050_write,
		.read = mpu6050_read,
		.release = mpu6050_release,
};

操作函数原型:static ssize_t mpu6050_write(struct file *filp, char __user *buf, size_t cnt, loff_t *off),其中各参数解释如下。

- **filp**:待操作的设备文件`file`结构体指针;
- **buf**:待写入数据的用户空间缓冲区指针;
- **cnt**:为要写入信息的长度;
- **off**:为当前的偏移位置,这个值通常是用来判断写文件是否越界

应用程序工作在用户空间,而驱动工作在内核空间,二者不能直接通信的,内核中的 memcpy—copy_from_usercopy_to_user。而copy_from_usercopy_to_user这两个函数是_memcpy()的二次封装。由于_memcpy() 函数是有缺陷的,譬如我们在用户层调用函数时传入的不是字符串,而是一个不能访问或修改的地址,那样就会造成系统崩溃。所以内核和用户态之间交互的数据时必须要先对数据进行检测,如果数据是安全的,才可以进行数据交互。上面的函数就是memcpy的改进版,在memcpy功能的基础上加上的检查传入参数的功能,防止有些人有意或者无意的传入无效的参数。

接下来讲述操作函数API接口文件。这一章的内容都是为了DMP库而运作,DMP库中主要调用i2c_writei2c_read函数,这两个需要与字符设备操作函数集的数据产生链接,但两边所传输的参数并不是一致的,所以在DMP库和设备驱动文件之中需要有一个文件,将字符设备操作函数集内的函数经过处理转化成与DMP库所需的i2c_writei2c_read函数参数一致,这个文件即是操作函数API接口文件,将操作函数集中的API接口经过处理方式转化成适配DMP库的API接口。

void mpu6050_i2c_write(char dev_addr, char reg_addr, size_t size, char pdata)         //(dev_addr, reg_addr, size, pdata) 
{
    unsigned char send_data[2];
    send_data[0] = reg_addr;
    send_data[1] = pdata;
    write(mpu6050_fd, send_data, size);     //write是内核的API接口,对应的是驱动文件里操作函数集的.write
}
3. DMP文件(Digital Motion Processor,数字运动处理器)

MPU6050模块的DMP是其内部集成的专用硬件模块,主要用于姿态解算传感器数据融合,能够显著简化开发流程并降低主控芯片的运算负担。以下是其核心作用及工作流程的详细说明

DMP的核心作用:

  1. 姿态解算与数据融合
    DMP通过内置算法(如四元数解算)将MPU6050的原始加速度计和陀螺仪数据融合,直接输出欧拉角(Pitch、Roll、Yaw)四元数,无需用户手动实现复杂的滤波或融合算法。

    • 加速度计:测量静态倾角,但动态噪声大。

    • 陀螺仪:测量角速度,但存在积分漂移。
      DMP通过融合两者优势,消除漂移并提高动态精度。

  2. 硬件加速与资源优化
    DMP是独立的硬件处理单元,可直接运行官方预置的算法(如InvenSense的嵌入式运动驱动库),将主控芯片从高计算量的姿态解算任务中解放,使其专注于其他实时任务。

  3. 内置滤波与降噪
    DMP内部已集成数字低通滤波器(DLPF动态校准功能,能对原始数据进行初步平滑处理,减少噪声干扰。

dmp文件当中,关键在于修改inv_mpu.c文件中宏定义后的文件改为我们在操作函数API接口文件预留的API接口,如此可将dmp库文件的函数完善,为应用层的调用函数做铺垫。

4. mpu6050应用文件

核心是调用dmp库文件,通过MPU6050_DMP_init()函数完成mpu6050设备和dmp的配置、初始化和通过MPU6050_DMP_Get_Date()完成数据解析、输出欧拉角。

int MPU6050_DMP_init(void);
int MPU6050_DMP_Get_Date(float *pitch, float *roll, float *yaw);
5. APP应用文件

到了这里,基本程序就结束了。

  • 这里通过调用open启用设备驱动;
  • 调用mpu6050应用文件中MPU6050_DMP_init文件对mpu6050设备和dmp的配置,完成对物理层设备的零点偏差进行校准;
  • 调用MPU6050_DMP_Get_Date获取数据解析后的数据;
  • 调用close关闭设备驱动,断开设备与驱动的连接。

至此,mpu6050调用dmp库开发流程结束。

Linux大佬云集的地方,搜大量资料找一个Linux开发环境下的DMP库移植,却找不到,大都为mcu基于hal库开发,或许对于大佬来说,纯粹是CV工程师的移植,但是对于我这个Linux小白来说,高度模块化的代码结构,以及在Linux下的手写驱动结构替代以往hal库的直接调用,出现的问题超多!这篇文章基于我学习过程中出现的问题,做了一个简单的整理,如有地方讲述错误或不清晰,可以在评论区评论,供许多像我一样的Linux初学者借鉴参考,谢谢!

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐