元器件 · 传感器

MPU-6050 惯性测量单元

MPU-6050 是一颗通过 I²C 感知倾斜、转动和加速度的六轴惯性测量单元——机器人靠它知道自身姿态,从而完成平衡和转弯。

它是什么

MPU-6050 是机器人感知自己运动的方式。六轴惯性测量单元——一颗芯片上陀螺仪加加速度计——报告倾斜、多快转、怎么加速。超声波看外面世界,IMU 看里面自己身体,自平衡车、航位推算导航要的就是它。

MPU-6050 IMU 转接板(GY-521 板)标注图:紫色 PCB,中央 MPU-6050 芯片和阻容,X/Y/Z 轴向指示三轴方向,底部 8 针排针(VCC、GND、SCL、SDA、XDA、XCL、AD0、INT)。
一颗芯片上陀螺仪加加速度计,感知三轴转动和倾斜,经 I²C 读。 下载 SVG

工作原理

里面两个传感器。加速度计量 X、Y、Z 线加速度——静止就是重力,告诉机器人哪边是下(倾角)。陀螺仪量绕各轴转多快。都经 I²C(SCL/SDA)读。

两个传感器单独用都不够:把陀螺仪积分成角度,它会慢慢漂移;完全相信加速度计,机器人一动读数就充满噪声。解法是传感器融合——把两者混合起来,让加速度计纠正陀螺仪的漂移,让陀螺仪抹平加速度计的噪声。传感器噪声、偏置与滤波讲的正是这项技术。

什么时候用它

机器人要知道自己朝向或运动时请 MPU-6050:

  • 自平衡车——控制环拼命保持零的倾角。
  • 航向和航位推算——编码器读数之间感知转弯改进里程计。
  • 运动检测 / 手势——被拿起、放倒、摇晃有反应。

量到东西的距离(墙、障碍)换测距传感器 HC-SR04 或 VL53L0X——IMU 感知机器人,不感知世界。

接线与坑

  • **I²C,3.3–5 V。**大多数转接板(紫色的 GY-521)带稳压器,所以 5 V 没问题;裸芯片只能 3.3 V。
  • 陀螺仪和加速度计要做融合——别拿原始读数去控制转向。运动中的单次加速度计采样没法当作倾角使用。
  • 偏航无磁力计会漂;MPU-6050 管俯仰横滚和短期转弯,不管绝对罗盘航向。
  • 刚性水平安装;松动的传感器加滤波去不掉的振动。

引脚

紫色 GY-521 转接板就是大家说的“MPU-6050”。八脚,一般四个悬空。

引脚 名 作用 一般
1 VCC 电源。板稳压吃 3.3–5 V;裸芯片只 3.3 V Uno 上 5 V,ESP32 上 3.3 V
2 GND 地,和单片机共地 接上
3 SCL I²C 时钟 Uno 上 A5
4 SDA I²C 数据 Uno 上 A4
5 XDA 辅助 I²C 数据——替你读磁力计 悬空
6 XCL 辅助 I²C 时钟 悬空
7 AD0 地址选择。低(默认)=0x68,高=0x69 悬空,第二颗接高
8 INT 中断输出——数据就绪、运动、自由落体触发 悬空,不想轮询再用

AD0 正是让一条总线上能挂两颗 MPU-6050 成为可能的那个引脚,也正因为它,地址冲突在这里有了一个大多数传感器都不提供的简单解法。

接到 Arduino

MPU-6050 Arduino Uno ESP32 说明
VCC 5 V 3.3 V 板载稳压都吃
GND GND GND 必须和其他一切共地
SCL A5 GPIO 22 别外加上拉——板上已有 2.2 kΩ
SDA A4 GPIO 21 同上
AD0 — — 只第二颗接 VCC

在 5 V 的 Arduino 上有个微妙之处。GY-521 的上拉电阻接到它自己稳压出的 3.3 V 电源轨,所以 I²C 线的空闲电平是 3.3 V 而不是 5 V。ATmega328P 的高电平门限是 0.6 × Vcc = 3.0 V,3.3 V 刚好越过——只富余 0.3 V。实践中它工作可靠,而这也正是长线或嘈杂的 I²C 在这块板子上比在纯 5 V 板子上更早出问题的原因之一。

读它:要紧的寄存器

库驱动一辈子不见寄存器,但四个解释了几乎所有问题。

寄存器 地址 为什么要在意
PWR_MGMT_1 0x6B **芯片上电时处于休眠。**往这里写 0,否则所有读数都是零
WHO_AM_I 0x75 返回 0x68——验证接线正确的最快方法
GYRO_CONFIG 0x1B 满量程:±250、±500、±1000、±2000 °/s
ACCEL_CONFIG 0x1C 满量程:±2、±4、±8、±16 g
ACCEL_XOUT_H 0x3B 14 字节的起点:6 字节加速度、2 字节温度、6 字节陀螺仪——用一次突发读取全部读出

这两个配置寄存器决定你用哪个缩放系数去除原始计数,而配错它正是“我的角度大了四倍”最常见的来源:

量程设置 陀螺 LSB/°/s 量程设置 加速 LSB/g
±250 °/s(默认) 131 ±2 g(默认) 16384
±500 °/s 65.5 ±4 g 8192
±1000 °/s 32.8 ±8 g 4096
±2000 °/s 16.4 ±16 g 2048

选不爆表最小量程。平衡车很少超 ±250 °/s,窄量程白送 ±1000 °/s 四倍分辨率。

能跑的最小代码

无库——全部就这些,值得跑一遍知道库替你干了什么。

#include <Wire.h>

const uint8_t MPU = 0x68;          // 0x69 if AD0 is tied high

void setup() {
  Serial.begin(115200);
  Wire.begin();

  Wire.beginTransmission(MPU);
  Wire.write(0x6B);                 // PWR_MGMT_1
  Wire.write(0);                    // wake up — the chip boots asleep
  Wire.endTransmission(true);
}

void loop() {
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);                 // ACCEL_XOUT_H
  Wire.endTransmission(false);      // repeated start, do not release the bus
  Wire.requestFrom(MPU, (uint8_t)14);

  int16_t ax = Wire.read() << 8 | Wire.read();
  int16_t ay = Wire.read() << 8 | Wire.read();
  int16_t az = Wire.read() << 8 | Wire.read();
  Wire.read(); Wire.read();         // temperature, discarded here
  int16_t gx = Wire.read() << 8 | Wire.read();
  int16_t gy = Wire.read() << 8 | Wire.read();
  int16_t gz = Wire.read() << 8 | Wire.read();

  // Default ranges: +/-2 g and +/-250 deg/s
  float pitch = atan2(ax, sqrt((float)ay * ay + (float)az * az)) * 57.2958f;
  float rateY = gy / 131.0f;        // deg/s

  Serial.print(pitch); Serial.print(' '); Serial.println(rateY);
  delay(20);
}

故意没做的两件事:不融合两传感器,不减偏置。都是下一步。

标定:最重要的二十行

静止时陀螺仪读数并不是零,而是一个小而相当稳定的偏置;由于你是靠积分陀螺仪来得到角度的,这个偏置也会被积分成一个持续增长的误差。只要 2 °/s 的偏置没被修正,一分钟后就是 120° 的漂移。

传感器纹丝不动平均几百采样,再减:

long sum = 0;
for (int i = 0; i < 500; i++) { sum += readGyroY(); delay(3); }
const float gyroBias = sum / 500.0f;   // subtract from every later reading

手册不写的三条实践:

  • **每次上电做,不是一次。**陀螺偏置随温度走,跑十分钟的车不是冷标定的车。
  • **“静止”就得是真的静止。**在有人靠着桌面时做标定,那点运动会永久地被烙进偏置里。
  • **加速度计需要的是水平参考,不是零。**朝下的那个轴应该读出 1 g。如果读数是 0.97 g,那就是你的缩放误差,表现出来就是倾角总是差那么两三度。

为什么一个传感器永远不够

两个传感器的失效方式恰好相反,这正是把它们融合起来能做得这么好的原因。

加速度计 陀螺仪
量 线加速度——静止即重力 转速
倾角靠 重力矢量三角 转速对时间积分
短期 充满噪声——每次颠簸和振动也都是加速度 极好,平滑,跟手
长期 平均而言正确——重力不会漂移 随偏置积分而漂移,没有上界
失效 车加速,“下”不再是下 永远,慢慢

互补滤波取两者各自擅长的那一半——短期信陀螺仪,同时让加速度计慢慢把估计值拉回真实值:

angle = 0.98f * (angle + rate * dt) + 0.02f * accelAngle;

这个 0.98 决定了交叉频率。取更大的值会更长时间地相信陀螺仪,更平滑但漂移更多;取更小的值则会去跟随加速度计的噪声。互补滤波教程讲的是怎么有意识地选它,而不是凭感觉。

故障排查

现象 可能原因 解决
读数全精确零 芯片还睡着 Wire.begin() 后 PWR_MGMT_1(0x6B)写 0
总线扫描什么都找不到 用了手册上的 8 位地址 手册里的 0xD0 是 0x68 左移一位的结果——用 0x68
两颗传感器只出现一颗 两颗都在默认地址上 把其中一块板的 AD0 接到 VCC,让它以 0x69 应答
角度大了 4 倍或 8 倍 缩放系数和配置的量程不匹配 让除数与 GYRO_CONFIG/ACCEL_CONFIG 匹配
静止时角度也持续漂移 陀螺仪偏置没标定 开机时静止采样 500 次取平均后减掉
行驶中倾角乱跳 直接用了原始加速度计角度 和陀螺仪融合;运动中的车,“下”已经不是下
运行一会儿后读数冻结 I²C 总线被锁死 检查上拉电阻;加一段总线恢复流程,把 SCL 打九下时钟
航向永远慢慢转 偏航无绝对参考 正常——偏航要磁力计或别的绝对源
约 1 kHz 以上振动时读数嘈杂 刚性安装把电机振动传了过来 软性安装传感器;通过 CONFIG(0x1A)启用片上 DLPF

MPU-6050 还是更新的?

MPU-6050 在原厂已经停产,却仍然是入门机器人里用得最广的 IMU,因为它便宜、到处都有文档,而且每个库都支持它。

零件 轴 不同 选它的时机
MPU-6050 6 默认;例程海量 学习、平衡、第一个 IMU 项目
MPU-9250 9 加磁力计,偏航有绝对参考 要罗盘航向不只转速
ICM-20948 9 MPU-9250 目前在产的继任型号 需要 9 轴且要求长期供货的新设计
BNO055 9 片上完成融合,直接输出姿态 你只想要一个角度而不想写滤波器——代价是几倍的价钱
LSM6DS3 6 型号较新,仍在生产,噪声更低 MPU-6050 断货时可以直接替换的升级件

老实话:车要俯仰横滚——平衡要的——MPU-6050 完全够,融合自己写一遍是好事。要绝对航向,六轴滤波到死没有,要磁力计。

探索关联图谱

用到它的搭建

包含 MPU-6050 惯性测量单元 的项目、学习路线和模拟器。

问题

MPU-6050 惯性测量单元 常见问题

MPU-6050 传感器是什么?

MPU-6050 是一个装在小型转接板上的六轴惯性测量单元(IMU)。它集成了三轴陀螺仪和三轴加速度计,因此机器人能感知自己倾斜了多少、转得多快、加速度如何——这一切都通过 I²C 总线读取。

MPU-6050 是怎么工作的?

它内部有两个传感器。加速度计测量线加速度——静止时测到的只有重力,这就告诉机器人哪个方向是下,也就是它的倾角。陀螺仪测量绕各轴的转速。你通过 I²C 读取两者,再把它们融合起来,得到任何一个单独都给不了的稳定姿态。

MPU-6050 用来做什么?

用来感知机器人自身的运动——让自平衡机器人保持直立,估计航向并在两次编码器读数之间改进里程计,以及检测机器人被推倒、拿起或摇晃。它感知的是机器人本身,而不是周围的世界,所以它是测距传感器的补充,而不是替代品。

MPU-6050 怎么标定?

静止时,陀螺仪和加速度计都会读出很小的非零偏移。标定方法是让传感器完全静止,对几百个采样取平均,然后从之后的每一个读数中减去这些偏移。陀螺仪尤其需要这样做——未经标定的偏置,正是让估计角度漂移最快的原因。

MPU-6050 怎么接到 ESP32 或 Arduino?

通过 I²C——把 SDA 和 SCL 接到板子的 I²C 引脚上,再接好电源和地。紫色的 GY-521 转接板带有稳压器,所以在 ESP32 上可以用 3.3 V 供电,在 Arduino Uno 上可以用 5 V 供电。然后由一个库替你读取陀螺仪和加速度计的寄存器。

延伸阅读

参考资料