舵机控制
288.6.舵机控制
#define ServoPin 12 // 将视频选用的 GPIO12 命名为 ServoPin,后面用名字引用该引脚。
void Myservo(int angle) // 定义接收目标角度的舵机控制函数;函数名和大小写照视频保留。
{ // 开始执行 Myservo 函数中的语句。
for (int i = 0; i < 5; i++) { // 按视频写法,连续发送 5 个同一目标角度的控制脉冲。
int pulsewidth = (angle * 11) + 500; // 按课堂公式计算高电平宽度,单位为微秒。
digitalWrite(ServoPin, HIGH); // 把 GPIO12 拉高,开始本次控制脉冲。
delayMicroseconds(pulsewidth); // 保持高电平 pulsewidth 微秒。
digitalWrite(ServoPin, LOW); // 把 GPIO12 拉低,结束本次高电平脉冲。
delayMicroseconds(20000 - pulsewidth); // 保持低电平至本轮名义上的 20 000 微秒结束。
} // 五轮高、低电平发送结束。
delay(10); // 五个脉冲之后再等待 10 毫秒;不是持续输出脉冲。
} // Myservo 函数结束。
void setup() { // 定义 Arduino 上电或复位后执行一次的初始化函数。
pinMode(ServoPin,OUTPUT); // 将 GPIO12 配置成输出模式,以便发送高、低电平。
} // 初始化结束。
void loop() { // 定义 Arduino 不断重复执行的主循环。
Myservo(0); // 向舵机发送表示课堂预设 0° 的一组脉冲。
delay(2000); // 暂停 2000 毫秒,再请求下一个角度。
Myservo(45); // 向舵机发送表示课堂预设 45° 的一组脉冲。
delay(2000); // 暂停约 2 秒。
Myservo(90); // 向舵机发送表示课堂预设 90° 的一组脉冲。
delay(2000); // 暂停约 2 秒。
Myservo(145); // 向舵机发送表示课堂预设 145° 的一组脉冲。
delay(2000); // 暂停约 2 秒;之后 loop 重来,重新请求 0°。
} // 本轮主循环结束,Arduino 框架会再次调用 loop。
一、舵机控制
本节使用 AI Thinker ESP32-CAM 的 GPIO12 驱动视频中的 SG90 舵机。上方是视频最终草图实际写出的业务代码:移除了 PPT 和 IDE 原有注释,仅在每个非空行增加讲解,保留 Myservo、pulsewidth、angle * 11 + 500、发送 5 次和 delay(10) 等原样写法。这里没有调用 Servo 库,也没有使用 ESP32 硬件 PWM;是程序依次拉高、延时、拉低 GPIO 来“软件生成”控制脉冲。以下按原笔记的四个小节展开,并区分课堂演示、公式近似与硬件额定要求。
1.1 舵机控制原理
- 视频 PPT 画的是可按角度定位的舵机:大约每隔
20 ms = 20 000 μs发送一个脉冲,也就是约50 Hz。所谓“高电平宽度”,是这一周期开始后信号维持高电平的时间;剩余时间为低电平。例如高1.5 ms、低约18.5 ms,构成一个约20 ms的周期。ms是毫秒,μs是微秒,1 ms = 1000 μs。PPT 依次画出约0.5 ms → 0°、1 ms → 45°、1.5 ms → 90°、2 ms → 145°、2.5 ms → 180°,用于理解“脉宽与目标位置有关”。 - 脉冲宽度占
20 ms周期的比例也叫占空比,PPT 的0.5~2.5 ms对应2.5%~12.5%;但舵机接收的是脉冲高电平持续多久,不能以为只要占空比相同、随意改变频率就会得到同一位置。0.5/1.5/2.5 ms 与 0/90/180° 是这节课采用的示意映射,不是所有 SG90、所有舵机的普适标定值。不同型号的允许脉宽、机械端点和零位可能不同;越过机械端点可能导致堵转、电流增大。Arduino 官方 Servo 文档也明确指出各厂家端点脉宽并不完全一致。 - 普通定位舵机内部不是“高电平时才转,低电平时就反转”:内部控制电路读取脉宽,把它解释为目标位置,随后由内部电机和反馈机构尝试转到目标。信号线只是控制输入,不能提供舵机电机所需的工作电流。视频配套硬件清单曾写“SG90(360 度)”,但 TowerPro 将普通 SG90和SG90 360° 连续旋转版列为不同产品;连续旋转版的脉宽主要对应转动方向、速度或停止点,不能按本节公式定位到 45°、90°。应以实物型号和规格确认所用的是定位版,不能只看名称“SG90”。
1.2 控制舵机函数
-
#define ServoPin 12是预处理宏:编译前把后面出现的ServoPin替换成12。12指芯片的 GPIO12/IO12,不是第 12 个物理孔位;一旦以后更换信号引脚,只需统一修改宏定义并检查该脚是否可用。Myservo(int angle)中的angle是调用时传入的期望角度,不是舵机实时测量值;该函数也不读取舵机实际位置。 -
视频实际公式为
pulsewidth = angle * 11 + 500,单位是 μs。其端点是 0° → 500 μs、180° → 2480 μs;系数 11 是为了近似课堂图中的 0.5~2.5 ms 范围,并非精确的(2500-500)/180 ≈ 11.11。代码的int只做整数运算,这里乘以整数 11 不会出现小数结果。看几个代入值更容易理解:调用参数 angle代码算得高电平 名义低电平 20000 - pulsewidth与视频示意图的关系 0° 500 μs = 0.500 ms 19 500 μs 对应示意图的 0.5 ms 45° 995 μs = 0.995 ms 19 005 μs 约 1 ms,不能写成精确 1000 μs 90° 1490 μs = 1.490 ms 18 510 μs 约 1.5 ms 145° 2095 μs = 2.095 ms 17 905 μs PPT 的 2 ms 只是示意近似 180° 2480 μs = 2.480 ms 17 520 μs 约 2.5 ms;主循环没有调用 180° -
每轮
for先digitalWrite(ServoPin, HIGH),等待pulsewidth微秒,再输出LOW,等待20000 - pulsewidth微秒。这样按代码请求的延时相加是 20 000 μs;实际 GPIO 调用、循环和系统调度也要耗时,因此示波器测到的周期未必精确 20 ms。这里手工延时会占住程序执行;与将来同时运行摄像头、Wi‑Fi 等任务相比,这只是便于理解的入门演示,不能把它等同于持续、精确的硬件 PWM。 -
i从 0 开始,在i < 5时执行,因此依次是 0、1、2、3、4,正好 5 个脉冲,名义发送时间为5 × 20 ms = 100 ms;退出循环后另有delay(10),即再等 10 ms。随后loop()中每个角度后还有delay(2000),约 2 秒内没有继续生成该角度的脉冲,引脚维持低电平。故它只是“短暂发送一组定位命令”,不是整整 2 秒持续维持 50 Hz 信号;若舵机未到位、受负载扰动或需要保持力矩,本例不能保证可靠保持。这里是解释原代码行为,不擅自改写成另一套代码。 -
从公式的设计意图看是覆盖
0~180°,但函数本身没有范围检查,传入异常角度还可能让20000 - pulsewidth不合理。实际可转的机械范围和端点脉宽必须按手中舵机规格确认,不要用手硬拧舵盘或长时间顶住机械止挡。
1.3 循环调用函数
setup()只运行一次,pinMode(ServoPin,OUTPUT);将 GPIO12 配成输出;loop()会不断从头执行,每次按 0° → 45° → 90° → 145° → 下一轮的 0° 请求位置。每次Myservo(...)自身发送约 100 ms 脉冲并等待 10 ms,后面的delay(2000)才是约 2 秒的观察间隔;因此相邻目标角度的开始时间并不是严格每隔 2 秒。delay(2000)的单位是毫秒,而delayMicroseconds(...)的单位是微秒;转录中把“两秒”误识为“两毫秒”。代码中的#define、括号、逗号和分号均用英文半角符号;中文输入法打出的全角括号、逗号等不能替代 C/C++ 语法符号。函数名Myservo的大小写也要保持一致,不能写成MyServo或myservo。视频虽然使用 Arduino IDE 编译,草图实际按 C++ 规则处理;这里依readme.md要求把课堂业务代码放在c代码围栏中。- 注意观察“目标值”与“实际转到的角度”不是同一个量。本例没有角度传感器回读,也没有串口打印实际位置;演示中看见舵盘转动,可以验证控制信号产生了效果,但不足以证明每一站都精确到标称角度。
1.4 舵机转动效果
- **先认清三根线及回路:**视频中的橙线是控制信号,连 ESP32-CAM 的
IO12/GPIO12;红线是舵机电源正极V+;棕线是地GND。信号只在相对共同地的电压下有意义,所以舵机地与 ESP32-CAM 地必须相连;舵机电机电流则应走舵机专用电源正极和回路,不要通过 GPIO12 给电机供电。上电前对照实物插头、标识确认三线顺序;接错正负极或把电源误接信号脚有损坏风险,改接线时先断电。 - 课堂画面与推荐供电要分开看:视频把舵机红线接在 ESP32-CAM 板上的
3.3V,并演示其转动,这只说明该次实物实验出现了转动。TowerPro 普通 SG90 产品页给出的工作电压是 4.8 V,其连续旋转版为 4.8~6 V,均不是 3.3 V;且舵机启动和负载变化会要求足够的供电电流。复现实验应先核实所购舵机的型号、额定电压和供电能力,优先用符合其规格的独立稳压电源供给舵机V+,将该电源负极与 ESP32-CAMGND共地,并确认舵机控制输入能识别 ESP32 的 3.3 V 信号。不能因为板上有一个3.3V排针就认定它适合给电机供电,也不能把 4.8/5 V 电源正极直接接入 GPIO12。 - **IO12 不是毫无限制的“空闲脚”:**ESP32 的 GPIO12/MTDI 在复位时也是启动配置脚。对使用 3.3 V Flash 的相关模块,复位瞬间若被外部电路拉高,会误选 1.8 V Flash 供电并可能导致启动、烧录失败;本例舵机信号线接在 IO12 上,遇到启动异常应检查外设在复位时是否影响该脚,必要时先断开舵机信号线排查。此外 GPIO12 也是 SD 卡总线的
DAT2,以后若启用 microSD 的四线数据模式,就不能再不加分析地同时把它当舵机专用输出。Espressif 启动脚说明、SD 总线说明。 - **按视频的烧录/运行顺序:**烧录前将
GPIO0/IO0临时接GND,接好 USB 转串口及板子供电,在 Arduino IDE 中选AI Thinker ESP32-CAM与实际 COM 口,上传草图;GPIO0 在复位时为低,芯片才进入串口下载模式。确认 IDE 写入、校验完成后,断电或确保线路安全地拔掉 GPIO0–GND 下载跳线,按复位键(或重新上电)进入正常运行,再接好舵机三线。不能把“拔掉烧录跳线”误作“拔掉供电”:板子和舵机仍需各自合适的电源。若视频那样串口断开/上传失败,先核对 COM 口是否仍存在、串口线与供电、GPIO0 跳线和复位时机;重插 USB 后重新选择端口再试,不是盲目反复烧录。Espressif 下载模式说明。 - 成功运行时视频展示舵盘按 0、45、90、145 的顺序改变朝向,下一轮再回到 0,持续重复。若舵机抖动或板子复位,先排查电源压降、共地、接线与型号;若只是转个不停而不在角度处停下,尤其要核对是否误用了360° 连续旋转版。实际效果同时受舵机型号、负载、脉宽标定和每次只发送 5 脉冲的限制,不把课堂画面描述成所有器件都能精确复现的保证。
核对依据
- 本地
videos/288.6.舵机控制.mp4的 PPT、Arduino IDE 最终草图和实物接线/动作画面,与transcripts/288.6.舵机控制.txt、原始notes/md/288.6.舵机控制.md交叉核对;代码以上述视频最终画面为准,没有用后续小车项目的综合代码替代。第 284 节课程硬件清单中的“SG90(360 度)”仅用于识别规格矛盾。 - TowerPro 普通 SG90与360° 连续旋转版的制造商产品页:核对版本区别和额定供电;Arduino Servo 官方接口说明及Adafruit 舵机脉宽讲解:核对脉宽、定位与连续旋转的区别。此处仅援引它们解释原理,未把库代码写进课堂草图。
- Espressif ESP32 启动脚与下载模式、SD 卡引脚与 GPIO12 注意事项:核对 GPIO0、GPIO12 的特殊用途。