刚入手 SG90 舵机时,我想做一个小型摇头云台,按网上的教程写servo.write(90)确实能转,但换成另一颗“SG90”后,角度却对不上,甚至出现“明明让舵机转 180°,结果它自己转了一圈又一圈”的现象。后来才发现,我拿到的是 SG90 的 360 度连续旋转版本,和普通位置舵机根本不是一回事。
本文就以 Arduino UNO 和 SG90 360 度连续旋转舵机为例,从硬件接线、PWM 控制原理、writeMicroseconds用法到开环控制的概念,做一个完整实测。新手可以把这篇当作入门教程,有基础的开发者也可以直接跳到第 4 节查看可复制代码和排错清单。
1. SG90 与 360 度连续旋转舵机
1.1 舵机是什么
舵机(Servo)是一种能根据控制信号执行动作的小型电机驱动模块。常见的模型舵机内部由直流电机、减速齿轮、控制电路组成。与传统直流电机不一样的是,舵机通常只需要一根信号线,就能让电机轴按要求动作,不需要自己做 H 桥驱动,使用门槛很低。
普通 SG90 是 180 度位置舵机,内部带有一个电位器,用来测量当前输出轴的位置。控制电路通过电位器反馈不断修正电机,让输出轴稳定停在指定角度。
而 360 度连续旋转舵机则是另一种形态。它的内部电位器往往被去掉,或者被固定替换成一个电阻分压网络,因此控制电路不再知道输出轴转到哪里。这时,给它一个 PWM 脉宽,它不会像普通舵机那样回到某个角度,而是会按某个方向和速度连续旋转。
1.2 SG90 标准版与 360 度连续旋转版的区别
很多初学者看到“SG90”就直接认为是 180 度舵机,实际上市场上常见两类:
| 对比项 | 标准 SG90(位置舵机) | SG90 360 度连续旋转舵机 |
|---|---|---|
| 输出轴限位 | 有机械限位,约 0~180 度 | 无机械限位,可连续旋转 |
| 内部电位器 | 有,用于位置反馈 | 无,或被固定替代 |
| 控制量 | PWM 脉宽对应目标角度 | PWM 脉宽对应方向和转速 |
| 典型行为 | 给一定脉宽,停在指定角度 | 给一定脉宽,持续朝某方向旋转 |
| 适合场景 | 机械臂、云台角度控制 | 小车驱动、摇头风扇、卷帘机构 |
从外部看,两种舵机都长一个样,重量也差不多,都是 9g 左右的微型舵机。购买时一定要向卖家确认是“180 度标准舵机”还是“360 度连续旋转舵机”,最好看产品标题和外包装,有些商家会把 360 度版本标成“带减速箱的连续旋转舵机”。
1.3 什么是开环控制
连续旋转舵机是一个典型的开环控制对象。所谓开环,是指控制器只管“输出指令”,不检测“实际结果”。比如我让舵机以某一个速度正转 2 秒,舵机到底转了多少圈,是否因为负载太大而转速下降,控制器并不知道。
因此,如果你试图用 360 度舵机精确控制云台转到某个固定角度,会发现很难。就算同样给writeMicroseconds(1700),空载时可能转得比较快,带负载后转速会明显下降。要做到精确定位,必须再加位置反馈,例如编码器或霍尔传感器,组成闭环控制。
本文的实测重点,是在理解这种开环特性的前提下,掌握如何让 360 度舵机稳定正转、反转、变速和停止。
2. 项目准备:硬件与接线
2.1 所需元件清单
我这次使用的环境如下:
- Arduino UNO 开发板
- SG90 360 度连续旋转舵机 1 个
- 面包板 + 若干公对公杜邦线
- 外部 5V 电源(可选,推荐)
- 100~470uF 电解电容(可选,用于电源稳定)
- 电脑 + Arduino IDE
如果你手头只有 USB 供电的 Arduino UNO,只接一个 SG90 空载测试也能转起来,但我不建议在带负载时继续依赖 Arduino 的 5V 引脚供电,原因后面单独讲。
2.2 SG90 引脚定义与接线方式
SG90 通常引出三根线,常见颜色和含义如下:
| 线色 | 作用 | 接到 Arduino UNO |
|---|---|---|
| 棕色或黑色 | GND | GND |
| 红色 | VCC / 电源正极 | 5V(或外部 5V 正极) |
| 橙色或黄色 | 信号线 | 数字引脚 D9 |
给出接线表:
SG90 360 度舵机 Arduino UNO 棕色线 --------------- GND 红色线 --------------- 5V 橙色线 --------------- D9这里有一个需要特别注意的地方:不同品牌、不同批次舵机的线序并不完全一致。有的舵机红色线在中间,有的是棕色线在中间,建议先查看舵机外壳上的丝印或商家提供的规格书,不要凭颜色盲接。接反电源线轻则舵机不转,重则烧毁舵机控制板。
由于本文需要演示舵机转动效果,建议把舵机轴朝上放在桌面上,方便观察输出轴旋转方向。
2.3 供电建议:为什么不能只靠 Arduino 5V
SG90 正常工作电压一般在 4.8V~6V 左右。单独一个舵机堵转时电流可能达到几百毫安甚至更高,启动瞬间也会有较大电流波动。
Arduino UNO 的 5V 引脚由 USB 或 Vin 降压后提供,能输出的电流有限。如果舵机负载较大,电流会把板载 5V 电压拉低,轻则舵机无力、抖动,重则 Arduino 直接重启,串口监视器会看到连接断开或输出乱码。
更稳妥的接法如下:
- 舵机红色线接外部 5V 电源正极。
- 舵机棕色线接外部 5V 电源负极,同时接到 Arduino 的 GND。
- Arduino UNO 单独通过 USB 供电。
这种“共地”做法很重要。Arduino 输出的是信号电平,而舵机的功率电流主要从外部电源获取。信号参考地必须和电源地一致,否则信号无法形成完整回路。
如果手头没有外部电源,临时测试时可以用 Arduino 5V,但最好在 Arduino 5V 和 GND 之间并联一个 100uF 以上的电解电容。注意电容正极接 5V,负极接 GND,反接会损坏电容。
3. 控制核心:PWM 脉宽与 writeMicroseconds
3.1 舵机识别什么样的 PWM 信号
舵机的信号本质是一路周期约 20ms 的 PWM 方波,也就是 50Hz。输出脉冲的宽度通常被称为脉宽,单位是微秒。
标准位置舵机的脉宽和角度大致对应关系如下:
0.5ms -> 0 度左右 1.5ms -> 90 度左右 2.5ms -> 180 度左右不过实际电路中,SG90 的脉宽范围通常取 1ms~2ms 更常见,比如 1ms 对应 0 度,1.5ms 对应 90 度,2ms 对应 180 度。这也是为什么很多人习惯调用writeMicroseconds(1000)、writeMicroseconds(1500)和writeMicroseconds(2000)来让舵机分别转到两端和中间。
对 360 度连续旋转舵机来说,脉宽不再对应位置,而对应方向和转速,因此不能用“脉宽等于角度”的思路去理解。常见对应关系如下:
约 1500us:停止 大于 1500us:一个方向连续旋转,脉宽越大转速越快 小于 1500us:另一个方向连续旋转,脉宽越小转速越快更精确地说,舵机内部有一个“中心点”脉宽,通常默认在 1500us 附近。只要信号脉宽接近中心点,舵机就不会转;偏离中心点越远,目标转速越快。
3.2 writeMicroseconds 与 write 的区别
Arduino 的 Servo 库提供了两个常用方法:
servo.write(angle); servo.writeMicroseconds(pulseWidth);write()是为普通位置舵机设计的,参数单位是角度。比如servo.write(90)会转换成约 1.5ms 的脉宽输出。如果你使用的是连续旋转舵机,还按角度去思考就会出现混乱。
writeMicroseconds()则直接输出指定的脉宽,单位是微秒,绕开了“角度换算”的中间层。连续旋转舵机本身没有角度概念,用这个函数更直观:
servo.writeMicroseconds(1500); // 停止 servo.writeMicroseconds(1700); // 正向旋转,速度较快 servo.writeMicroseconds(1300); // 反向旋转,速度较快使用 Servo 库时还会用到attach(pin),它默认支持标准舵机脉宽范围。如果希望自定义角度映射范围,可以写成:
servo.attach(9, 1000, 2000);这里第二个和第三个参数是角度换算的最小脉宽和最大脉宽。对于 360 度舵机,我们不依赖write()的角度换算,所以通常直接attach(9)就够了。
有一点需要提醒:Servo 库对脉冲宽度有一定限制,太极端或太宽的脉宽可能被裁剪掉。我们实际常用范围在 1000~2000us 之间,完全在支持范围内,不需要担心。
3.3 找到自己舵机的停止脉宽
不同厂家生产的 360 度舵机,中心点不一定是精确的 1500us。有的舵机写 1500us 可能还在缓慢转动,需要手动微调到 1480us 或 1520us 才会停。
因此拿到舵机后,先做一个中心点测试非常有必要。最简单的测试代码如下:
#include <Servo.h> Servo sg90; void setup() { sg90.attach(9); sg90.writeMicroseconds(1500); } void loop() { // 让舵机一直保持 1500us 脉宽,观察是否停止 }把程序上传到 Arduino 后观察舵机。如果它还在缓慢转动,就把脉宽改成 1480、1490、1510、1520 逐个测试,找到让它完全静止的那个值。后面所有速度控制,都围绕这个实测出的中心值展开。
4. 实战:Arduino UNO 控制 360 度舵机正转、反转、变速、停止
4.1 演示逻辑设计
接下来完成一个完整可运行的实测程序。为了直观地展示 360 度舵机的行为,我们分阶段让舵机执行以下动作:
高速正转 2 秒 中速正转 2 秒 停止 2 秒 中速反转 2 秒 高速反转 2 秒 停止 3 秒正转方向取决于舵机安装方式,如果发现方向反了,代码中正反转的脉宽对调即可。
4.2 编写完整代码:固定动作演示
新建一个 Arduino 工程,在 IDE 中创建文件,写入如下代码:
#include <Servo.h> Servo sg90; const int servoPin = 9; const int stopPulse = 1500; // 实测中心停止脉宽 void setup() { sg90.attach(servoPin); sg90.writeMicroseconds(stopPulse); delay(500); } void loop() { // 1. 高速正转 sg90.writeMicroseconds(1800); delay(2000); // 2. 中速正转 sg90.writeMicroseconds(1650); delay(2000); // 3. 停止 sg90.writeMicroseconds(stopPulse); delay(2000); // 4. 中速反转 sg90.writeMicroseconds(1350); delay(2000); // 5. 高速反转 sg90.writeMicroseconds(1200); delay(2000); // 6. 停止 sg90.writeMicroseconds(stopPulse); delay(3000); }代码逻辑很简单:
#include <Servo.h>引入 Servo 库。sg90.attach(servoPin)把舵机信号线绑定到 D9 引脚。- 每次切换速度前,先调用一次
writeMicroseconds(),然后延时一段时间,让舵机转够指定时长。
这里有一个细节:连续旋转舵机写入脉宽后,舵机会持续保持当前转速,不会自动停。所以程序里每个阶段都要明确写新的脉宽。最后写1500us就是为了让舵机停下来。
4.3 编写完整代码:串口在线调速
固定动作演示只能验证舵机基本能力,不方便测试不同脉宽对应的实际转速。我们再用串口做一个更实用的“在线调速”工具:电脑向 Arduino 发送一个脉宽值,舵机立刻按该脉宽运行。
完整代码如下:
#include <Servo.h> Servo sg90; const int servoPin = 9; void setup() { Serial.begin(9600); sg90.attach(servoPin); sg90.writeMicroseconds(1500); Serial.println("SG90 360 servo serial test"); Serial.println("Please input pulse width, e.g. 1500"); } void loop() { if (Serial.available() > 0) { int pulse = Serial.parseInt(); // 简单范围保护,避免输入明显异常的值 if (pulse >= 800 && pulse <= 2200) { sg90.writeMicroseconds(pulse); Serial.print("Set pulse: "); Serial.println(pulse); } else { Serial.println("Out of range, please input 800-2200"); } // 清空串口缓冲区,避免连续触发多次 while (Serial.available() > 0) { Serial.read(); } } }上传后打开 Arduino IDE 的串口监视器,把波特率设为 9600,在输入框中依次输入:
1500 1600 1700 1800 1400 1300 1200观察舵机变化。输入 1500 时舵机应停止;输入 1700、1800 时朝一个方向旋转,脉宽越大转速越快;输入 1300、1200 时朝另一个方向旋转,脉宽越小转速越快。
4.4 运行与预期结果说明
正常现象如下:
- 写入 1500us,舵机轴基本静止,用手轻捏输出轴能感觉到电机处于锁止或不输出扭矩的状态。
- 写入 1700us,舵机以较慢速度持续朝一个方向旋转。
- 写入 1800us,舵机更快地朝同方向旋转。
- 写入 1300us,舵机朝另一个方向旋转。
- 写入 1200us,反转速度更快。
如果通电后舵机不仅不转,还发出“嗡嗡”的抖动声,通常说明信号不正常或供电不足,可以按照第 5 节排查。
4.5 结合电位器做一个简易转速调节
如果希望不依赖电脑串口,也可以用一个 10k 电位器来调节 PWM 脉宽。将电位器两端接 Arduino 5V 和 GND,中间抽头接 A0 引脚,然后用analogRead()读到的 0~1023 映射到 1000~2000us。示例代码如下:
#include <Servo.h> Servo sg90; const int potPin = A0; const int servoPin = 9; void setup() { sg90.attach(servoPin); sg90.writeMicroseconds(1500); } void loop() { int potValue = analogRead(potPin); // 将 0~1023 映射到 1000~2000us int pulse = map(potValue, 0, 1023, 1000, 2000); sg90.writeMicroseconds(pulse); delay(15); }这个例子说明了一个关键点:360 度舵机的输入信号在 1000~2000us 之间是连续可调的,因此改变电位器位置,就能平滑改变舵机转速和方向。电位器旋到中间位置时输出约 1500us,舵机停止;往两边旋转时,舵机开始加速转动。
5. 常见问题与排查思路
5.1 问题汇总表
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 舵机不转,一点反应都没有 | 接线错误或供电不足 | 核对三条线,改用外部 5V 电源 |
| 舵机抖动、进退反复 | 信号线接触不良或脉宽处于临界死区 | 重新插紧杜邦线,微调脉宽避开临界值 |
| 舵机一直转、无法停止 | 停止脉宽不是 1500us | 用 3.3 节方法找到真实中心点 |
| 转动方向与预期相反 | 正反转脉宽定义反了 | 把大于中心点和小于中心点的值对调 |
| 一接舵机 Arduino 就重启 | 舵机启动电流拉低板载电压 | 使用外部电源,增加滤波电容 |
| 带负载后转速明显下降 | 电源功率不足或舵机超载 | 换更大电流电源,或降低机械负载 |
| 想转固定角度但始终不准 | 360 度舵机无位置反馈 | 改用反馈舵机或添加编码器闭环 |
5.2 Arduino 一接舵机就重启
这是最常见的问题。由于舵机启动和堵转时电流突增,Arduino 的稳压器件会被严重拉低,甚至 USB 供电自动复位。
解决步骤:
- 断开舵机红线与 Arduino 5V 的连接。
- 使用外部 5V 电源的正极接舵机红线。
- 外部电源负极同时接舵机棕线和 Arduino GND。
如果仍然不稳定,可以在舵机电源两端并联一个 100~470uF 电解电容,电容尽量靠近舵机电源引脚。
5.3 停止位置需要校准
一部分 360 度舵机标称中心点就是 1500us,但实际会有偏差。比如我手头有一颗舵机,写入 1500us 时还在缓慢正转,改成 1470us 才停。
校准步骤可以按下面来:
- 先把脉宽设成 1500us,观察舵机是否停。
- 如果还转,每次增减 10us,例如 1480、1470、1460。
- 找到“临界停止点”,如果 1470us 不转但 1460us 会反转,说明停止点就在两者之间。
- 把这个停止点保存为常量
stopPulse,后续代码都用它代替固定 1500。
校准之后,转速控制才能准确。否则你会觉得舵机转速忽快忽慢,其实只是中心点偏移导致的“假象”。
5.4 脉宽接近中心值时舵机抖动
在中心点附近,舵机控制电路会进入临界状态,可能出现输出轴轻微抖动或发出电流声。这是正常现象。工程上可以设置一个“死区”,让脉宽落在一定范围内时强制输出停止脉宽:
if (pulse > 1490 && pulse < 1510) { sg90.writeMicroseconds(stopPulse); } else { sg90.writeMicroseconds(pulse); }这样执行器就不会因为微小脉宽变化而频繁抖动。
6. 最佳实践与工程建议
6.1 供电设计放在第一位
微型 360 度舵机虽然看起来小,但工程应用时必须把供电当成大头问题。
建议:
- 不要用 Arduino 5V 引脚驱动多个舵机。
- 选用能持续输出 1A 以上的 5V 电源,具体按舵机数量和负载评估。
- 舵机电源与单片机控制板共地,但尽量分别供电。
- 长线供电时,在舵机端并联一个电容。
如果使用锂电池或大容量电源,还应该在电源输入端并联一个大电容,避免热插拔时拉弧损坏接口。
6.2 把“停止脉宽”定义成常量
与其在代码里到处写 1500,不如在校准后给它一个名字:
const int SERVO_STOP_US = 1470;这样后期如果更换舵机,只需要修改这一处常量,不需要搜索整个程序替换数字。这也是良好的代码习惯。
6.3 明确开环控制的边界
360 度连续旋转舵机的核心特征是开环速度控制,它适合以下场景:
- 摇头风扇叶片旋转。
- 摄像头持续扫描。
- 小车简单前进后退。
- 机械结构往复运动。
它不适合以下场景:
- 云台需要精确停在某个角度。
- 机械臂关节需要位置保持。
- 需要判断当前是否发生堵转。
- 需要知道舵机转了多少圈。
如果你的项目是上面这些“位置敏感”需求,建议换回标准位置舵机,或选用带反馈的 360 度舵机、双轴编码器电机,再配合 PID 控制器实现闭环。
6.4 程序结构建议
即使是一个小小的舵机控制程序,我也建议把关键参数集中管理。示例:
#include <Servo.h> Servo sg90; const int SERVO_PIN = 9; const int STOP_PULSE_US = 1500; const int MAX_SPEED_US = 1800; const int MIN_SPEED_US = 1200; void setup() { sg90.attach(SERVO_PIN); sg90.writeMicroseconds(STOP_PULSE_US); } void drive(int pulseUs, int durationMs) { sg90.writeMicroseconds(pulseUs); delay(durationMs); } void loop() { drive(MAX_SPEED_US, 2000); drive(STOP_PULSE_US, 1000); drive(MIN_SPEED_US, 2000); drive(STOP_PULSE_US, 1000); }封装之后,后续如果要增加更多动作,只需要维护drive()的调用序列即可。对稍微复杂一点的项目,还能用状态机代替delay(),这里不展开。
6.5 安全与异常保护
连续旋转舵机的输出轴一直转动,如果机械结构卡住,电机会持续堵转并发热,严重时会烧坏舵机内部驱动芯片。
在项目中建议:
- 在程序中增加运行时间保护,避免舵机长时间同一方向运行。
- 机械结构预留限位或过载打滑空间。
- 必要时在电源回路中串联自恢复保险丝。
举个例子,如果你在做一个自动卷帘机构,舵机正转 3 秒应该到达上限,但万一限位开关失效,程序就会一直输出正转信号。此时可以加一个简单判断:连续正转超过 5 秒就强制停止并报错。
7. 总结与上手清单
写到这里,关于 SG90 360 度连续旋转舵机的基础知识和测试方法已经讲得比较完整了。我建议你拿到舵机后,按下面这个顺序做一遍初测:
第 1 步:确认舵机确实是 360 度连续旋转版本。 第 2 步:正确接线,尽量使用外部电源并共地。 第 3 步:写入 1500us,观察舵机是否停止。 第 4 步:如果还在转,用串口工具微调出真实停止脉宽。 第 5 步:分别测试 1700us 和 1300us,确认正反转方向。 第 6 步:把测试结果整理成常量,再开始做实际项目。这样做的好处是,你不会跳过供电和停止脉宽校准这两个最容易踩坑的步骤。后面无论是做云台摇头、小车驱动,还是其他自动化工装,都能少走不少弯路。如果遇到舵机抖动、方向相反或 Arduino 重启的问题,可以直接参考本文第 5 节的排查表。