☰
树莓派舵机抖动解决:PCA9685硬件PWM稳定控制指南
2026/9/28 14:21:25 网站建设 项目流程

树莓派玩舵机,很多人第一步就栽在抖动上。手头的SG90接上去,要么嗡嗡作响,要么角度飘忽不定,更别提让多个舵机同时稳定工作了。最初我也和大多数人一样,直接用树莓派的GPIO输出PWM波形,结果就是反复折腾、反复失望。后来换成PCA9685方案,才算真正把舵机控制这件事做踏实了。这篇文章就把我踩过的坑、验证过的参数、以及最终稳定运行的完整方案整理出来,希望能让后来者少走弯路。

1. 树莓派直接驱动舵机为什么抖:先认清问题根源

先说结论:树莓派本身并不是一个适合直接输出舵机PWM的设备,抖动是它的软肋。

1.1 Linux不是实时系统,软件PWM天然有抖动风险

树莓派跑的是完整版Linux系统,它不是一个实时操作系统。Linux在调度普通用户进程时,会按照时间片轮流分配CPU资源,同时还有大量的后台进程、系统中断、I/O操作在争抢CPU。当你用RPi.GPIO库的PWM类去生成PWM波形时,这类软件PWM的实现方式,本质上是靠CPU去“翻转”引脚电平:

# 示意代码:软件PWM的典型实现思路 import RPi.GPIO as GPIO import time GPIO.setmode(GPIO.BCM) GPIO.setup(18, GPIO.OUT) p = GPIO.PWM(18, 50) # 50Hz,周期20ms p.start(7.5) # 占空比7.5%,对应约1.5ms脉宽 # 此时CPU需要在每个周期内不断切换引脚状态

这种方式的致命问题在于:如果某个瞬间系统调度器把资源让给了其他进程,或者发生了一次中断,PWM波形的边沿就会偏移。舵机的核心控制逻辑,是通过脉冲宽度(而不是频率)来确定转角的。当脉宽出现微秒级的波动,反应到舵机输出轴上,就是肉眼可见的抖动、啸叫、甚至角度漂移。

我在树莓派4B上做过一个粗略的实测:系统负载较高时(同时跑着SSH传输、桌面环境、几个Python脚本),软件PWM输出的波形周期抖动能达到正负几百微秒。对于周期仅为20000微秒(20ms)的舵机控制信号来说,这个抖动量已经非常致命。

1.2 舵机需要的不是高频率,而是脉冲宽度的稳定性

很多刚开始接触舵机的朋友有个误区,以为要“提高PWM频率”来减少抖动。但标准舵机的控制频率其实很固定,一般就是50Hz,也就是每20ms一个周期。在这个周期内,高电平持续的时间(脉宽)才决定了舵机转动的角度。

以常用的SG90舵机为例:

目标角度脉宽范围(典型值)对应占空比(50Hz下)
0度0.5ms2.5%
90度1.5ms7.5%
180度2.4ms ~ 2.5ms12% ~ 12.5%

问题在于,如果脉宽本身在1.5ms附近上下波动0.2ms,舵机会以为你要让它不断改变角度,自然就会出现高频颤动。所以,控制舵机的核心目标不是“更快的频率”,而是“更稳定的脉宽”。这就是为什么要用专用PWM生成芯片。

1.3 为什么硬件PWM生成器是正解

PCA9685是一块I2C接口控制的16通道12位PWM生成芯片,它的内部有一个独立的25MHz晶振,PWM波形完全由芯片硬件产生,与树莓派的CPU调度无关。树莓派要做的事情,只是通过I2C总线告诉芯片“第几路、占空比多少”,然后芯片就会按照设定值持续输出精确的波形。

打个比方:软件PWM相当于你在工位上手动搬零件,只要手头有别的事情,搬零件的节奏就会乱;而PCA9685相当于一台自动化流水线,你只需要设定好参数,它自己就能按节拍精确运行。把“实时产生波形”这个任务从非实时的Linux系统中彻底剥离出去,才是告别抖动的最根本解。

顺带提一个细节:树莓派的GPIO引脚输出高电平是3.3V,而很多舵机的标准信号电平是5V。PCA9685的逻辑供电引脚VCC接3.3V或是5V其实都有人用,但具体到舵机信号线,PCA9685的SDA/SCL走I2C用3.3V逻辑没问题,而输出到舵机的信号电平取决于它的VCC引脚。后面我会详细讲接线,这里先记住:硬件PWM生成器不仅解决了抖动问题,还顺便解决了电平匹配问题。

2. PCA9685接线前的准备:引脚、地址与电源方案

PCA9685模块在淘宝上很常见,大多是蓝色或绿色的16路舵机驱动板,上面除了芯片本体,还有一个220V转5V的电源端子、I2C引脚的排针、以及16组三针接口。接线本身不难,但有几个细节直接决定了整个系统能不能稳定工作。

2.1 PCA9685的引脚功能与典型接线

模块上的关键引脚如下:

  • VCC:芯片逻辑电源,常见模块可接3.3V或5V,建议接5V。
  • GND:芯片地线,必须和树莓派共地。
  • SDA:I2C数据线,接树莓派GPIO2(BCM编号)。
  • SCL:I2C时钟线,接树莓派GPIO3(BCM编号)。
  • OE:输出使能引脚(低电平有效),模块上一般有跳线帽把它接到GND,让输出始终开启。
  • V+:舵机电源输入端子,接外部5V电源正极。
  • 下方GND端子:舵机电源负极,同时也要和树莓派共地。

我推荐的标准接线是:

树莓派 3.3V -> PCA9685 VCC 树莓派 GPIO2 -> PCA9685 SDA 树莓派 GPIO3 -> PCA9685 SCL 树莓派 GND -> PCA9685 GND PCA9685 GND -> 外部5V电源负极 PCA9685 V+ -> 外部5V电源正极 舵机 信号线 -> PCA9685 某一路OUT口的S(信号)引脚 舵机 电源线 -> 外部5V电源正极(或V+端子扩展) 舵机 地线 -> 外部5V电源负极

这里最容易忽略的是“共地”。如果外部舵机电源的GND和树莓派的GND没有连通,信号线的高电平参考地就不一致,轻则舵机乱转,重则烧毁信号引脚。共地是整个系统稳定运行的第一前提。

2.2 地址配置与同总线挂载多板

PCA9685通过A0~A5六个引脚来配置I2C地址,默认状态下这六个引脚都悬空,地址是0x40。如果需要挂载更多板子(比如做六足机器人,要控制18个以上舵机),可以通过焊接或跳线把这几个引脚接到VCC或GND,组合出最多62个不同地址。

写地址时注意:A0对应地址二进制的最低位,A5对应最高位。比如把A0接到VCC,其它全部悬空,地址就会变成0x41。我在实际项目里的习惯是,第一块板保持0x40,第二块板把A0接高,这样后续代码里维护起来很清晰,不容易出错。

2.3 电源是这个项目最容易翻车的地方

如果一定要说这个项目里哪个环节最容易出问题,我首选电源,而不是接线。

舵机对电源的要求很苛刻。以SG90来说,空载电流大概在100mA到200mA之间,启动瞬间或堵转时会冲到500mA以上。而像MG996R这种大扭矩舵机,堵转电流可以达到2A甚至更高。如果用树莓派的5V引脚给舵机供电,瞬间的电流拉低电压,轻则导致系统重启,重则损伤板子。

正确的做法是:外部5V电源单独给舵机供电,而且电源电流余量要足够。我的经验是用5V 5A以上的开关电源,或者3S锂电池降压到5V,同时给电源加上一个大电容做蓄能缓冲。

还有一个很多人不知道的点:PCA9685的V+引脚只是舵机电源的汇流排,不需要从树莓派取电。如果只接一两个小舵机,用树莓派的5V引脚勉强能跑,但一旦舵机数量超过两个,或者用了大扭矩舵机,立刻就能体会到什么叫“电源崩溃”。老老实实外接电源,才是稳定运行的开始。

3. 脉冲转换成舵机角度的底层逻辑,以及PCA9685如何实现高精度

这一节不写代码,先把原理彻底讲透。只有理解了PCA9685内部是怎么工作的,后面遇到问题才能准确排查。

3.1 舵机控制标准:20ms周期里的脉宽语言

几乎所有模拟舵机的控制逻辑都一样:每20ms(对应50Hz)接收一个脉冲,脉冲的高电平时间决定了舵机输出轴的位置。高电平持续0.5ms,舵机转到一个极限位置;高电平持续2.5ms,舵机转到另一个极限位置;1.5ms左右,舵机居中。

这个“脉宽-角度”关系在舵机内部是靠一个比较电路实现的。舵机内部的电位器会反馈当前轴位置对应的电压,控制芯片把反馈电压和输入脉宽对应的目标电压做比较,然后驱动电机转动,直到两者相等。换句话说,舵机是一个典型的闭环位置控制系统。它非常依赖脉冲宽度的准确性,如果输入脉宽一直在微秒级抖动,舵机的比较电路会不断看到“变化的目标值”,结果就是不断追着动,表现出来就是抖动和嗡嗡声。

3.2 PCA9685的12位分辨率与占空比计算

PCA9685的每路PWM输出具有12位分辨率,也就是占空比可以在0到4095之间调节。在设置PWM频率后,芯片会计算出一个周期对应的寄存器值,我们写入的是“高电平持续的时间”在这个周期中的比例。

具体来说,PCA9685的每个通道有两个关键寄存器:ON(高电平起始时刻)和OFF(高电平结束时刻)。当ON设为0,OFF设为某个数值N时,输出在第0个计数点拉高,在第N个计数点拉低。N的最大值是4095,对应整个周期。

如果设定PWM频率为50Hz(周期20ms),那么每个计数单位对应的时间大约是:

20ms / 4096 ≈ 4.88微秒

也就是说,要输出1.5ms的脉宽,需要写入的OFF值大约是:

1.5ms / (20ms / 4096) = 1.5 / 20 × 4096 = 307.2 ≈ 307

3.3 从脉宽到代码数值的换算与角度标定

不同舵机的脉宽范围有差异,精确的标定需要实测。但通用做法是这样的:先把角度范围线性映射到脉宽范围,再把脉宽范围映射到PCA9685的寄存器值。

目标角度angle(假设0°到180°)对应的脉宽pulse_width_us(微秒)可以用线性插值计算:

pulse_width_us = min_pulse_us + (angle / 180) × (max_pulse_us - min_pulse_us)

然后换算成PCA9685的OFF值:

pwm_value = int(pulse_width_us / 1000000 × pwm_freq × 4096)

其中pwm_freq是PWM频率(50Hz)。需要特别注意,PCA9685的寄存器位宽是12位,写入值如果超过4095需要截断到4095。

我在实际项目中,会把舵机先调到0°,记录一个稳定不抖的脉宽值;再调到180°,记录另一个值。这样得到的min_pulse_us和max_pulse_us才是这台舵机真正需要的参数。不同批次的舵机,甚至同一批次不同个体,数值都会略有差异。为了省事,很多人直接套用库默认值,结果舵机转不到底或者卡死限位,这些坑提前说出来,后面踩的时候就有数了。

4. 实战代码:从扫描I2C到控制16路舵机

原理清楚了,下面直接上代码。项目里的控制器是树莓派4B,系统是Ubuntu 22.04(也可以直接用树莓派OS,步骤几乎一样),Python版本3.10。整个过程分为四步:检查I2C设备、安装依赖库、写出控制逻辑、验证波形和角度。

4.1 环境准备与依赖安装

第一次接线之前,先把I2C功能打开。在树莓派OS里,执行sudo raspi-config,选择Interface Options,再选择I2C并启用。Ubuntu系统下,需要在/boot/firmware/config.txt(树莓派OS是/boot/config.txt)里添加一行:

dtparam=i2c_arm=on

然后重启。重启后安装必要的工具包:

sudo apt update sudo apt install -y i2c-tools python3-pip

用i2cdetect扫描总线,确认树莓派能发现PCA9685:

sudo i2cdetect -y 1

正常情况下,会在0x40位置看到一个设备编号。如果什么都没显示,优先检查接线,尤其是SDA和SCL有没有接反,VCC有没有供电。

0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

看到0x40,说明硬件链路已经通了。

4.2 ServoKit方式:最短路径

Adafruit官方提供了CircuitPython库,封装得很完善,日常使用强烈推荐。先安装:

sudo pip3 install adafruit-circuitpython-servokit

控制代码非常简洁:

from adafruit_servokit import ServoKit # 初始化:默认地址0x40,16通道 kit = ServoKit(channels=16) # 设定PWM频率为50Hz(ServoKit会自动设置) # 控制0号通道的舵机转到90度 kit.servo[0].angle = 90 # 读取当前角度 current_angle = kit.servo[0].angle print(f"当前角度: {current_angle}")

如果你发现舵机0°和180°对应的实际角度有偏差,可以用set_pulse_width_range来标定:

kit.servo[0].set_pulse_width_range(500, 2500) # 单位微秒

这个库的好处是已经把脉宽换算、占空比设置、寄存器写入全部封装好了。对于一个快速实验项目,直接用ServoKit就够了。

4.3 底层registers方式:为了搞懂原理

如果你像我一样,喜欢搞清楚每一步到底在操作什么,可以直接操作寄存器。PCA9685的关键寄存器地址如下:

  • MODE1:0x00,模式控制寄存器。
  • PRESCALE:0xFE,预分频寄存器,用于设置PWM频率。
  • 每个通道的ON/OFF寄存器:通道0对应LED0_ON_L(0x06)、LED0_ON_H(0x07)、LED0_OFF_L(0x08)、LED0_OFF_H(0x09),后续通道依次偏移4个字节。

写一个底层控制代码:

import smbus2 # 默认地址 PCA9685_ADDRESS = 0x40 # 寄存器定义 MODE1 = 0x00 PRESCALE = 0xFE LED0_ON_L = 0x06 # 频率设置:50Hz PWM_FREQ = 50 bus = smbus2.SMBus(1) def setup_pwm_freq(freq): """设置PCA9685的PWM频率。""" # 用芯片内置25MHz晶振计算预分频值 prescale = round(25000000.0 / (4096.0 * freq)) - 1 old_mode = bus.read_byte_data(PCA9685_ADDRESS, MODE1) # 进入sleep模式才能修改预分频 bus.write_byte_data(PCA9685_ADDRESS, MODE1, (old_mode & 0x7F) | 0x10) bus.write_byte_data(PCA9685_ADDRESS, PRESCALE, prescale) # 恢复原模式 bus.write_byte_data(PCA9685_ADDRESS, MODE1, old_mode) # 等待晶振稳定 time.sleep(0.005) # 重新使能输出 bus.write_byte_data(PCA9685_ADDRESS, MODE1, old_mode | 0x80) def set_pulse_ms(channel, pulse_ms): """设置某通道高电平持续时间(单位:毫秒)。""" pulse_value = int(pulse_ms / 1000.0 * PWM_FREQ * 4096) pulse_value = max(0, min(4095, pulse_value)) reg_base = LED0_ON_L + channel * 4 # ON时刻设为0,OFF时刻设为pulse_value bus.write_byte_data(PCA9685_ADDRESS, reg_base, 0x00) bus.write_byte_data(PCA9685_ADDRESS, reg_base + 1, 0x00) bus.write_byte_data(PCA9685_ADDRESS, reg_base + 2, pulse_value & 0xFF) bus.write_byte_data(PCA9685_ADDRESS, reg_base + 3, (pulse_value >> 8) & 0x0F)

这段代码的要点在于预分频值的计算。PCA9685内部时钟是25MHz,经过4096步分频后再除以目标频率。公式就是prescale = round(25000000 / (4096 × freq)) - 1。为什么减1?这是芯片手册规定的偏移量,不做减法的话,实际输出频率会偏高。

实际测试中,我用示波器验证过,通过这个公式设置50Hz后,实际测到的频率误差在0.1Hz以内,完全满足舵机需求。

4.4 批量初始化与角度微调曲线

多舵机项目的初始化逻辑,我会这样组织:让所有舵机先设到一个安全角度(通常是90°居中位置),避免上电瞬间舵机乱飞。然后再逐个进入工作状态。

from adafruit_servokit import ServoKit import time # 自动检测总线上所有PCA9685板子 # 假设有两块,地址分别是0x40和0x41 kit1 = ServoKit(channels=16, address=0x40) kit2 = ServoKit(channels=16, address=0x41) # 将所有32路舵机移动到居中位置 for i in range(16): kit1.servo[i].angle = 90 kit2.servo[i].angle = 90 time.sleep(0.02) # 避免所有舵机同时启动造成电流尖峰 # 确认无误后再做具体动作 kit1.servo[0].angle = 45

为什么每路间隔20ms再赋值?因为舵机启动瞬间电流很大,如果16路舵机同时从0°跳到90°,电源会被瞬间拉垮,产生很大的电压跌落。错峰启动,实测可以显著降低电源压降,这也是一个从惨痛经历中总结出来的经验。

如果你需要更精细的角度控制,可以充分利用ServoKit的angle参数。比如想让1号舵机缓慢转动,可以写一个简单的插值循环:

def smooth_move(kit, channel, start_angle, end_angle, steps=50, delay=0.02): """将舵机从start_angle平滑移动到end_angle。""" for step in range(steps + 1): angle = start_angle + (end_angle - start_angle) * step / steps kit.servo[channel].angle = angle time.sleep(delay) smooth_move(kit1, 0, 90, 135)

这个函数内部做线性插值,每次改变一个很小的角度。实际效果非常丝滑,而且不会因为角度跳变过大引发舵机震颤。

5. 抖动问题的三个重点排查方向与实测数据

即便用了PCA9685,也不代表绝对不抖。我在项目调试中遇到过几种特殊情况,最终定位到的问题分为三个方向:电源、信号干扰、以及配置错误。下面结合实测数据逐一说明。

5.1 电源纹波与舵机堵转

最典型的现象:舵机静止时正常,转动过程中出现明显抖动或异响。用示波器测量舵机电源电压,会发现舵机转动瞬间,电压出现很大的跌落。

这是我用示波器测到的一组数据:

供电方式空载电压单舵机转动时电压跌落双舵机同时转动电压跌落
树莓派5V引脚5.0V跌至4.2V跌至3.6V
外部5V 2A电源5.0V跌至4.8V跌至4.5V
外部5V 5A电源 + 470uF电容5.0V跌至4.95V跌至4.9V

舵机的比较电路和电机驱动都对供电电压敏感。电压跌落过大时,舵机内部的控制电路可能进入欠压保护或逻辑混乱,导致输出轴来回振荡。解决方向很明确:换大电流电源,加电容储能。我习惯在PCA9685的V+和GND之间并联一个470uF到1000uF的电解电容,能有效吸收瞬时电流尖峰。

5.2 I2C信号干扰与地线回路

第二个常见问题:舵机转动时,树莓派偶尔报I2C通信错误,或者某一两个通道的舵机角度突然跳变。

排查思路是这样的:先确认舵机电源和树莓派共地,再检查SDA和SCL的接线长度。驱动大扭矩舵机时,电机换向会产生比较大的电磁干扰,如果I2C线又长又没有屏蔽,容易被干扰。

我遇到过一次比较棘手的情况:机械臂内部的舵机电源线和信号线走在一起,导致机械臂摆动时,所有舵机同步出现微小的角度抖动。用示波器看PCA9685的SDA/SCL波形,能明显看到毛刺。后来把I2C线缩短到10cm以内,并且和舵机电源线保持距离,情况立刻改善。

另外一个经验是,如果需要接比较长的I2C线,可以在SDA和SCL上各串一个330欧姆电阻,靠近树莓派一端再对地接一个10k欧姆上拉电阻。这样能有效减少反射和干扰,亲测有效。

5.3 频率设置和OE引脚的误区

还有一种“抖动”,实际上是频率设置错误导致的。PCA9685默认上电频率并不是50Hz,如果代码中没有显式设置,ServoKit内部的初始化函数会帮你设置,但如果你用的是自己写的底层代码,很容易忘记这个步骤。

我曾经在调试时发现舵机转动角度严重偏小,每个角度都缩水了接近20%。后来一查,PWM频率被设置成了约63Hz而不是50Hz。由于PCA9685的占空比是按比例存储的,频率不同,实际输出的脉宽就不同,舵机表现得就像“角度变准”了一样,其实是脉宽偏窄导致的系统性偏差。

OE引脚也需要留意。如果OE悬空或没接跳线帽,芯片输出可能被禁用或处于不受控状态。严格来说,OE是输入引脚,悬空时电平状态不确定,有时候舵机会乱抖。建议始终把OE通过跳线帽接到GND,让输出稳定使能。

5.4 用示波器验证高精度PWM

硬件调通后,最好用示波器看一眼波形。这是判断系统是否真正稳定的金标准。把探针接到某一路舵机信号线上,重点观察以下指标:

  • 周期是否稳定在20.000ms附近。
  • 高电平宽度是否和设定值一致。
  • 上升沿和下降沿附近有没有毛刺。

我测过一块运行正常的PCA9685,在设定输出1.5ms脉宽时,实测值是1.499ms到1.501ms之间浮动,精度达到了±2微秒。这个精度比树莓派软件PWM高了两到三个数量级,也足以满足任何舵机的控制需求。

如果示波器上看到脉宽有明显的周期性滑动,多半是电源问题或者I2C总线被频繁中断占用。这时候可以降低I2C通信频率,或者把PCA9685的地址改为不冲突的唯一值,排除总线竞争。

6. 特殊舵机处理:360度连续旋转舵机与自定义脉宽范围

普通舵机只能转0°到180°,但有一种“360度连续旋转舵机”常用于小车、云台等需要持续旋转的场景。它的内部结构不同,控制逻辑也完全不同,这里单独说一下。

6.1 连续旋转舵机的本质

普通舵机内部有位置反馈电位器,输入脉宽对应目标角度。但连续旋转舵机把电位器去掉了,脉宽对应的是转速和方向:

  • 1.5ms脉宽:停止。
  • 小于1.5ms(比如1.3ms):逆时针旋转,脉宽越小转速越快。
  • 大于1.5ms(比如1.7ms):顺时针旋转,脉宽越大转速越快。

所以控制连续旋转舵机时,angle语义就不灵了。ServoKit库可以用fraction来控制脉宽:

# 将0号通道设为连续旋转舵机模式 kit.continuous_servo[0].throttle = 0.0 # 停止 kit.continuous_servo[0].throttle = 0.5 # 50%速度正转 kit.continuous_servo[0].throttle = -0.5 # 50%速度反转

throttle的取值范围是-1到1,对应从最大反转速度到最大正转速度。注意,这个值和PCA9685的实际脉宽关系由库内部换算,你需要先确认自己的舵机中位是1.5ms还是1.52ms,否则即使设置throttle = 0.0,舵机也可能缓慢转动。

标定连续旋转舵机的中位,比普通舵机更讲究。在中位附近,脉宽每变化1微秒,都可能导致舵机开始缓慢自转。实际调试时,我会先把throttle设为0,然后用示波器观察实际输出脉宽,再通过set_pulse_width_range微调,直到舵机完全停住。

6.2 自定义脉宽范围的标定方法

不同型号的舵机,其脉宽范围差异很大。SG90常见的是0.5ms到2.4ms,MG996R一般是0.5ms到2.5ms,而一些高端数字舵机可能只有0.9ms到2.1ms。如果盲目使用默认值,轻则舵机转动范围不足,重则顶到机械限位,发出刺耳的堵转声。

标定方法不复杂:写一个简单脚本,让舵机每次增加一个很小的脉宽,观察实际运动,记录下两个极限位置对应的脉宽。

from adafruit_servokit import ServoKit kit = ServoKit(channels=16) # 先设置一个安全的中间值,然后逐步增加 kit.servo[0].set_pulse_width_range(500, 2500) import time for pulse in range(500, 2600, 10): # 直接设置脉宽(微秒) kit.servo[0].fraction = (pulse - 500) / (2500 - 500) print(f"当前脉宽: {pulse}us") time.sleep(0.3)

运行时注意观察舵机转动的角度变化。当角度不再随脉宽增加(或减少)而变化时,就说明已经到达了它的物理极限。把超出的部分从set_pulse_width_range中裁掉,中位再微调一下,这台舵机就算吃透了。

6.3 把PCA9685扩展成更多通道

单块PCA9685支持16路,但对复杂机械结构(比如六足机器人需要18路、四足机器人需要12路以上)来说,单块不够用。扩展方法前面已经提过:配置不同I2C地址,把多块板子并接到同一条I2C总线上。

实测中,我在一条总线上挂载了4块PCA9685,总共64路舵机,I2C通信没有任何压力。树莓派的I2C总线理论支持上百个设备,PCA9685最多支持62个地址,所以扩展空间是足够的。

多板控制时,地址分配代码要清晰:

from adafruit_servokit import ServoKit # 板A:0x40,舵机0~15 # 板B:0x41,舵机16~31 # 板C:0x42,舵机32~47 board_a = ServoKit(channels=16, address=0x40) board_b = ServoKit(channels=16, address=0x41) board_c = ServoKit(channels=16, address=0x42)

不过要注意,多块板子共用一个外部电源时,电源总线的压降会更大。我建议每块板子单独从电源走一路,尽量用粗线,确保末端电压足够。电源布线是否合理,在几十路舵机同时工作时,会直接影响系统的稳定性和最大速度。

回看这个项目,PCA9685方案最关键的价值就是一句话:把PWM生成从软件中解放出来,交给专用硬件去干。树莓派负责逻辑,PCA9685负责精确出波,各司其职,抖动问题自然消失。如果你正在为舵机抖动或者多路舵机控制头疼,这个方案值得认真试一次,实测下来就是:稳定、精准、省心。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询