← 返回文章库

用 PCA9685 一次驱动 16 个舵机:机械臂和六足的标准做法

最后更新 2026-06-20
⏱ 约 18 分钟 🟡 涉接线/强电
你将学到
  • 说清单片机直接驱动多舵机的两个硬伤:PWM 通道不够、供电被拖垮
  • 用 Adafruit_PWMServoDriver 封装 setAngle(channel, deg) 并让多个舵机依次扫动
  • 正确接线:V+ 接独立 5V、VCC 接 3.3V 逻辑、共地、I2C 地址 0x40
  • 通过焊接地址跳线把多块板级联到 32 路以上

你照着教程把一个舵机接到 ESP32,调好角度,挥得很爽。然后你想做个机械臂——底座转、大臂、小臂、手腕翻转、手腕旋转、夹爪,六个舵机。你刚接到第三个,问题就来了:引脚开始不够分,供电一上电主控直接重启,舵机抖得像帕金森。

这不是你接错了,是单片机直接扛多舵机这条路本来就走不通。这篇我们把它讲透,然后换成机械臂、六足、人形这些项目的标准做法:PCA9685。

如果你还没搞清楚单个舵机是怎么被 PWM 控制角度的,先去看 舵机入门,那篇讲了 50Hz、脉宽对应角度这些基础,这里不重复。

先把单舵机的控制原理捋一遍

舵机靠一路 PWM 信号定角度,关键就两个数:

  • 频率 50Hz:也就是每 20ms 一个周期。这是模拟舵机的标准刷新率,几乎所有 SG90、MG996R 都吃这个。
  • 脉宽 0.5ms ~ 2.5ms:高电平持续多久决定转到哪。0.5ms 大致对应 0°,1.5ms 对应 90°(中位),2.5ms 对应 180°。不同舵机的实际边界会差一点,后面校准时再说。

舵机内部有个电位器一直在量当前角度,比对你给的脉宽,差多少就往哪转。所以你只要持续喂对的脉宽,它就稳在那个角度。

一路 PWM 控一个舵机,听起来很简单。问题在「一路」这两个字。

为什么六个舵机就把 ESP32 逼到墙角

直接用主控的 ledcWrite 之类去生成多路舵机 PWM,你会同时撞上两堵墙。

第一堵墙:PWM 通道不够,而且精度被稀释。 ESP32 的 LEDC 外设有 16 个通道不假,但它们共享有限的定时器,你要给每路都配 50Hz 这种低频、又要保证脉宽分辨率,排起来很别扭。换成 Arduino UNO 这种,能稳定输出舵机 PWM 的引脚就更少了。控两三个还行,控六个、十二个,代码和定时器分配会乱成一锅粥。

第二堵墙,也是更致命的:供电。 舵机不是省油的灯。一个 MG996R 空载几十毫安,一旦带载、尤其是堵转(转不动还在使劲),瞬间电流能冲到 1A 以上。六个舵机同时启动或者同时堵转,瞬时电流好几安培。

如果你图省事,把舵机的电源线也接到主控的 5V 输出上——主控板上那个稳压器根本供不出这么大电流,电压被瞬间拉垮,主控欠压复位。现象就是:你一让多个舵机动,板子就重启。 很多人卡在这里查了一晚上代码,其实一行代码都没错,是电不够。

这两堵墙,PCA9685 一块板子全帮你拆掉。

PCA9685 是什么,凭什么能解决

PCA9685 是一颗 16 路 PWM 驱动芯片,Adafruit 把它做成了一块带舵机排针的现成模块。它干三件事:

  1. 用 I2C 接管控制。主控只需要两根线(SDA、SCL)跟它通信,发个命令说「第 3 路,脉宽设成 X」,剩下的它自己管。两根线就够,不再跟你抢引脚。关于 I2C 这套主从、地址、上拉的机制,可以看 I2C 总线原理
  2. 板载振荡器自己生成 16 路 PWM。芯片内部有个 25MHz 时钟,配上 12 位分辨率(0~4095),独立算出 16 路各自的 PWM 波形,稳定输出。你设好一次频率(50Hz),16 路一起按这个频率走。PWM 是怎么用占空比表达「强度/时长」的,见 PWM 原理
  3. 舵机电源走独立的 V+,跟逻辑电源彻底分开。板子上那排舵机座的正极,统一接到一个叫 V+ 的端子,你从这里灌入一个独立的 5V 大电流电源。舵机吃电吃的是这路,跟主控的供电井水不犯河水。主控重启的问题,根上解决。

一句话:PCA9685 把「控制」和「供电」分了家。 控制走 I2C 两根线,供电走独立 5V,主控只负责动嘴皮子发命令。

接线:四类线,别接错

这是整个项目最容易翻车的地方,照着来。

控制侧(主控 ↔ PCA9685):

PCA9685 引脚 接到 ESP32 作用
VCC 3.3V 芯片逻辑供电,不是给舵机的
GND GND 逻辑地
SDA GPIO21(以你板子丝印为准) I2C 数据
SCL GPIO22(以你板子丝印为准) I2C 时钟

SDA/SCL 的具体引脚号每块 ESP32 开发板不一样,以板上丝印为准。大多数 ESP32 默认 SDA=21、SCL=22。

供电侧(独立 5V → PCA9685):

  • V+ 端子接独立 5V 电源正极(比如一个 5V/3A 以上的电源适配器,或者足够的电池组)。这路专门喂舵机。
  • 这个 5V 电源的地,必须和 ESP32 的 GND 接到一起——这就是「共地」。不共地,I2C 的电平就没有共同参考,命令发不进去,舵机不动。
  • 舵机的三根线(信号/正/负)直接插 PCA9685 对应通道的排针,有防呆方向,黑(棕)线朝 GND 那一侧。

I2C 地址默认 0x40。板子背面有六个地址跳线(A0~A5),一块不焊就是 0x40,后面级联会用到。

记住这条铁律:舵机的电,永远从 V+ 那路独立电源来,绝不从主控取。

⚠️ 安全

舵机供电务必分离,且共地。 三个安全点焊死在脑子里:① 舵机电源走独立 5V,容量按「舵机数 × 单个堵转电流」估,六个 MG996R 至少备 5V/6A;② 独立电源地和主控地必须共地,否则不工作甚至烧逻辑;③ 上电顺序先接好地线再上电,带电插拔舵机容易打火、冲坏 PCA9685。堵转电流很大,长时间堵转会烧舵机也会烧驱动板,代码里别让舵机硬顶到机械极限。

完整可跑代码:让多个舵机依次扫动

库用 Adafruit 的 Adafruit_PWMServoDriver(Arduino 库管理器搜 "Adafruit PWM Servo Driver" 装上)。核心是:初始化时 setPWMFreq(50),再封装一个 setAngle(channel, deg) 把角度翻译成 PCA9685 认的 0~4095 计数值。

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// 默认 I2C 地址 0x40,不传参数也行,这里写明更清楚
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);

// 舵机脉宽边界,单位微秒。0.5ms=500us 对应 0°,2.5ms=2500us 对应 180°
// 不同舵机会有偏差,先用这组通用值,后面"角度不准"再校准
const int SERVO_MIN_US = 500;
const int SERVO_MAX_US = 2500;
const int SERVO_FREQ   = 50;     // 50Hz,周期 20ms = 20000us

// 你要驱动的舵机接在哪几个通道
const int CHANNELS[] = {0, 1, 2, 3, 4, 5};
const int NUM_SERVOS = sizeof(CHANNELS) / sizeof(CHANNELS[0]);

// 把"微秒脉宽"换算成 PCA9685 的 12 位计数值(0~4095)
// 一个周期 20000us 对应满量程 4096 个计数
int usToTicks(int us) {
  return (int)((long)us * 4096 / 20000);
}

// 核心封装:给某个通道设角度
void setAngle(int channel, int deg) {
  if (deg < 0)   deg = 0;
  if (deg > 180) deg = 180;
  // 角度线性映射到脉宽
  int us = map(deg, 0, 180, SERVO_MIN_US, SERVO_MAX_US);
  pwm.setPWM(channel, 0, usToTicks(us));
}

void setup() {
  Serial.begin(115200);
  Wire.begin();              // ESP32 默认 SDA=21 SCL=22,需要别的引脚:Wire.begin(SDA, SCL)
  pwm.begin();
  pwm.setPWMFreq(SERVO_FREQ); // 全部 16 路统一 50Hz

  // 上电先全部回中位,避免猛地弹一下
  for (int i = 0; i < NUM_SERVOS; i++) {
    setAngle(CHANNELS[i], 90);
  }
  delay(500);
  Serial.println("PCA9685 ready");
}

void loop() {
  // 让每个舵机依次扫一遍:0° -> 180° -> 90°
  for (int i = 0; i < NUM_SERVOS; i++) {
    int ch = CHANNELS[i];
    Serial.printf("舵机通道 %d 扫动\n", ch);

    setAngle(ch, 0);    delay(400);
    setAngle(ch, 180);  delay(400);
    setAngle(ch, 90);   delay(400);
  }
  delay(1000);
}

几个值得你停下来看一眼的地方:

  • usToTicks() 把脉宽换算成计数。一个 20ms 的周期被切成 4096 份,你的脉宽占多少微秒,就换算成多少份。50Hz 是这个换算的前提,所以 setPWMFreq(50) 一定要先调对。
  • setAngle 里做了 0~180 的钳位,防止你手滑传个 200° 把舵机顶到机械极限。
  • 上电先全回中位再开始,是个好习惯——不然舵机可能从未知位置猛弹到目标,机械臂上电就甩一下,容易撞坏结构。

你应该看到什么

烧录后打开串口监视器(115200):

  • 看到 PCA9685 ready,说明 I2C 通了、板子认到了。
  • 六个(你接了几个就几个)舵机一个接一个地动:每个先转到 0°,停一下,转到 180°,停一下,回到 90°,然后轮到下一个。
  • 串口同步打印「舵机通道 X 扫动」。
  • 主控不重启——这是验证供电分离成功的关键信号。如果一动就重启,直接跳到下面排查表第三行。

如果舵机动作干脆、到位、不抖,主控稳稳没重启,恭喜,多舵机驱动这关你过了。

故障排查表

现象 最可能的原因 怎么修
舵机抖动、嗡嗡响、不停微动 供电电流不足或电压不稳 / 共地不良 换更大电流的独立 5V 电源;检查独立电源地与主控地是否真共地;线材太细换粗
舵机完全不动 V+ 没接独立电源 / 接到了 VCC V+ 必须接 5V 独立电源,VCC 只是 3.3V 逻辑供电,两者别搞混
一让多个舵机动主控就重启 舵机在从主控取电,把主控拖欠压 舵机供电走独立 V+,绝不从主控 5V 取;确认共地
角度不准、0° 不到位或 180° 过头 该型号舵机脉宽边界和 500/2500us 不一致 微调代码里 SERVO_MIN_US/SERVO_MAX_US,逐个试到位
串口报 I2C 找不到设备 / 卡住 SDA/SCL 接反、没接上拉、地址不对 核对 SDA/SCL 引脚和板子丝印;I2C 需要上拉(模块通常自带),见 上拉电阻;用 I2C 扫描确认地址是不是 0x40
上电瞬间舵机猛甩一下 上电时通道是随机/历史值 setup 里先把所有通道设到中位再开始动作

两个变体玩法

变体一:级联到 32 路、48 路。 一块 PCA9685 是 16 路,做人形或者多足明显不够。它支持级联:把第二块板的地址跳线 A0 焊上,它的地址就变成 0x41,代码里再 new 一个 Adafruit_PWMServoDriver(0x41) 就行。两块板的 SDA/SCL 并在同一条 I2C 总线上,各管各的 16 路。地址跳线 A0~A5 六位,理论上能挂到 62 块、近千路,远超你需要。

Adafruit_PWMServoDriver board1 = Adafruit_PWMServoDriver(0x40); // 通道 0~15
Adafruit_PWMServoDriver board2 = Adafruit_PWMServoDriver(0x41); // 焊了 A0
// 两块都要 begin() 和 setPWMFreq(50)

变体二:做一张「扫动表情表」。 把一组动作写成数组,让多个舵机按节奏一起动,做出像呼吸、点头、招手这种连贯姿态。比如把眼睛、嘴、头三个舵机的目标角度按帧排成表,loop 里按帧推进,一个机器人小表情就出来了。这其实就是关键帧动画的雏形,小智那类桌面机器人的「卖萌」动作就是这么做的,可以参考 小智的运动控制

动手挑战:两个舵机协调做抓取

光让舵机一个个轮流动太初级。给你一个真实场景的挑战:

用两个舵机模拟一次抓取。 一个当「手臂下压」(通道 0),一个当「夹爪开合」(通道 1)。要求动作有先后、协调,像真的去夹一个东西:

  1. 初始:手臂抬起(0°)、夹爪张开(180°)。
  2. 手臂缓慢下压到 90°——注意是「缓慢」,别一步到位。试试用 5° 为步长、每步 delay 20ms 的方式插值,让动作平滑。
  3. 到位后,夹爪合拢(180° → 60°),夹住。
  4. 手臂再抬回 0°,把「东西」提起来。
  5. 停 1 秒,松爪、复位,循环。

做完你会发现两个新问题,刚好是下一阶段要解决的:一是怎么让动作更顺滑(插值/缓动),二是手臂末端到底停在空间哪个点——这就不是「设角度」能直接回答的了,得算坐标。

小结·下一步

回头看,多舵机这关的核心就一句话:把控制和供电分家。控制交给 PCA9685 走 I2C 两根线,供电交给独立 5V 走 V+,主控只发命令。抖动、不动、重启这些坑,根上几乎都是供电和共地的问题,记住排查表前三行能帮你省下好几个晚上。

你现在能让一堆舵机听话地各就各位了。但机械臂真正的难题还没碰:你想让夹爪精确到达空间里某个 (x, y, z) 点,而不是手动一个个试角度——这需要把「末端位置」反算成「每个关节的角度」,也就是逆运动学。下一篇我们就啃这块硬骨头:机械臂运动学

📄 来源 / 自校链接

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

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

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