SG90连续旋转舵机与角度舵机的区别及Arduino控制方法
2026/9/12 5:15:37 网站建设 项目流程

很多 Arduino 初学者会在同一批项目中遇到两种长得很像的 SG90:一种是普通角度舵机,用来控制云台或机械臂的旋转角度;另一种是贴了“360 度”标签的连续旋转版,经常被拿来当小车轮子或旋转平台。实际测试时,后者用write(90)并不能像预期那样指向中间位置,有时甚至是一直接着转。问题并不是舵机坏了,而是两种舵机对同一个信号的解释方式完全不同。

普通的 180 度舵机把脉宽解释成“目标角度”,内部通过电位器反馈让输出轴停在指定角度;360 度连续旋转版则把脉宽解释成“方向与速度”,脉宽接近中位时停止,偏离中位时就朝某一方向连续旋转。外部只负责发脉宽,不读取实际转速或转过圈数,这种用法就是典型开环控制。下面用 Arduino UNO 配合一块 SG90 360 度连续旋转舵机,把writeMicroseconds的用法、停止点校准、方向验证和故障排查整条链路都说清楚。

1. 为什么这块“SG90”不是转到指定角度,而是一直转

1.1 普通角度舵机和 360 度连续旋转舵机的内部差别

普通 SG90 属于标准位置型舵机。输出轴旁边有一颗电位器,电位器会随着齿轮转动改变阻值。舵机里的控制板比较“目标位置对应的电压”和“当前电位器反馈的电压”,两者一致就停止驱动电机。所以程序里只要告诉它目标角度,最终输出轴总会停在那个角度附近。这个内部反馈是小舵机能精确停在指定角度的基础。

360 度连续旋转版在设计上把机械限位去掉,同时取消或弱化了位置电位器反馈。控制板不再追求“让输出轴停到某个角度”,而是根据输入脉宽决定电机向哪个方向转、以多快速度转。输出轴可以连续不断旋转,没有 0 度到 180 度这样的边界。因此,标准舵机写的write(90)在 360 度版本上不产生“转到 90 度”的效果,只会被换算成某一脉宽,然后转换为对应的转速和方向。

1.2 用脉冲宽度决定方向与速度,而不是角度

舵机信号本质是一串周期约 20ms 的脉冲。角度舵机看的是高电平脉宽和角度之间的关系,例如不少产品按 0.5ms 到 2.5ms 对应 0 度到 180 度估算。360 度连续旋转舵机同样接收这个信号,但内部逻辑改成了先找“中点脉宽”,再比较输入脉宽和中点的差值。

  • 输入脉宽约等于中点脉宽时,舵机停止。
  • 输入脉宽明显小于中点脉宽时,舵机朝一个方向旋转。
  • 输入脉宽明显大于中点脉宽时,舵机朝相反方向旋转。
  • 离中点越远,转速越快。

这个概念最容易理解错的地方在于:很多人把“1ms”或“2ms”当成角度边界来记,实际上连续旋转舵机根本没有稳定的角度边界,信号只表示方向和转速。write(90)在这种场景下不如writeMicroseconds(1500)直白,因为后者直接告诉控制板当前发送的脉宽是多少微秒,不经过角度换算。

1.3 两种舵机选型对比

买舵机前先看标签和商品说明,不能只看是不是 SG90。同样是 SG90 外形,内部可能完全不同。

对比项普通 SG90 角度舵机SG90 360 度连续旋转版
输出效果输出轴按角度定位输出轴连续正转或反转
内部位置反馈有电位器反馈无位置反馈或已禁用位置反馈
典型控制信号0.5ms 到 2.5ms 对应角度约 1.5ms 停止,偏离中位控制方向与速度
write(90)大多转到 90 度附近不一定停止,取决于换算后的脉宽
writeMicroseconds(1500)通常停到约 90 度位置设计上应停止,但需要校准实际中点
是否适合做轮子不适合,角度范围受限适合做驱动轮,但精度依赖外部控制

下单和接线前先确认:如果是给小车装轮子,应该选连续旋转版;如果只是控制云台或舵机臂转动,普通版更合适。若实物没有明显标签,可以通过上电后发送 1500µs 观察:能持续旋转的是连续旋转版,转到固定位置停住的更可能是普通角度舵机。

2. 开环控制是什么:只管发信号,不管实际转没转

2.1 开环和闭环在舵机场景里的区别

开环控制的意思是控制器只发出指令,不读取执行结果。控制链路通常是“指令 -> 驱动电路 -> 电机 -> 负载”,缺少“反馈 -> 误差修正”这一环。

把普通 SG90 单独拿出来看,它内部其实有电位器闭环:目标角度和实际角度会被比较。用户面对的是“角度指令”这个接口,不需要自己处理位置反馈。但 360 度连续旋转舵机把内部位置反馈取消后,用户就只能向它发送速度控制信号。系统不会报告“我转了一圈”“我卡住了”或“我当前速度是多少”,所有关于转速和圈数的信息都需要外部补充。

这里也解释了一个常见误区:有人把“360 度舵机能转 360 度”理解成“能精确转一整圈然后停下来”。实际上,360 度版本没有办法从自身判断是否已经转完一圈。程序里写“高速旋转 2 秒再停止”,那是靠时间去估算转过的角度,不是舵机反馈得到的真实结果。这就是开环控制的典型特征。

2.2 360 度版本没有位置反馈,所以别把它当精确舵机用

实测中你会看到这样一个现象:同一段“满速转 1 秒”的程序,电池电压高时舵机转得快,转过的圈数多;电池电压低时转速下降,同样 1 秒转过的圈数少。因为程序只控制了时间,没有控制转速,也没有根据转速修正时间。

这不是代码写得不对,而是开环控制的固有限制。如果要让舵机作为机器人驱动轮并可靠计算里程,最好在电机输出轴或轮子上增加编码器,再配合闭环控制。输入材料如果没有提供编码器方案,那么这块 SG90 连续旋转舵机更适合先用开环方式验证电机能力,而不是直接承担高精度定位任务。

3. 接线和参数准备:Arduino UNO 与 SG90 360 的连法

3.1 硬件清单和连线方式

做入门实测不需要复杂设备。准备一块 Arduino UNO、一个 SG90 360 度连续旋转舵机、几根杜邦线、一台安装好 Arduino IDE 的电脑即可。若舵机带负载或要多只电机同时测试,再准备一个外部 5V 电源和一个 100µF 到 470µF 的电解电容。

SG90 舵机通常引出三根线,颜色有棕色、红色、橙色,也可能是黑色、红色、黄色。接线对应关系如下:

舵机引线颜色功能Arduino UNO 接法
棕色或黑色GNDGND
红色VCC5V 或外部 5V 电源正极
橙色或黄色信号数字引脚 9

红色线不能接到 3.3V,也不能接到高于 6V 的电源上。常见 9g 舵机工作电压以 4.8V 到 6V 居多,Arduino UNO 的 5V 引脚适合单只舵机空载测试。如果舵机堵转或负载较大,电流可能从几百毫安升到更高,依靠 UNO 板载稳压器供电会造成电压跌落,严重时 Arduino 会重启,因此多舵机或带负载场景应使用独立电源。

3.2 舵机控制信号不是普通占空比,关键是脉宽周期

舵机控制要求的是一个大约 20ms 周期内的高电平持续时间。Arduino 的Servo库在调用attach()后会自动产生约 50Hz 的更新频率,并维护正确的高电平时间。使用Servo库时,writeMicroseconds(1500)表示每个周期内输出 1500µs 的高电平。

很多初学者看到“PWM”就尝试用analogWrite(),这是不合适的。analogWrite()控制的是占空比,例如 90 和 255 的比例,切换频率固定,不直接产生舵机所需的 1ms 到 2ms 脉宽。标准做法是让舵机信号引脚连接 Arduino 的 PWM 引脚,并使用内置Servo库。

信号参数常见值说明
信号周期约 20ms典型 50Hz 刷新
高电平脉宽500µs 到 2500µs具体范围以舵机型号资料为准
实践常用脉宽1000µs 到 2000µs很多 360 版按此范围标定
中点脉宽接近 1500µs因批次可能偏移,需要实测校准
速度关系偏离中点越远,转速越高到边界后可能进入平台期

3.3writeMicrosecondswrite的区别和正确用法

Servo库提供两个常用方法:

  • write(angle):输入 0 到 180 表示角度,库内部把角度换算成脉宽。
  • writeMicroseconds(value):直接输入高电平时间,单位是微秒。

标准角度舵机用write(90)很好理解。连续旋转舵机再用角度去换算反而容易造成混淆,因为你不知道 90 对应的是中点还是偏转方向。使用writeMicroseconds能让程序意图更清晰:写 1500 是中点,写小于中点的是正转,写大于中点的是反转。

调用attach(pin, min, max)时,minmax会影响write()的角度到脉宽映射。如果不指定,Arduino 内置 Servo 库通常会按较宽的默认 MIN 和 MAX 脉宽处理。为了让write()的换算也靠近 1000µs 到 2000µs 范围,有的程序会写成attach(pin, 1000, 2000)。但最终建议仍是用writeMicroseconds()控制连续旋转舵机,因为它不经过角度语义,直接表示你要发多少脉宽,排查时思路更清晰。

4. 编写第一个实测程序:找“停止点”并验证方向

4.1 Arduino IDE 环境准备

使用 Arduino IDE 1.8 或 2.x 都可以。Servo库是 Arduino AVR 核心自带的库,不需要额外安装。把 Arduino UNO 用 USB 线连接电脑,在“工具 -> 开发板”中选择 Arduino Uno,在“工具 -> 端口”中选择对应串口,然后编译上传。

这里要特别注意:舵机信号线只接一个引脚,程序里的引脚号必须和实际接线一致。例如信号线接在 D9,代码里就写 9。引脚不一致是最常见的“编译成功但不动作”的原因。

4.2 最小停止程序:先把 1500µs 作为假想中点

第一次上电不要急着控制小车前进,先写一个最简程序,让舵机收到 1500µs 信号。程序如下:

#include <Servo.h> Servo myServo; const int servoPin = 9; void setup() { myServo.attach(servoPin, 1000, 2000); myServo.writeMicroseconds(1500); } void loop() { // 保持当前脉宽输出 }

上传完成后观察舵机。若它完全停止,说明这块舵机的中点确实接近 1500µs。若它还在缓慢旋转或抖动,说明中点并不在 1500µs,需要进入下一步校准。

4.3 通过串口实时输入脉宽,找出真实中点

找一个准确的停止点,是做后续所有方向测试的基础。可以用一段通过串口输入脉宽的程序来逐点试探。

#include <Servo.h> Servo myServo; const int servoPin = 9; void setup() { Serial.begin(9600); myServo.attach(servoPin, 1000, 2000); myServo.writeMicroseconds(1500); Serial.println("input pulse width in microseconds:"); Serial.println("example: 1500 1490 1480 1510 1520"); } void loop() { if (Serial.available() > 0) { int value = Serial.parseInt(); if (value >= 500 && value <= 2500) { myServo.writeMicroseconds(value); Serial.print("pulse = "); Serial.println(value); } else { Serial.println("out of safe range"); } } }

数据在 Arduino 串口监视器的输入框中直接发送。试探顺序建议从 1500µs 开始,然后依次输入 1490、1480、1470,观察舵机是否从旋转变成停止。如果 1500µs 时它朝一个方向转,后面会有一个时刻开始反向,两个方向交界处附近通常存在一个停止区,选择一个能稳定停住的值作为stopPulse

这个过程中可能出现一个几微秒宽的“死区”,也就是脉宽在 1460µs 到 1480µs 之间舵机都不明显转动。出现死区时不要担心,取死区中间值即可。连续旋转舵机的中点是硬件参数,不同批次可能不同,程序里不能把 1500µs 当成永远有效。

注意:测试时不要让舵机长时间堵转,例如用手捏住输出轴不让它转。小舵机堵转电流会明显上升,长时间堵转会发热,严重时可能损坏舵机内部驱动。

5. 用一个串口遥控测试验证开环控制特性

5.1 功能设计和测试步骤

校准出停止点后,就可以验证开环控制的完整行为。设计目标是:用串口输入不同命令,舵机分别执行停止、慢速正转、快速正转、慢速反转、快速反转。

这里不需要精确知道转速是多少,只需要观察方向是否随脉宽变化、速度是否随偏移量变化。先把停止点保存为stopPulse,再用“停止点 ± 偏移量”生成低速和高速信号。例如停止点是 1470µs,慢速信号可以用 1390µs 和 1550µs,快速信号可以用 1250µs 和 1690µs。

5.2 完整示例代码

下面是串口控制测试程序,方向命名先用方向 A 和方向 B 表示。实际测试若发现方向相反,只调整符号即可。

#include <Servo.h> Servo myServo; const int servoPin = 9; // 假设实测停止点为 1470,请替换成你自己校准出来的值 const int stopPulse = 1470; void setup() { Serial.begin(9600); myServo.attach(servoPin, 1000, 2000); myServo.writeMicroseconds(stopPulse); Serial.println("s: stop"); Serial.println("1: direction A low speed"); Serial.println("2: direction A high speed"); Serial.println("3: direction B low speed"); Serial.println("4: direction B high speed"); } void loop() { if (Serial.available() > 0) { char cmd = Serial.read(); switch (cmd) { case 's': myServo.writeMicroseconds(stopPulse); Serial.print("pulse = "); Serial.println(stopPulse); break; case '1': myServo.writeMicroseconds(stopPulse - 80); Serial.print("pulse = "); Serial.println(stopPulse - 80); break; case '2': myServo.writeMicroseconds(stopPulse - 220); Serial.print("pulse = "); Serial.println(stopPulse - 220); break; case '3': myServo.writeMicroseconds(stopPulse + 80); Serial.print("pulse = "); Serial.println(stopP

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

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

立即咨询