← 返回传感器图鉴

MPU6050 六轴姿态传感器

最后更新 2026-06-20
⏱ 约 7 分钟 🟡 涉接线/强电
🛒 器材清单
器材数量参考
MPU6050 模块(GY-521)1

价格随渠道波动,以购买页实时为准。

想做一台自平衡小车,或者拿手一晃就能控制屏幕的手势遥控器,第一步都是一样的:板子得先知道自己"朝哪、怎么动"。地面是平的还是斜的、车身正在往左倒还是往右倒、手腕是抬起来还是甩出去——这些"姿态"和"运动"信息,靠按键和光线传感器都拿不到。MPU6050 就是干这件事的:一颗芯片里塞进六个传感器,三轴加速度计加三轴陀螺仪,把物体的姿态量化成数字,从 I2C 口源源不断送出来。它便宜、资料多,是几乎所有姿态入门项目的起点。

工作原理

MPU6050 把两类 MEMS 传感器合在一颗芯片里,加起来六个数据通道,所以叫"六轴"。

加速度计——测三个方向(X/Y/Z)的线性加速度,单位是 g(1g ≈ 9.8 m/s²)。关键点是它永远能感受到重力:板子静止平放时,Z 轴会读到约 +1g,X/Y 约 0g;把板子立起来,重力就会"分"到对应的轴上。利用这一点,静止时可以反算出板子的倾角(俯仰 pitch、翻滚 roll)。问题是:只要板子在动、有振动,加速度计读数里就混进了运动加速度和噪声,算出来的角度会跳来跳去、抖得厉害

陀螺仪——测三个轴的角速度(旋转有多快),单位是 °/s。它不受重力影响、对瞬间运动反应快、短时间内很平滑。但它给的是"角速度",要得到"角度"必须对时间做积分。积分有个致命毛病:每次的微小误差会一点点累加,板子明明没动,算出的角度却在缓慢爬升——这就是漂移。放几分钟,角度能漂出十几度。

两个传感器各有长短,正好互补:加速度计长期准(有重力当基准)但短期抖,陀螺仪短期稳但长期漂。把两者融合就能取长补短,输出一个既稳又准的姿态角。最简单的方案是互补滤波,一行式就能表达:

angle = 0.98 * (angle + gyro_rate * dt) + 0.02 * accel_angle;

意思是:以陀螺仪积分为主(占 98%,保证平滑),同时用加速度计算出的角度做小幅"拉回"(占 2%,防止漂移)。要更高精度可以上卡尔曼滤波,原理类似但用概率模型动态调权重。

芯片里还集成了一个 DMP(数字运动处理器):它能在板载硬件里直接把六轴融合成现成的姿态四元数/欧拉角,省掉主控算融合的功夫——代价是配置麻烦、库体积大,入门可以先用软件互补滤波理解原理。

MPU6050 通过 I2C 与主控通信。

接线

MPU6050 ESP32 说明
VCC 3.3V / 5V 模块(GY-521)自带稳压,5V 也行;裸芯片只能 3.3V
GND GND 共地
SDA GPIO21 I2C 数据,模块板载上拉,一般无需外加
SCL GPIO22 I2C 时钟
AD0 GND 或 3.3V 决定地址:接地=0x68(默认),接高=0x69

AD0 这一脚是关键:一条 I2C 总线想挂两颗 MPU6050,就把一颗的 AD0 接地(0x68)、另一颗接高(0x69)。模块上 AD0 通常默认下拉到地,所以不接也是 0x68。I2C 线如果接长了读数不稳,了解一下上拉电阻

完整代码

下面用 Adafruit MPU6050 库读原始六轴并换算,附带零偏校准说明。

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

Adafruit_MPU6050 mpu;

// 陀螺仪零偏(静止时的偏置),开机校准后填入
float gyroBiasX = 0, gyroBiasY = 0, gyroBiasZ = 0;

void calibrateGyro() {
  // 校准时务必让板子完全静止
  const int N = 500;
  float sx = 0, sy = 0, sz = 0;
  for (int i = 0; i < N; i++) {
    sensors_event_t a, g, t;
    mpu.getEvent(&a, &g, &t);
    sx += g.gyro.x; sy += g.gyro.y; sz += g.gyro.z;
    delay(3);
  }
  gyroBiasX = sx / N;   // 取静止平均当作零偏
  gyroBiasY = sy / N;
  gyroBiasZ = sz / N;
  Serial.printf("零偏校准完成: %.4f %.4f %.4f\n",
                gyroBiasX, gyroBiasY, gyroBiasZ);
}

void setup() {
  Serial.begin(115200);
  if (!mpu.begin()) {            // 默认地址 0x68
    Serial.println("找不到 MPU6050,检查接线和 AD0");
    while (1) delay(10);
  }
  mpu.setAccelerangeRange(MPU6050_RANGE_4_G);   // ±4g 量程
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);      // ±500°/s 量程
  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);   // 低通滤波带宽

  Serial.println("校准中,请保持静止...");
  calibrateGyro();
}

void loop() {
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);   // 库已换算好单位

  // 加速度单位 m/s²;陀螺单位 rad/s(这里减掉零偏)
  float gx = g.gyro.x - gyroBiasX;
  float gy = g.gyro.y - gyroBiasY;
  float gz = g.gyro.z - gyroBiasZ;

  Serial.printf("Acc[m/s2] X:%.2f Y:%.2f Z:%.2f | ",
                a.acceleration.x, a.acceleration.y, a.acceleration.z);
  Serial.printf("Gyro[rad/s] X:%.2f Y:%.2f Z:%.2f | ",
                gx, gy, gz);
  Serial.printf("Temp:%.1fC\n", temp.temperature);

  delay(50);
}

注意 setAccelerangeRange 在不同库版本里可能写作 setAccelerometerRange,以你装的库头文件为准。要直接拿到 pitch/roll 姿态角,可以换用 MPU6050_light 库,它内部已经做好互补滤波。

你应该看到什么

打开串口监视器(115200),先看到"校准完成"。然后:

  • 板子平放静止:Acc 的 Z 约 +9.8,X/Y 接近 0;三个 Gyro 都贴近 0(这就是零偏校准的效果)。
  • 慢慢把板子立起来:重力会从 Z 轴"转移"到 X 或 Y 轴,对应数值从 0 涨到约 ±9.8,Z 降到 0。
  • 快速转动板子:对应转动方向的 Gyro 值瞬间冲到几 rad/s,停下立刻回 0——这就是陀螺仪对运动的灵敏反应。
  • 拿手指弹一下板子:Acc 会出现一个尖峰然后回落。

如果静止时 Gyro 不回 0,说明零偏没校准好,重新静止上电再来一次。

读数解读 / 校准

量程要按需求选。 加速度计有 ±2/4/8/16g 四档,量程越小分辨率越高:测倾角、计步选 ±2g 或 ±4g;测剧烈冲击、无人机才用 ±16g。陀螺仪有 ±250/500/1000/2000°/s 四档:缓慢姿态用 ±250 或 ±500,高速旋转(穿越机)才用 ±2000。

零偏校准是必做项。 MPU6050 出厂没有完美归零,静止时陀螺仪也会读到一个非零的固定偏置。开机时让板子静止几百毫秒、取平均值当作偏置,之后每次读数都减掉它——上面代码的 calibrateGyro() 就是干这个。不校准,积分出来的角度会飞快漂走。

算倾角的思路。 静止时用加速度三轴反三角函数就能直接出倾角:pitch = atan2(ay, az)roll = atan2(ax, az)(单位弧度,乘 57.3 转成度)。但只要板子在动,这个值就抖,必须和陀螺仪融合(见进阶)。

选型 / 避坑

型号 轴数 能算航向角(yaw) 定位
MPU6050 6 轴(加速度+陀螺) 不能(会漂) 入门、自平衡、计步,够用
MPU9250 9 轴(多磁力计) 能(磁力计当指南针) 要稳定航向、无人机定向

核心区别:MPU6050 算 pitch/roll 没问题,但算不准 yaw(航向角)——因为绕重力轴转动时加速度计感知不到,没有外部基准,yaw 只能靠陀螺积分,必漂。要稳定的航向角,得用带磁力计的 MPU9250 这类九轴方案,磁力计相当于电子指南针给 yaw 一个绝对基准。入门和大多数自平衡、姿态项目,MPU6050 完全够用。

🚧 避坑

读数"飘"是最常见的坑,几乎都是两件事没做:① 没做传感器融合——加速度算的角度天生抖,陀螺积分天生漂,必须用互补滤波或卡尔曼把两者融合;② 没做零偏校准——开机静止取平均当偏置,每次读数减掉。这两步缺一不可,单独用任何一个传感器都得不到稳定姿态角。

故障排查

现象 原因 处理
I2C 扫不到设备 接线错 / AD0 状态不确定 扫 0x68 和 0x69 两个地址都试;查 SDA/SCL 是否接反
begin() 返回失败 供电不足或接触不良 确认 VCC 有 3.3V,焊点牢固,杜邦线插紧
读数全是 0 或固定值 芯片没唤醒 / 没正确 init 确认调了 mpu.begin() 成功;有的库需手动退出睡眠模式
读数乱跳、无规律 量程设太大 / 没设低通滤波 调小量程、加 setFilterBandwidth
角度缓慢漂移 没融合 / 没校零偏 加互补滤波 + 静止零偏校准
静止时 Gyro 不为 0 零偏没减掉 重新静止校准并在读数里减偏置

进阶 / 变体

把互补滤波加进 loop(),就能输出稳定的 pitch/roll:

float pitch = 0;
unsigned long lastT = 0;

void loopFusion() {
  sensors_event_t a, g, t;
  mpu.getEvent(&a, &g, &t);

  unsigned long now = millis();
  float dt = (now - lastT) / 1000.0; lastT = now;

  // 加速度算的角度(长期准、短期抖)
  float accPitch = atan2(a.acceleration.y, a.acceleration.z) * 57.3;
  // 陀螺积分(短期稳、长期漂),减零偏
  float gyroRate = (g.gyro.x - gyroBiasX) * 57.3;

  // 互补滤波:陀螺为主、加速度拉回
  pitch = 0.98 * (pitch + gyroRate * dt) + 0.02 * accPitch;

  Serial.printf("pitch: %.1f deg\n", pitch);
}

把这个稳定的 pitch 角接进 PID 控制器,输出给电机,就是自平衡小车的核心——传感器告诉车身"我正往哪边倒、倒多快",PID 决定电机怎么反向加速把它扶正。

典型应用

  • 自平衡小车:用 pitch 角喂给 PID,是 MPU6050 最经典的去处。
  • 手势识别 / 翻转检测:靠加速度和角速度的特征判断手腕动作。
  • 计步:检测加速度的周期性波峰,是运动手环的基础。
  • 云台增稳:实时测姿态,反向驱动电机抵消抖动。
  • 姿态显示 / 无人机飞控:融合后的姿态角是飞控闭环的输入。

小结 · 相关

MPU6050 把六轴 IMU 做到了几块钱,记住三件事就能用好:选对量程、开机做零偏校准、姿态角必须融合(互补滤波或卡尔曼)。它只能稳算 pitch/roll,要航向角换 MPU9250。

参数以上方 datasheet 为准;本页公开资料整理,接线与代码请结合实物验证。

📄 来源 / 自校链接

本文为公开资料整理,非亲测。关键参数与代码请结合实物与下列官方来源验证。

内容有错、看不懂、或想看下一期?告诉我们 →

本文为公开资料的学习整理,非亲测。涉接线/花钱/合规的步骤请结合实物与官方最新资料验证,风险自负。见免责声明