
第一次用舵机做六足机器人时我把舵机直接接到了Arduino的5V引脚上代码里写了句myservo.write(90)结果舵机咔咔响了两声就开始疯狂抖动最后干脆停在某个角度不动了。后来排查了很久才发现问题根本不在代码而是供电不足加复位时序不对两个坑叠到了一起。也是从那次开始我把“舵机控制”当成了一门需要认真研究的信号与机械交叉课而不是简单接个线、写个write就完事。这篇内容不绑定某块开发板而是把舵机控制里最关键的几个节点串起来内部结构与闭环原理、PWM脉宽与角度映射、Arduino和树莓派Pico两种主流平台的实际操作、MG996R这类大扭矩舵机的选型与识别以及最让人头疼的复位、抖动、角度不准的排查思路。刚接触舵机的人可以通读一遍已经玩过一段时间但总被奇怪问题卡住的人可以直接跳到最后一章对号入座。1. 拆开舵机看本质位置闭环系统为什么只听PWM的指挥1.1 舵机内部不是“一个电机”而是一套微型伺服系统很多人第一次拿到舵机以为它就是一个小型直流电机通电就转。实际上舵机的核心价值在于“定位”而不是“旋转”。拆开一个标准舵机的外壳里面至少有这么几样东西直流电机、减速齿轮组、反馈电位器、控制电路板。直流电机提供动力但转速快、扭矩小直接带负载根本转不动所以要通过减速齿轮组降速增扭这就是为什么舵机能输出较大力矩。反馈电位器与输出轴联动舵机转动时电位器的滑臂跟着转电阻值随之变化从而把“当前角度”变成电压信号。控制电路板则是整个系统的大脑它读取外部输入的PWM信号解析出“目标角度”再与电位器反馈回来的“实际角度”做比较。这条链路很容易理解如果目标角度大于实际角度控制电路就让电机正转如果目标角度小于实际角度就让电机反转一旦两者相等电机停止舵机就稳定在指定位置。这是一个典型的闭环控制系统类比空调的恒温逻辑传感器不断测量室温压缩机不断调整直到温度到达设定值才停机。1.2 普通电机和舵机的显著区别位置反馈决定控制方式普通直流电机只要两端加上电压就会连续旋转电压越高转得越快你无法让它“停在某个具体角度”。舵机则不同它天生就是位置伺服设备输入的是一个代表角度的脉宽信号输出的是输出轴的绝对位置。这个区别直接影响了控制策略。对普通电机你需要额外加编码器或者限位开关才能知道它转到哪里对舵机内部电位器已经把位置反馈做好了你不需要关心电机本身转不转只管告诉它“到多少度”。这也是舵机在机器人关节、机械臂、云台、车模转向结构中如此流行的重要原因。从信号角度讲舵机只认PWM脉宽不认普通的数字高低电平。如果只是给一个高电平或低电平舵机不会转动。只有精确控制高电平的持续时间舵机才知道你要的是什么位置。这个细节是很多人掉进坑里的起点因为我见过有人直接把信号线接到Arduino数字引脚的正极结果舵机纹丝不动。2. 50Hz与1.5msPWM脉宽如何换算成精确角度2.1 标准PWM周期、高电平时间和角度的映射关系绝大多数微型舵机尤其是航模和机器人常用的SG90、MG90S、MG996R使用的PWM控制信号约定如下周期为20ms也就是频率50Hz在这个周期内高电平的时间长度决定舵机角度。常见的映射关系是高电平1ms对应0°、1.5ms对应90°、2ms对应180°。这是最经典的范围也是很多国产舵机的默认参数。另一些舵机会使用0.5ms到2.5ms作为0°到180°的范围所以遇到同一份代码在不同舵机上角度偏差很大时不要先怀疑代码写错而要先看舵机手册里的脉宽范围。如果手动计算脉宽可以用一个线性公式角度对应脉宽 最小脉宽 (最大脉宽 - 最小脉宽) × 目标角度 / 180例如标准舵机最小脉宽1000µs、最大脉宽2000µs想让舵机转到60°脉宽就是1000 (2000 - 1000) × 60 / 180 1333µs这个计算逻辑在Arduino里已经被库封装好了但直接在树莓派Pico上使用MicroPython时通常需要自己写所以理解公式比死记库函数更管用。2.2 为什么舵机只认高电平时间不认占空比很多人学PWM时会先接触LED调光那里面强调的是占空比也就是高电平时间占整个周期的比例。于是不少人觉得舵机也一样只要占空比对了就行。这个理解在周期固定的前提下能凑合但容易忽略一个关键点舵机内部的信号解码电路是对高电平持续时间做脉冲宽度计数而不是对比例做平均电压解析。举例来说同样是50%的占空比如果PWM周期是10ms高电平持续5ms这早就超出了标准舵机的脉宽范围舵机要么不执行要么直接失控。所以在舵机控制场景中不要只想着占空比要想着脉冲宽度µs。这也是为什么很多Pico入门者直接把默认的PWM频率用在舵机上结果舵机要么不动要么猛烈抽搐因为Pico的PWM初始化如果不显式设置频率默认可能不是50Hz。另一个常见的信号误区是逻辑电平。Arduino Uno的数字引脚输出5V高电平可以直接驱动绝大多数舵机信号线。树莓派Pico的GPIO输出3.3V高电平虽然许多舵机也能识别但部分舵机对高电平门槛比较敏感如果遇到信号识别不稳可以加一级电平转换模块把3.3V信号升到5V。3. 标准舵机与360°舵机的关键分水岭MG996R选型对照3.1 MG996R普通版与360°版逻辑上完全不同MG996R是一颗非常流行的模拟舵机金属齿轮、大扭矩、价格适中几乎成了DIY机械臂的首选。但这个型号在电商平台上有两个版本标准角度版和360°连续旋转版。两者外观几乎一样价格也差不多但控制逻辑天差地别。标准角度版内部有完整的位置反馈电位器输入1.5ms脉宽时舵机回到中位输入更高或更低的脉宽时转到指定角度并保持。360°连续旋转版为了能无限旋转把反馈电位器替换成了固定电阻控制电路不再知道输出轴的实际位置。因此无论你怎么改脉宽它都不会停在指定角度上只会根据脉宽大小决定停止、正转、反转以及转速。所以网上很多人抱怨“MG996R不能控制角度”十有八九是买到了360°版本而不是标准版本。如果你确实需要控制角度务必在商品标题里仔细辨认“标准舵机”“180°”字样不要只看“MG996R”这个型号名。3.2 如何快速识别手里的舵机是标准版还是360°版首先可以看包装和详情页但有时候买回来已经拆了就得动手实测。一个最简单的办法把舵机接上电源信号线输入一个1.5ms的脉宽Arduino里就是myservo.write(90)。标准舵机会停在某个角度并保持360°连续旋转舵机则会完全停止转动。然后将脉宽改成2mswrite(180)标准舵机转到另一个角度后保持360°连续旋转版则会持续逆时针旋转。如果手里没有代码环境可以把信号线接到一个50Hz、高电平2ms的方波信号上再观察输出轴。只是测试时注意不要让连续旋转版转太久避免机械限位或者电流过大。另外标准舵机用手去扭输出轴时会感到明显的阻力因为内部齿轮和电位器咬合在一起360°舵机用手扭输出轴时有些型号会比较顺滑因为齿轮组与电位器没有位置反馈联系。这个方法不绝对但结合脉宽测试基本能判断。3.3 常见舵机参数对比与选型思路型号类型工作电压堵转电流扭力典型用途SG90标准角度4.8V~6V约200mA1.8kg·cm小型云台、轻型关节MG90S标准角度4.8V~6V约300mA2.2kg·cm金属齿轮小舵机MG996R标准/360°可选4.8V~7.2V2A以上9.4kg·cm机械臂、小车底盘DS3218标准数字6V~7.4V3A左右20kg·cm人形机器人、大型关节选型时最关键的不是角度范围而是扭矩和供电能力。MG996R虽然扭力大但启动瞬间和堵转时电流能到2A以上如果用Arduino的5V引脚带电压会被拉低舵机就会抖动甚至复位这也是我一开篇遇到的情况。正确的做法是单独准备一个5V/6V稳压模块或电池舵机电源与开发板电源共地但不共用同一路输出。4. Arduino控制舵机接线、库函数、复位与角度映射的完整流程4.1 接线与供电三根线哪里都能接但别把小舵机当大力士舵机输出三根线通常红色是电源正极棕色或黑色是电源负极橙色或黄色是信号线。接线时先把舵机电源接到外部5V电源的正负极再把信号线接到Arduino的数字引脚比如9号引脚。这里最容易被忽略的是“共地”也就是外部电源的负极必须和Arduino的GND连在一起。如果不共地Arduino输出的信号高电平没有统一的参考点舵机控制电路可能读不到有效脉宽从而出现随机抖动、无响应等奇怪现象。如果只接一个SG90这类小舵机且动作不频繁Arduino的5V引脚还能勉强带一下但接MG996R或者多个舵机时千万不要从Arduino板载5V引脚取电。板载稳压器和USB供电能力有限一旦电流需求超过供电上限Arduino自身会先掉电重启问题表现为“舵机一动串口就断”“程序像卡死一样”其实都是供电不足造成的。4.2 Servo库的常用函数与角度控制示例Arduino官方提供的Servo.h库把PWM脉宽生成封装得很好用户不需要自己算寄存器。基本用法是#include Servo.h Servo myServo; void setup() { myServo.attach(9); // 信号线接9号引脚 myServo.write(90); // 上电先回中位完成复位 delay(500); } void loop() { for (int angle 0; angle 180; angle) { myServo.write(angle); delay(15); } for (int angle 180; angle 0; angle--) { myServo.write(angle); delay(15); } }attach(pin)默认使用标准1000µs到2000µs范围对应0°到180°。如果手里的舵机实际范围更宽或更窄可以显式指定myServo.attach(9, 500, 2500); // 使用500µs到2500µs对应0°到180°writeMicroseconds()则直接让你控制高电平脉宽比如writeMicroseconds(1500)就是让舵机回到中位。这种写法在调试舵机边界时特别有用可以精确扫描某个舵机的真实脉宽范围。关于复位我个人的经验是在setup里先attach再write(90)并延时几百毫秒。这样做是为了避免舵机在上电瞬间收到一个不可预知的脉宽导致猛甩到一边。尤其是机械臂上电猛转很容易撞到周围结构轻则齿轮异响重则打齿。4.3 Arduino控制360°连续旋转舵机速度控制而不是角度控制如果你手里确实是一个360°连续旋转版舵机那么代码逻辑要换一套。它不认绝对角度只认“相对中位”的偏差输入1500µs舵机停止输入小于1500µs舵机向一个方向旋转差值越大转速越快输入大于1500µs舵机向另一个方向旋转差值越大转速越快。实际代码可以这样#include Servo.h Servo wheelServo; void setup() { wheelServo.attach(10); wheelServo.writeMicroseconds(1500); // 停止 delay(500); } void loop() { wheelServo.writeMicroseconds(1600); // 慢速正转 delay(2000); wheelServo.writeMicroseconds(1500); // 停止 delay(1000); wheelServo.writeMicroseconds(1400); // 慢速反转 delay(2000); }这种舵机很适合做小车底盘、云台连续旋转机构但不能用来做机械臂关节因为无法反馈当前角度。5. 树莓派Pico的MicroPython实现从底层PWM到平滑扫动5.1 Pico的PWM资源与舵机信号线的连接树莓派Pico没有专门封装好的Servo库但每个可用的GPIO几乎都可以复用为PWM输出这让它成为学习舵机底层控制的绝佳平台。用MicroPython控制舵机时需要引入machine.PWM然后设置频率为50Hz再通过duty_u16()设置脉宽占空比。接线方面Pico的GPIO输出3.3V舵机信号线可以接到GPIO15、GPIO14等任意支持PWM的引脚。舵机电源照旧用外部5V电源负极和Pico的GND共地。很多人直接把舵机电源接到Pico的VSYS或者3V3引脚如果舵机很小勉强能用但接MG996R基本会直接把板载稳压器拖垮所以还是那句话独立供电。5.2 用duty_u16计算脉宽并实现角度控制duty_u16的范围是0到6553516位整数对应整个PWM周期的高电平占比。如果PWM频率是50Hz周期是20ms那么65535对应20ms全部为高电平。因此1ms脉宽对应65535 × (1 ÷ 20) ≈ 32771.5ms脉宽对应65535 × (1.5 ÷ 20) ≈ 49152.5ms脉宽对应65535 × (2.5 ÷ 20) ≈ 8192如果使用1000µs到2000µs映射0°到180°则可以这样写from machine import Pin, PWM import time servo PWM(Pin(15)) servo.freq(50) MIN_DUTY 3277 # 1000us - 0度 MAX_DUTY 6554 # 2000us - 180度 # 如果舵机范围是0.5ms到2.5ms则MIN_DUTY1638MAX_DUTY8192 def set_angle(angle): if angle 0: angle 0 if angle 180: angle 180 duty int(MIN_DUTY (MAX_DUTY - MIN_DUTY) * angle / 180) servo.duty_u16(duty) def reset_servo(): servo.duty_u16(4915) # 1.5ms回到中位 time.sleep_ms(500) reset_servo() for angle in range(0, 181, 45): set_angle(angle) time.sleep_ms(500)这段代码里最需要记住的是PWM频率必须先设置为50Hz否则duty_u16对应的实际高电平时间不是预期值舵机就会乱动。我调试时遇到过一种情况舵机对3.3V信号仅有微弱响应接上后只能听到电流声无法转动后来加了电平转换模块才稳定。如果你的舵机也这样可以先用Arduino的5V信号对比测试确认是不是信号电平问题。5.3 Pico与Arduino控制舵机的差异和取舍项目Arduino Servo库树莓派Pico MicroPython封装程度高attach/write即可低需要自己算duty值频率设置库自动处理必须手动设置50Hz灵活性默认逻辑固定较难自定义可以任意控制脉宽和信号学习价值适合快速实现功能适合理解底层原理环境搭建Arduino IDE稳定Thonny或MicroPython环境即可如果你只想快速跑通一个机械臂demoArduino的Servo库确实省心。但如果你想从原理层面吃透舵机控制用Pico手写一遍PWM计算会让你对脉宽、周期、duty值之间的关系有更深的记忆。两个平台我都用过现在做方案时会优先考虑Arduino做MCUPico则更像是一个随时可用的PWM信号发生器用于测试舵机个体差异很好用。6. 舵机失控、抖动、角度不准的排查链路与校准技巧6.1 上电猛转和复位失败先把初始姿态控制住上电瞬间开发板引脚电平不稳舵机可能收到一个随机脉宽导致输出轴猛甩到某个极限位置。这个现象在机械臂上尤其危险因为会撞到桌面或者其他结构件。解决办法就是程序里显式控制复位行为。Arduino代码如下void setup() { myServo.attach(9); myServo.write(90); delay(300); }Pico代码如下servo.freq(50) servo.duty_u16(4915) time.sleep_ms(300)这里的要点有两个一是在任何动作代码之前执行复位二是给舵机一个稳定过程的时间。如果只是attach而没有立刻write某些版本的Servo库会保留上一次使用的脉宽导致“上次停在哪这次就从哪启动”可能会造成意外动作。6.2 抖动、吱吱响、发热严重从供电到干扰逐项查舵机抖动并且有刺耳的“吱吱”声是新手遇到最多的问题。排查链路一般是检测舵机电源电压。用万用表在舵机动作时测电源两端如果电压低于4.5V基本可以确定供电不足。解决方法是改用更大电流的稳压模块或者在舵机电源线上并联一个大电容比如470µF电解电容吸收瞬时电流冲击。确认信号共地。这个前面已经强调过电源负极和开发板GND必须连在一起。检查信号线是否过长或受到干扰。PWM信号线建议控制在20cm以内如果必须延长使用屏蔽线或绞合线并远离大电流电机线。验证PWM频率。使用Pico或自己生成的PWM时确认频率是50Hz左右。很多舵机在更高频率下也能工作但反馈电路会过热出现抖动和寿命下降。如果以上都没问题再检查机械负载。舵机输出轴被卡住时控制电路会一直尝试纠正误差电流居高不下舵机就会发热、抖动。这种情况下调整机械结构比改代码更有效。6.3 角度不准与左右不对称用writeMicroseconds做边界校准同一个型号的舵机个体差异也会导致角度不准这是电位器精度和齿轮装配误差造成的。比如你写write(90)实际停在87°或者左右两个舵机在相同角度下姿态不同。这时候不要试图在代码里写死一个补偿值因为不同位置误差可能不同。更好的办法是做一个脉宽扫描实验用writeMicroseconds从500µs开始每次增加50µs记录舵机实际到达最左和最右的临界脉宽然后用自己计算出的映射关系写入。Arduino里可以这样myServo.attach(9, 500, 2500); myServo.writeMicroseconds(500); // 测试左极限 delay(500); myServo.writeMicroseconds(2500); // 测试右极限 delay(500);如果发现500µs时舵机已经到极限2500µs时也是一样说明舵机实际的有效脉宽范围可能比500~2500更窄。调整以后再用自定义范围attach(pin, min, max)写入角度误差会减小很多。还有一点舵盘安装错位也会导致“角度不准”。如果把舵盘夹在输出轴上时装歪了哪怕舵机本身精准看起来的角度也会偏差。这种情况不需要校准脉宽重新安装舵盘即可。6.4 多舵机场景下的供电分配与有序启动同时驱动多个舵机时最大的问题永远是电流。Arduino Uno的5V输出无法带动多路舵机Pico的3V3也一样。标准做法是外接一个5V/6V、电流5A以上的稳压模块给所有舵机供电然后开发板单独供电两者共地。启动顺序上建议让舵机一个一个完成复位避免同时上电瞬间电流叠加。代码层面就是把多个write(90)分散执行中间加delaymyServo1.write(90); delay(200); myServo2.write(90); delay(200); myServo3.write(90);如果舵机数量很多比如六足机器人18路舵机再好的供电方案也会让MCU扛不住这么多次PWM信号输出。这时候可以考虑PCA9685这类16路舵机驱动板它通过I2C接口扩展PWM输出把脉宽生成任务从主控里分担出去主控只负责发角度数据。PCA9685本身也建议单独供电并且要留意它的逻辑电平与主控兼容性。我在实际项目里养成了三个习惯到现在都还在用一是任何舵机上电前先确认复位脉宽是1.5ms二是所有舵机电源都独立供电并共地绝不从开发板取电三是新舵机到手先做一次脉宽边界测试再写到代码里当参数。这些小习惯看起来麻烦但能省下无数调试时间。最后分享一个测试舵机时的小技巧准备一个独立的PWM信号发生器或者直接用Pico写一个扫描程序把脉宽限制在500µs到2500µs之间循环输出。这样在测试机械结构、观察齿轮啮合、判断舵机是否堵转时比反复改代码高效得多。舵机这东西本质上就是一套很简单的伺服闭环一旦你把信号、供电、负载这三个变量控制住后续所有项目都会顺很多。