江科大STM32入门:MPU6050六轴姿态传感器详解(原理+硬件+寄存器)


📌 开发环境: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 信号链路
从框图可以清晰地看到数据流向:
-
传感器阵列:X/Y/Z三轴加速度计、X/Y/Z三轴陀螺仪、温度传感器
-
自测单元:用于验证芯片好坏,启动自测后传感器会输出标准值,方便检测是否损坏
-
ADC转换:16位模数转换,将模拟信号数字化
-
传感器寄存器:转换后的数据先存入内部寄存器(地址0x3B~0x48)
-
FIFO:先入先出缓冲区,可以批量读取数据,降低主控中断频率
-
DMP:可选的数据融合处理
-
通信接口: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语言逐行解析,敬请期待!
如果本文对你有帮助,欢迎点赞 👍 收藏 ⭐ !有任何问题欢迎在评论区交流,一起学习进步!

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


所有评论(0)