ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Arduino控制SG90 360度连续旋转舵机:脉宽、校准与开环控制

Arduino控制SG90 360度连续旋转舵机:脉宽、校准与开环控制 拿到一个丝印上写着“SG90”的舵机如果它是 360 度连续旋转版本请立刻忘掉“标准舵机是按角度转动”的习惯。你会发现servo.write(90)并不能让它停在中间servo.write(0)也不会让它转 180 度后再停下来。它可能一直转也可能在某个脉冲下缓慢爬行甚至发writeMicroseconds(1500)后它还在慢慢转。这不是接线错了也不是舵机坏了而是没有理解它的本质360 度连续旋转舵机是一个“开环速度控制设备”不是“位置控制设备”。标准 SG90 内部有位置反馈能闭环锁住角度而 360 度版本通常取消了电位器反馈或者把反馈固定在中位控制信号实际只决定“朝哪个方向转、转多快”不决定“转多少度”。本文我用 Arduino UNO SG90 360 度连续旋转舵机把整套控制逻辑拆开讲清楚。内容包括SG90 标准角度版和 360 度连续旋转版怎么区分writeMicroseconds()为什么是更合适的控制 API硬件接线、基础代码、停止点校准开环控制下如何用时间估算转动量常见故障排查和工程建议。读完你能独立跑通一个“转向 停 反向转”的示例并且知道什么时候该加编码器或限位开关把开环系统升级成闭环。1. 为什么 SG90 360 度版本不是“能转 360 度的 SG90”1.1 名字相同内部逻辑不同标准 SG90 是最常见的 9g 微型舵机。它的内部结构包括直流电机、减速齿轮、电位器和一块控制电路。工作原理可以简化成一句话输入一个目标位置的 PWM 脉冲控制电路读取电位器反馈的当前位置计算误差然后驱动电机向误差减小的方向转动直到当前位置和目标位置一致。这就是典型的闭环位置控制。电位器就是反馈传感器。所以标准 SG90 可以快速到达 0°、45°、180° 这样的角度位置并且外部负载不太大时位置能保持住。而市面上的“SG90 360 度连续旋转舵机”从机械结构上往往取消了电位器与输出轴的联动或者直接把电位器替换成固定电阻分压。这样一来控制电路不再知道输出轴到底在哪个角度它只拿输入脉冲与一个固定中位做比较。比较结果如果是“正偏差”电机就一直往一个方向转如果是“负偏差”电机就一直往另一个方向转如果接近零偏差电机才停。所以 360 度连续旋转舵机本质上是一个用 PWM 脉宽控制方向与转速的直流电机驱动模块。很多新手会把它理解成“能转 360 度的 SG90”其实不对。标准 SG90 的旋转范围通常约 180°而 360 度连续旋转舵机是可以无限圈连续旋转的只是没有角度反馈不能精确告诉你转到哪了。1.2 拿到手后怎么快速区分如果你手边已经有一个舵机不确认它是标准角度版还是连续旋转版可以用下面方法初步判断用手轻轻捏住输出舵盘尝试转动。如果转起来明显有阻力并且松手后会自动弹回一个固定位置大概率是标准角度舵机内部存在电位器位置反馈。如果转动比较顺畅没有明显回中力可以转很多圈说明大概率是连续旋转版本。不过这个判断不是 100% 准确有些电机减速箱本身也有阻力。更可靠的方法是在程序中发一个位置指令观察动作。发write(90)如果舵机快速回到中间位置并停住就是标准角度版如果它不回到中间甚至继续转动那就是连续旋转版。1.3 闭环与开环一个关键区别项目标准角度舵机360 度连续旋转舵机反馈方式电位器反馈当前位置无位置反馈或固定中位控制目标到达指定角度转动方向和转速控制类型闭环位置控制开环速度控制常用 APIwrite(angle)writeMicroseconds(us)能否自动回到固定位置能不能典型应用机械臂关节、云台、机器人关节小型轮子、持续旋转结构、云台扫动从控制角度看标准舵机是“我告诉你目标角度它自己努力转过去”连续旋转舵机是“我告诉你往左转、转快点但到底转了多少它不负责汇报”。这个区别决定了后续所有代码写法。2. writeMicroseconds 与 write 有什么区别2.1 Servo 库中的两个核心 APIArduino IDE 自带的Servo.h库封装了舵机控制的底层细节。最常用的两个方法是servo.write(angle); servo.writeMicroseconds(us);write(angle)的参数是角度范围一般是 0 到 180。库内部会把角度映射成对应的脉冲宽度再发送给舵机。对标准角度舵机来说这个 API 很符合直觉。writeMicroseconds(us)的参数是高电平脉冲宽度单位是微秒。RC 舵机通常工作在 50Hz即周期约 20ms其中高电平脉冲宽度决定了控制量。常见范围是 500us 到 2500us很多舵机的实用范围是 1000us 到 2000us。2.2 为什么 write 会让连续旋转舵机失控标准舵机里write(0)、write(90)、write(180)分别对应不同的角度。但连续旋转舵机没有角度反馈write(0)实际只是发出一个接近 500us 或 1000us 的脉冲这个脉冲会让舵机朝某一方向全速转动。它不会在 0 度停下来因为它根本不知道 0 度在哪里。同理write(90)只是输出大约 1500us 的中性脉冲。问题是不同批次的连续旋转舵机真正停止点不一定严格在 1500us。有的在 1460us有的在 1530us。如果你直接write(90)舵机可能仍然以很慢的速度转动。所以控制 360 度连续旋转舵机时我更推荐直接使用writeMicroseconds()。它让你从“角度思维”切换到“脉宽思维”更贴近舵机实际工作的信号层面。2.3 脉宽、方向与速度的关系对典型的连续旋转舵机控制规律大致如下脉宽表现低于 1500us如 1000us一个方向全速转动接近 1500us如 1480~1520us停止或不确定高于 1500us如 2000us另一个方向全速转动与 1500us 偏差越小速度越慢注意这里的 1000us 和 2000us 并非所有舵机都一样。有的 SG90 360 版本支持更宽的脉宽范围有的则只在 700us 到 2300us 之间有效。具体范围以你实际购买到的舵机说明书为准。“接近 1500us 就停止”也有一个坑停止点不一定正好是 1500us。即使是同型号同批次也可能有微小差异。后面我会专门写一个校准程序帮你找到真正停止点。3. 环境准备与硬件接线3.1 需要准备的器材Arduino UNO 开发板 x1SG90 360 度连续旋转舵机 x1面包板 x1杜邦线若干USB 数据线 x1外部 5V 电源可选但强烈推荐建议再准备一瓶带颜色的胶带或记号笔。后面做开环角度标定时需要在舵机输出盘上做标记。3.2 SG90 三根线怎么接SG90 的线序有固定规律但不同厂商线色可能略有差异。常见标准接线如下舵机线颜色含义接 Arduino UNO棕色或黑色GNDGND红色VCC电源正极5V 或外部 5V 正极橙色或黄色信号D9如果你的舵机线色不是上面这种不要凭印象接先用万用表确认线序或者查看商品页说明。接反电源线很容易烧毁舵机控制板。3.3 一个舵机如何供电SG90 是 9g 微型舵机单个舵机在轻载测试时理论上可以尝试从 Arduino UNO 的 5V 引脚取电。但要注意UNO 的板载稳压器供电能力有限如果舵机堵转或瞬间启动电流较大可能导致 Arduino 复位表现为舵机抽搐、USB 断开或程序重新运行。所以更稳妥的接线有两种方案一直接接 UNO 5V适合空载短时间验证。SG90 红 → Arduino 5V SG90 棕 → Arduino GND SG90 橙 → Arduino D9方案二外部 5V 电源给舵机供电Arduino 与舵机共地。SG90 红 → 外部 5V 正极 SG90 棕 → 外部 5V GND 和 Arduino GND共地 SG90 橙 → Arduino D9共地是为了让 Arduino 输出的信号电平和舵机电源参考地一致。如果地不共信号可能乱跳舵机行为会很奇怪。从工程角度只要不是只做几秒钟的空载演示都建议用方案二。后面如果接两个或更多舵机更不能全部从 UNO 5V 引脚取电。4. 基础驱动示例正转、停止、反转4.1 最小控制代码在 Arduino IDE 中新建项目输入以下代码#include Servo.h Servo servo; const int servoPin 9; void setup() { servo.attach(servoPin); // 先输出 1500us让舵机进入停止状态 servo.writeMicroseconds(1500); delay(1000); } void loop() { // 保持停止 servo.writeMicroseconds(1500); delay(2000); // 高速正转 1.5 秒 servo.writeMicroseconds(1900); delay(1500); // 停止 1 秒 servo.writeMicroseconds(1500); delay(1000); // 高速反转 0.8 秒 servo.writeMicroseconds(1100); delay(800); // 回到停止 servo.writeMicroseconds(1500); delay(2000); }这段代码的核心是servo.writeMicroseconds()。它直接告诉舵机当前要输出的脉宽。1900us高于中性点舵机会向一个方向转动1500us接近中性点舵机停止1100us低于中性点舵机向另一个方向转动。其中delay()决定了转动时间。比如高速转 1.5 秒后会停止但这不是“精确转 1.5 秒对应某个角度”只是“让执行过程持续一段时间”。4.2 如何验证是否成功上传代码后打开串口监视器没有特别输出但舵机应该按照下面顺序动作上电后先保持不动约 1 秒停止 2 秒朝一个方向转动 1.5 秒停止 1 秒朝另一个方向转动 0.8 秒再次停止。如果舵机转了一个方向后没有停下来而是继续低速转动说明 1500us 并不是你手上舵机的真正停止点。下一步需要做校准。5. 用串口校准程序找到真正停止点5.1 为什么要校准很多连续旋转舵机的“中位停止脉冲”并不是标准的 1500us。虽然大部分舵机以 1500us 为中性参考但在实际生产中元件误差会导致停止点偏移。另外舵机还存在“死区”。也就是脉宽变化很小的一段范围内电机可能不转或者输出力矩很小。死区之外脉宽稍微偏离中位舵机就会开始缓慢转动。如果你发 1500us 后发现舵机还在慢慢爬不要惊讶先校准。5.2 串口校准程序下面的程序会通过串口监视器接收你输入的脉宽值并实时发送给舵机。你可以从 1500us 开始逐步加减找到舵机真正停止的值。#include Servo.h Servo servo; const int servoPin 9; int currentUs 1500; void setup() { Serial.begin(9600); servo.attach(servoPin); servo.writeMicroseconds(currentUs); Serial.println(Servo calibration start); Serial.print(Current pulse width: ); Serial.println(currentUs); Serial.println(Input a value between 1000 and 2000); } void loop() { if (Serial.available() 0) { int value Serial.parseInt(); // 简单防呆避免误输入越界值 if (value 800 value 2500) { currentUs value; servo.writeMicroseconds(currentUs); Serial.print(Set to: ); Serial.println(currentUs); } else { Serial.println(Value out of range); } } }操作步骤编译上传程序打开串口监视器波特率选择 9600在输入框发送1500观察舵机是否停转如果还在缓慢转动发送1480、1520、1460、1540等值反复比较找到一个输出轴完全静止、即使轻轻用手扭动也有反向抵抗力的脉宽值把该值记为NEUTRAL_US。你可能发现多个相邻值都能让舵机停止比如 1485us 到 1515us 都在死区内。这时取中间值作为中性脉宽即可。比如最终测得停止点在 1480us那么后续代码中就不要再用 1500us 作为中位而应该定义const int NEUTRAL_US 1480;这是一个非常关键的实测步骤。标题里说“Arduino UNO 实测”真正的实测不是看舵机能不能转而是校准出你手上这只舵机的停止点。6. 开环控制下的角度估算与场景实践6.1 连续旋转舵机能做什么由于精度不高360 度连续旋转舵机不适合做机械臂关节或其他需要精确角度定位的场景。它更适合小型小车驱动轮水平云台持续扫描模型风机、转盘等匀速旋转结构要求转向灵活、不要求精确角度的机器人底盘。典型例子是双舵机小车。两个 360 度连续旋转舵机分别驱动左右轮通过差速实现前进、后退和转向前进左右轮同向转动后退左右轮反向转动原地左转左轮反向转右轮正向转停车两个舵机都输出中性脉宽。这种控制模型简单但真正的难点是开环控制没法保证两个轮子转速完全一致所以小车走直线可能会偏。为什么因为 PWM 脉宽决定了控制信号但实际转速还受电池电压、负载、地面摩擦力、舵机个体差异影响。你给两个舵机都发 1900us它们实际转速未必完全一致。6.2 如何估算转过的圈数开环控制下想粗略实现“转到某个角度”通常的思路是先选定一个固定脉宽测量单位时间内转过多少圈然后用时间推算目标角度。具体做法如下在舵机输出盘上贴一段彩色胶带作为标记发送一个固定脉宽例如 1000us让舵机全速转动记录一个固定时间比如 2 秒时间到后立刻发送中性脉宽停止观察舵机输出盘转了多少圈重复几次取平均得到“2 秒约转 N 圈”的经验值。之后你要让它转大约 5 圈就按这个经验值估算需要多长时间然后改为const int NEUTRAL_US 1500; // 根据校准结果修改 const int SPEED_US 1000; // 全速方向 servo.writeMicroseconds(SPEED_US); delay(calculatedMs); servo.writeMicroseconds(NEUTRAL_US); delay(100);注意这只是估算不是精确控制。因为每次启动、停止、负载变化都会带来误差而且误差会累积。如果你只想测试这个思路可以先写一个简单标定代码#include Servo.h Servo servo; const int servoPin 9; const int NEUTRAL_US 1480; // 改成你校准到的值 void setup() { Serial.begin(9600); servo.attach(servoPin); servo.writeMicroseconds(NEUTRAL_US); delay(1000); } void loop() { // 全速转 2 秒 servo.writeMicroseconds(1000); delay(2000); // 停止 servo.writeMicroseconds(NEUTRAL_US); delay(2000); // 观察结束后进入死循环避免反复自动测试 while (1) { delay(1000); } }上传后观察输出盘转动的圈数再把时间参数换算成你要的角度。这是个典型的开环标定实验。6.3 开环控制的局限开环控制最大的问题是没有“确认机制”。你发出“转 2 秒”的指令后如果舵机中途堵转程序并不知道也没有任何自动修正能力。结果就是最终位置和你预期位置偏差很大。所以在实际项目中如果位置精度很重要你有几个选择换成标准角度舵机利用内部电位器闭环定位换成步进电机或带编码器的直流电机做位置闭环在连续旋转舵机的输出端增加限位开关每次启动时回零消除累积误差增加霍尔编码器或光电码盘把开环系统升级成闭环。“开环控制适合结构简单、误差不敏感、成本敏感的小项目”是一个比较准确的判断。真正的机器人项目精度要求稍高就会考虑闭环。7. 常见问题与排查思路问题现象可能原因排查方式解决方案舵机完全不动供电不足、接线错误、程序没跑检查线色、重新上传例程外部 5V 供电共地确认信号线接对发 1500us 还在慢慢转中性点偏移用串口校准程序扫描脉宽校准出真正停止点定义NEUTRAL_US舵机只能全速转不能调速脉宽变化太快或范围不对从 1000us 到 2000us 逐步测试确认舵机支持的脉宽范围Arduino 重启或舵机抽搐舵机电流过大拉低电压观察 UNO 是否重启改用外部电源不直接从 5V 引脚取电舵机发热严重持续堵转或长时间高速运行触摸外壳温度串口通信空闲时输出中性脉宽避免长时间堵转两个舵机转速不一致个体差异、供电不稳更换 PWM 引脚或测量电压使用同批次舵机追求一致需闭环servo.read()读了不对读到的只是目标值不是实际值打印串口观察连续旋转舵机没有角度反馈不能依赖read()需要特别提醒的是很多新手会把servo.read()当成实际反馈来用。实际上标准舵机读取的是库内部记录的最后目标值不是传感器返回值。360 度连续旋转舵机则更不可能告诉你当前角度。如果你需要真实角度只能外接编码器或位置传感器。另外如果你用两个舵机做小车尽量不要让一个舵机在堵转状态下持续工作。堵转电流大、发热快容易损坏舵机内部齿轮或电机。8. 最佳实践与工程建议8.1 把中性点和水头引脚写成常量控制代码不要到处直接写1500。建议开头统一定义const int NEUTRAL_US 1480; const int MIN_US 1000; const int MAX_US 2000; const int servoPin 9;这样换一只舵机时只需要重新校准并修改NEUTRAL_US不需要翻遍全项目。对多档速度可以封装成函数#include Servo.h Servo servo; const int NEUTRAL_US 1480; const int MIN_US 1000; const int MAX_US 2000; void setServoPulse(int speedPercent) { speedPercent constrain(speedPercent, -100, 100); int pulseUs; if (speedPercent 0) { pulseUs NEUTRAL_US (MAX_US - NEUTRAL_US) * speedPercent / 100; } else { pulseUs NEUTRAL_US (NEUTRAL_US - MIN_US) * speedPercent / 100; } servo.writeMicroseconds(pulseUs); } void setup() { servo.attach(9); servo.writeMicroseconds(NEUTRAL_US); delay(500); } void loop() { setServoPulse(60); delay(1000); setServoPulse(0); delay(1000); setServoPulse(-60); delay(1000); }需要说明的是这个映射仍是近似关系。由于舵机内部电路和死区存在小百分比脉宽下可能不转或者速度并非完全线性。8.2 注意电源和共地SG90 360 连续旋转舵机虽然体积小但堵转电流仍然可能很大。单舵机测试建议配备一个能提供 1A 以上电流的 5V 电源并把电源负极与 Arduino GND 连接。如果条件不足至少做到每次只接一个舵机进行测试不要长时间堵转避免 USB 供电驱动多舵机。8.3 测试流程先空载再带载新舵机到手先不要直接装到车体或机械结构上。先在空载状态下验证舵机能否按要求正转、反转、停止校准停止点测量不同脉宽下的转速确认稳定后再接入实际负载。接入负载后转速会发生变化之前空载标定的圈数不再可靠需要重新做带载标定。8.4 设计“软启停”不要瞬间正反转如果你的程序需要让舵机从全速正转立即切换到全速反转建议先让舵机回到中性脉宽等待几百毫秒后再反向。瞬间正反转会让电流冲击变大齿轮也更容易磨损。一个简单做法是servo.writeMicroseconds(1000); delay(1000); servo.writeMicroseconds(NEUTRAL_US); delay(300); servo.writeMicroseconds(2000); delay(1000);8.5 为闭环升级预留接口如果项目是做课程设计或长期迭代建议在硬件设计时预留编码器接口或限位开关输入引脚。哪怕第一版先用开环跑通功能后续也能比较容易地加反馈。软件上可以把“设置目标速度”和“是否到达目标”分开抽象。开环阶段到达判断可以用延时闭环阶段只需要替换为编码器计数判断不需要改太多业务逻辑。9. 总结SG90 360 度连续旋转舵机的入门关键不是背几个 API而是理解它和标准角度舵机的本质差异。标准舵机是“位置闭环”360 度连续旋转舵机是“开环速度控制”。控制它时不要带着角度思维要用writeMicroseconds()直接控制脉宽。实际项目中最容易踩的坑有三个不知道停止点可能偏移直接写死 1500us把开环控制当成精确角度控制直接用 Arduino 5V 引脚驱动多个舵机导致供电不稳定。解决办法也很明确先区分手上舵机是标准版还是连续旋转版再用串口校准程序找到真正停止点然后以“脉宽 时间”的方式估算转动量。如果精度不够就需要引入编码器或限位开关把系统升级成闭环。这个从开环到闭环的思考路径比单纯跑通一个例子更有价值。
返回列表