🚀 我的STM32学习专栏:[点击直达 | 持续更新中]

📌 开发环境:Keil5 + STM32F103C8T6最小系统板 + ST-Link
📌 参考资料:江科大STM32入门教程、《STM32F10xxx参考手册》、MPU6050数据手册
📌 前置文章江科大STM32入门:I2C通信协议详解(软件模拟篇)| 从时序到数据帧一次讲透
📌 适用硬件:MPU6050模块(GY-521等通用模块)


一、前言

做平衡车、四轴飞行器、姿态检测的时候,我们总需要知道"物体现在是什么姿势"。光靠人眼看肯定不行,得让单片机"感知"到姿态变化。

MPU6050 就是干这个活的。它是一个6轴姿态传感器,能同时测出芯片自身在X、Y、Z三个方向上的加速度角速度。把这6个数据一融合,就能得到我们常说的姿态角(也叫欧拉角),也就是物体在空间中的"俯仰、滚转、偏航"角度。

💡 我的理解:你可以把MPU6050想象成一个装在芯片里面的"微型惯性测量单元"。它不问GPS、不问磁场,就靠感知自身的运动状态,告诉你"我现在是歪的还是正的、是在加速还是减速"。这种不依赖外部信号的测量方式,叫惯性导航,也是无人机能稳定飞行的核心原理之一。


二、6轴到底是哪6轴?

很多人第一次听到"6轴"会懵:XYZ只有3个轴啊,哪来的6个?

其实这里的"6轴"指的是 3轴加速度计 + 3轴陀螺仪,合起来6个测量维度:

传感器类型 测量内容 通俗解释
3轴加速度计 (Accelerometer) X、Y、Z轴的加速度 感知"重力方向"和"直线加速度",比如倾斜时重力在三个轴上的分量
3轴陀螺仪 (Gyroscope) X、Y、Z轴的角速度 感知"旋转的快慢",比如绕某个轴转动的速度

💡 我的理解:加速度计和陀螺仪是互补的。加速度计能告诉你"哪边是地面"(因为有重力),所以静态时很准;但一运动起来,它会把运动加速度也当成重力分量,就乱了。陀螺仪正好相反,它测的是"转得多快",动态时很灵敏;但长时间积分会漂移,静态时反而靠不住。所以最后必须把两者数据融合起来,才能得到稳定的姿态角。MPU6050内部甚至自带了DMP(数字运动处理器)专门干这个融合的活。

加速度计只具有静态稳定性,陀螺仪只具有动态稳定性,两者必须互补使用。


三、欧拉角:Pitch、Roll、Yaw

拿到6轴原始数据后,我们最终想要的是姿态角,也就是物体相对于初始位置的旋转角度。这里引入欧拉角的概念,用三个角度描述刚体在三维空间的姿态:

欧拉角 英文名 动作描述 生活例子
俯仰角 Pitch 绕X轴旋转,机头下倾/上仰 抬头看天、低头看手机
滚转角 Roll 绕Y轴旋转,机身左翻/右翻 侧身倾斜、手机横屏
偏航角 Yaw 绕Z轴旋转,机头左转/右转 原地转身、左右摇头

💡 我的理解:刚开始学的时候,我总是搞混这三个角到底绕哪个轴转。我的记忆方法是——Pitch像点头,Roll像歪头,Yaw像摇头。而且注意,MPU6050的加速度计+陀螺仪组合,在没有磁力计辅助的情况下,Yaw角(偏航角)是会漂移的。因为绕Z轴旋转时,重力方向不变,加速度计帮不上忙,只能靠陀螺仪积分;而陀螺仪积分久了必然漂移。所以如果要测准确的偏航角,通常还要外接磁力计(比如HMC5883L)或者MPU9250(内置磁力计)。


四、MPU6050关键参数解读

看芯片不能只看"它能干什么",还得知道"它能干到什么程度"。下面把核心参数逐个拆解:

图注:模块硬件电路原理图,包含LDO稳压、I2C上拉电阻、地址选择等关键电路。

4.1 16位ADC与量化范围

MPU6050内部集成了16位ADC,把传感器测到的模拟信号转成数字量。16位有符号数的范围是 -32768 ~ 32767

这意味着:无论你把量程设得多大,ADC输出的数字范围始终是±32767。量程越大,每个数字代表的物理量就越大,分辨率就越低

4.2 量程选择(重中之重)

传感器 可选量程 含义
加速度计 ±2g / ±4g / ±8g / ±16g 最大能测多少个重力加速度
陀螺仪 ±250°/s / ±500°/s / ±1000°/s / ±2000°/s 最大能测每秒转多少度

怎么选? 做平衡车一般±2g就够了,因为重力也就1g;做四轴飞行机动得比较猛,可能需要±8g或±16g。陀螺仪同理,转得慢选±250°/s精度高,转得快选±2000°/s量程大。

⚠️ 风险说明:量程不是越大越好!量程越大,分辨率越低。比如±2g时,1个数字代表约0.00006g;±16g时,1个数字代表约0.0005g,精度差了将近10倍。所以根据实际场景选最小够用的量程,这是提升数据质量的第一要义。

4.3 可配置的数字低通滤波器(DLPF)

传感器数据总有噪声,尤其是陀螺仪。MPU6050内置了数字低通滤波器,可以配置截止频率。配置得越低,输出越平滑,但延迟越大;配置得越高,响应越快,但噪声越多。

💡 我的理解:低通滤波器就像筛子,把高频抖动筛掉。做平衡车时,如果电机振动导致数据抖动,可以适当调低截止频率,让数据"稳"一点。但别调太低,否则你快速倾斜时它反应不过来,车就摔了。

4.4 采样频率

通过配置SMPLRT_DIV(采样分频器) 来控制数据输出速率。公式大概是:

采样频率 = 陀螺仪输出频率 / (1 + SMPLRT_DIV)

具体数值还要看DLPF配置,数据手册里有详细表格。一般我们设个1kHz或更低就够了,没必要追求极致,毕竟STM32处理也需要时间。


五、硬件电路分析

5.1 引脚功能

从电路原理图可以看出,MPU6050模块的引脚定义如下:

引脚 功能 说明
VCC 电源 模块通常支持3.3V~5V输入,内部有LDO稳压到3.3V给芯片供电
GND
SCL I2C时钟线 已内置4.7kΩ上拉电阻,可直接接STM32的GPIO
SDA I2C数据线 同上,内置上拉
XCL 主机I2C时钟 用于外接其他从机(如磁力计),扩展功能
XDA 主机I2C数据 同上
AD0 地址最低位 接地时地址为0x68,接VCC时为0x69
INT 中断输出 可配置为数据就绪中断、自由落体检测、运动检测等触发

5.2 I2C从机地址

MPU6050的7位从机地址由 110100X 决定,X就是AD0引脚的电平:

  • AD0 = 0(接地):地址为 1101000 = 0x68

  • AD0 = 1(接VCC):地址为 1101001 = 0x69

⚠️ 特别注意:很多新手在这里踩坑。0x68是不带读写位的7位地址。实际I2C发送时,需要先把地址左移1位,再或上读写位(写=0,读=1)。所以:

  • 写操作的实际发送字节是 0x68 << 1 | 0 = 0xD0

  • 读操作的实际发送字节是 0x68 << 1 | 1 = 0xD1

江科大的I2C代码里用的就是0xD0和0xD1,原因就在这里。

5.3 DMP单元

DMP全称 Digital Motion Processor,是InvenSense(MPU6050厂商)内置的硬件算法单元。它可以直接输出四元数或欧拉角,解放主控MCU的计算压力。不过DMP的用法比较复杂,需要加载固件,初学者先用原始数据自己写融合算法(如互补滤波)会更利于理解原理。


六、模块内部结构框图

(以下为内部结构框图与数据帧格式)

图注:上图展示了MPU6050内部完整信号链路——从传感器自测、模拟信号采集、ADC转换,到传感器寄存器、FIFO缓冲、DMP处理,最后通过I2C/SPI接口与主控通信。

6.1 信号链路

从框图可以清晰地看到数据流向:

  1. 传感器阵列:X/Y/Z三轴加速度计、X/Y/Z三轴陀螺仪、温度传感器

  2. 自测单元:用于验证芯片好坏,启动自测后传感器会输出标准值,方便检测是否损坏

  3. ADC转换:16位模数转换,将模拟信号数字化

  4. 传感器寄存器:转换后的数据先存入内部寄存器(地址0x3B~0x48)

  5. FIFO:先入先出缓冲区,可以批量读取数据,降低主控中断频率

  6. DMP:可选的数据融合处理

  7. 通信接口:Slave I2C/SPI(与主控通信)、Master I2C(外接传感器)

6.2 时钟系统

MPU6050支持内部振荡器和外部时钟输入。一般模块上都用内部时钟,CLKIN引脚悬空。外部时钟适用于对时序要求极高的场景,入门阶段不用管。

6.3 电荷泵(Charge Pump)

框图左下角有个Charge Pump,旁边批注"电荷泵(是一种升压电路),外接个电容"。它的作用是为内部某些电路提供高于电源电压的偏置电压,保证传感器正常工作。模块上已经给你配好了电容,不用额外操心。


七、I2C通信与寄存器详解

MPU6050通过I2C与STM32通信,所有配置和数据读取都围绕寄存器展开。理解寄存器,就理解了如何控制这块芯片。

7.1 数据帧格式

两种写时序:

  • 单字节写:Start → 写地址(0xD0) → ACK → 寄存器地址(RA) → ACK → 数据(DATA) → ACK → Stop

  • 连续写(Burst Write):在发送第一个数据后,不发送Stop,继续发下一个数据,MPU6050会自动把地址指针+1

读操作类似,只是需要先"_dummy write" 寄存器地址,再Restart发读地址(0xD1),然后读取数据。

💡 我的理解:Burst模式读传感器数据特别爽。比如从0x3B(ACCEL_XOUT_H)开始,连续读14个字节,就能一次性拿到加速度6字节+温度2字节+陀螺仪6字节,效率比单个读高多了。江科大的代码里用的就是连续读。

7.2 配置类寄存器

寄存器名 地址(Hex) 功能
SMPLRT_DIV 0x19 采样频率分频器,8位整体作为分频值
CONFIG 0x1A 配置寄存器,包含外部同步设置和低通滤波器配置(DLPF_CFG)
GYRO_CONFIG 0x1B 陀螺仪配置,设置量程(FS_SEL)和自测位
ACCEL_CONFIG 0x1C 加速度计配置,设置量程(AFS_SEL)和自测位

CONFIG寄存器里的DLPF_CFG(低通滤波配置)是最常用的:

DLPF_CFG 加速度计带宽 陀螺仪带宽 说明
0 260Hz 256Hz 最低延迟,噪声最大
3 44Hz 42Hz 常用平衡点
6 5Hz 5Hz 最平滑,延迟最大

7.3 数据寄存器(只读)

所有数据都是16位有符号数,分高字节(H)和低字节(L)存储,读取后需要拼接:

寄存器名 地址(Hex) 内容
ACCEL_XOUT_H / L 0x3B / 0x3C X轴加速度高/低字节
ACCEL_YOUT_H / L 0x3D / 0x3E Y轴加速度高/低字节
ACCEL_ZOUT_H / L 0x3F / 0x40 Z轴加速度高/低字节
TEMP_OUT_H / L 0x41 / 0x42 温度传感器数据
GYRO_XOUT_H / L 0x43 / 0x44 X轴陀螺仪高/低字节
GYRO_YOUT_H / L 0x45 / 0x46 Y轴陀螺仪高/低字节
GYRO_ZOUT_H / L 0x47 / 0x48 Z轴陀螺仪高/低字节

数据拼接公式

int16_t Accel_X = (Data_H << 8) | Data_L;  // 高字节左移8位,或上低字节

7.4 电源管理寄存器(极易踩坑!)

寄存器名 地址(Hex) 上电默认值 说明
PWR_MGMT_1 0x6B 0x40 电源管理1,第6位是SLEEP位!默认是1,芯片在睡觉!
WHO_AM_I 0x75 0x68 器件ID,读取用于验证通信是否正常

⚠️ 超级重点PWR_MGMT_1 (0x6B) 上电默认是0x40,也就是SLEEP位为1,芯片处于睡眠模式! 如果你上电后直接读数据,读出来的全是0或者乱码。必须先把0x6B写0x00,唤醒芯片,才能正常读取传感器数据。这个坑我踩过。


八、个人理解与心得

(以下为个人理解,仅供参考,欢迎评论区交流)

8.1 关于"数据融合"的认知

刚开始学MPU6050的时候,我以为只要读出加速度计和陀螺仪的数据,简单算一下就能得到姿态角。后来才发现,数据融合是一门学问

最简单的融合方法是互补滤波:加速度计测的角度低频准、高频噪声大;陀螺仪积分出的角度高频准、低频漂移。用互补滤波器把两者"互补"一下,就能得到一个既不怕噪声又不漂移的角度。公式大概长这样:

角度 = α * (角度 + 陀螺仪角速度 * dt) + (1-α) * 加速度计算出的角度

其中α是一个0~1之间的系数,通常取0.98左右。这个公式虽然简单,但效果已经很不错了,做平衡车完全够用。

8.2 关于量程与分辨率的选择

我一开始贪图方便,直接把量程设到最大(±16g,±2000°/s),想着"反正不会溢出"。结果数据抖动特别厉害,PID怎么调都稳不住。后来把加速度计量程改回±2g,陀螺仪±500°/s,数据立马干净了很多。

我的结论:量程选最小够用的,这是提升数据质量最简单有效的方法,比后面加滤波器管用多了。

8.3 关于I2C通信速率

MPU6050的I2C接口支持最高400kHz(Fast Mode)。我用100kHz和400kHz都试过,读数据都能正常读。但如果要配合DMP输出高帧率姿态数据,或者外接多个从机,400kHz会更从容一些。


九、踩坑记录

(以下为踩坑记录,都是血泪史,建议新手逐条对照检查)

❌ 坑1:上电后读不出数据,全是0

现象:I2C通信正常(能读到WHO_AM_I = 0x68),但传感器数据寄存器全是0。

原因PWR_MGMT_1 (0x6B) 默认是0x40,SLEEP位被置1,芯片在睡觉!

解决:初始化时务必执行 MPU6050_WriteReg(0x6B, 0x00); 把SLEEP位清0。也可以写0x01选择X轴陀螺仪作为时钟源,据说这样更稳定。

❌ 坑2:数据拼接错误,数值跳变

现象:读出来的数据偶尔会从正数突然变成很大的负数,或者 vice versa。

原因:高字节和低字节拼接时,没有用有符号类型。比如用 uint16_t 拼接后再强制转 int16_t,可能会因为符号位处理不当导致错误。

解决:直接用 int16_t 接收拼接结果:

int16_t Accel_X = ((int16_t)Buf[0] << 8) | Buf[1];  // Buf[0]是H, Buf[1]是L

❌ 坑3:忽略温度补偿

现象:传感器上电后,前几分钟数据漂移明显,尤其是陀螺仪零偏。

原因:MEMS传感器对温度敏感,芯片自发热会导致零偏漂移。

解决:上电后预热1~2分钟,或者做温度补偿校准。做高精度项目时,每次上电先采集一段时间的零偏数据取平均,作为静态偏移量,后续读取时减去这个偏移。

❌ 坑4:以为MPU6050能直接输出欧拉角

现象:看网上有人说MPU6050能直接输出角度,找了半天没找到对应寄存器。

原因MPU6050默认只输出原始加速度、陀螺仪、温度数据。要直接输出欧拉角或四元数,必须启用DMP并加载固件,这不是开箱即用的功能。

解决:初学者建议先用原始数据+互补滤波自己算角度,理解原理后再尝试DMP。江科大的教程也是先讲原始数据读取。


十、总结表格

项目 内容
芯片型号 MPU6050
传感器类型 6轴惯性测量单元(3轴加速度计 + 3轴陀螺仪)
ADC分辨率 16位有符号数(-32768 ~ 32767)
加速度量程 ±2g / ±4g / ±8g / ±16g
陀螺仪量程 ±250°/s / ±500°/s / ±1000°/s / ±2000°/s
通信接口 I2C(标准模式100kHz / 快速模式400kHz)、SPI
7位从机地址 0x68(AD0=0)/ 0x69(AD0=1)
关键唤醒操作 向0x6B写入0x00,退出睡眠模式
原始数据寄存器 0x3B ~ 0x48(加速度6B + 温度2B + 陀螺仪6B)
内置处理器 DMP(数字运动处理器),可选用于姿态解算
典型应用 平衡车、四轴飞行器、智能手环、姿态检测

十一、结语与下节预告

写这篇文章的时候,我把江科大的视频又回顾了一遍,把数据手册的相关章节也翻出来对照了一下。说实话,MPU6050这个芯片看起来简单,就是"读几个寄存器的事",但要把里面的原理吃透——为什么加速度计静态准、为什么陀螺仪会漂移、为什么低通滤波器要这么配、为什么上电要唤醒——还是需要静下心来把每一个知识点串起来。

这篇文章主要把原理、硬件、寄存器三个层面梳理了一遍,希望能帮你建立一个完整的认知框架。下一篇我会详细讲解MPU6050的代码实现,包括I2C通信代码、寄存器初始化、数据读取与拼接、互补滤波算法,以及如何在OLED上实时显示姿态角。代码会用C语言逐行解析,敬请期待!

如果本文对你有帮助,欢迎点赞 👍 收藏 ⭐ !有任何问题欢迎在评论区交流,一起学习进步!

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐