模块熟悉

第 18 课:舵机——让角度转起来

第一次做"模拟输出"——不是开或关,而是"转到 37 度"

我是吴老师。这节课是「输出」这一侧的分界线。

你回想一下前面那些课,凡是让东西"动起来"的,全都是同一个套路:灯要么亮要么灭,蜂鸣器要么响要么不响。它们只有两种状态——你从来没办法跟板子说"亮 37%"这种话,因为做不到。

但从舵机开始,事情变了。你要说的是:"转到 37 度。"

一个具体的、连续的、有零有整的角度。 这是你这门课第一次做模拟输出。第 10 课你学过模拟输入(拧旋钮读一个连续的值),今天我们在输出的方向上,把同一件事再做一遍。

一、先认识它

舵机就是一个能转到指定角度的小马达

它长得像个蓝色的小方块,顶上伸出来一个塑料小摇臂(叫"舵盘")。你让它转 30 度,它就转 30 度;你让它转 150 度,它就转 150 度。它不会转圈,只在一个扇形范围里来回走——普通舵机的范围是 0 ~ 180 度

套件里这颗是 MG90S,金属齿轮的小舵机,力气不大但很耐用,做云台、做机械臂、做指针表盘都够用。

说个有意思的事:舵机怎么知道"自己转到几度了"?

它内部除了电机和齿轮,还有一个小电位器——对,就是你第 10 课拧的那个东西。电机一转,它跟着转,舵机里的电路就靠它的电压变化知道"我现在到哪了";没到目标角度,就继续使劲。

你发现没有——这是一个做在舵机内部的、小小的闭环(第 9 课那个"传感器 → 判断 → 执行器")。你现在不用记,知道有这回事就行。

二、它要的信号:还是"快速开关"

板子怎么表达"转到 37 度"?

它没法直接吐出一个中间值的电压。引脚只会两件事:输出高电平(3.3V)或者低电平(0V)。 没有"输出 1.2V"这个选项。

这块 C3 芯片里没有 DAC 这个部件(别的型号的 ESP32 有些带),所以它天生就不会"输出中间电压"。你可以翻一下你板子的引脚图确认——查一遍比我告诉你更牢。

那怎么办?用"快速开关"来假装。

PWM 用快速开关模拟中间值,占空比越大等效电压越高

这个比喻你一定要记住:

你家老式的水龙头,只有"全开"和"全关",拧不到中间。 现在我需要"半桶水",怎么办?我快速开关——开一秒、关一秒、开一秒、关一秒……接在水桶里的水,平均下来正好是一半。

我没法把水龙头定在中间,但我可以让它"一半时间开着"。

电也是一样的:让引脚快速地在 3.3V 和 0V 之间切,切得够快,后面的东西感受到的就是一个"中间值"。

这招的正式名字叫 PWM(脉冲宽度调制,Pulse Width Modulation)。那个"开的时间占整体的比例",叫占空比(duty cycle)。

你看图上那三条波浪线:同一个开关动作,开的时间越长,平均下来就越"高"。灯是这样变亮的,舵机是这样变角度的——同一个道理。

舵机数的是"每次开多久"

不过舵机有个小脾气:它不看"平均多少",它看"每次开的那一下有多长"。

具体是这样:

每次"开"多久 舵机转到
1 毫秒
1.5 毫秒 90°
2 毫秒 180°

而且它要求这个开关动作每秒重复 50 次(也就是每 20 毫秒来一轮)。

你算一下:20 毫秒里开 1.5 毫秒,占空比是 7.5%。所以它说到底还是占空比,只不过数值很小,而且是固定的频率、只有"开多久"在变。

这三个数字(1ms / 1.5ms / 2ms)你不用背——库会帮你把"角度"换算成脉冲宽度,你只管写 write(90)。但你得知道底下发生了这么一回事,不然以后碰到舵机抖动、嗡嗡响,你就不知道该往哪儿想。

三、接线:橙、红、棕

舵机的线是三根并排的,颜色是固定的,认颜色就行

线色 是什么 接到
信号 GPIO7
电源 独立 5V正极
GND(要和板子共地

顺序就是橙 / 红 / 棕,顺着线看,一根挨一根。

注意:接错是有区别的。 橙线(信号)接错一般不会烧,顶多不动;红线和棕线接反(电源接反)会烧——这是要命的那个。所以插之前先看颜色,别凭手感,舵机那个插头长得很对称,反着也能插进去。

顺带说一句:GPIO7 只是我给你的建议(图形化平台里舵机的默认脚也是它)。信号线接哪个 GPIO 都行,只有电源那两根不能乱接。

四、最大的坑:电不能从板子上取

这是这节课最重要的内容。

先把现象告诉你,你等下会亲眼看到:你把舵机的红线接到板子的 3.3V 上,一上电——舵机抖一下,然后串口监视器里又打印了一遍启动信息。

板子重启了。

我知道你在想什么:"是不是我程序写错了?是不是板子坏了?" 都不是。

为什么

舵机里面是个电机。电机的脾气是——不转的时候不太耗电,一启动那一下特别费电。 那一下能有多大?几百毫安;要是被东西挡住转不动,一安培都打不住。 没概念的话这么比:手机充电器一般也就输出 1~2 安培。

而板子上那个 3.3V 是从哪来的?USB 的 5V 经过一颗很小的稳压芯片变出来的。 那颗芯片设计上只够给主控、传感器、LED 这些"细活"用,就是几十毫安的量级。

你让舵机从这儿取电,等于让一根台灯的电线去带一台空调。

还记得吗——夏天一开空调,家里的灯会闪一下。 那就是大功率电器启动的瞬间把电压拉下去了。板子这儿发生的是同一件事,只是它更脆弱:电压一被拉低,欠压保护就动作,直接重启。

配张图看得更清楚:

舵机的电源要从独立 5V 来,并且负极必须和板子的 GND 连在一起共地

图里那根灰色的、从电池盒一路绕到板子上的线,就是"共地"——下一段专门讲它。

正确做法

舵机的电,单独给它。

按推荐顺序,你有这几个选择:

  1. 一个 5 号电池盒(4 节 = 6V)。MG90S 的标称工作电压是 4.8~6V,6V 完全没问题,而且电池的电流能力远比板子强
  2. 一个 5V 电源适配器 / 充电宝,能引出正负极的那种
  3. 手头什么都没有、只想先看一眼效果:临时从拓展板上的 5V 脚取电——那个脚是从 USB 直接过来的,比 3.3V 强不少,做做演示可以

但第 3 条是权宜之计。 一旦舵机被挡住(比如做机械臂时顶到桌子了),电流一冲上去,USB 供电照样会垮。想做得像样,就上独立电源——这是后面 12 个实战课题里反复出现的一件事。

注意:不要接 3.3V,一定不行。 就算电流够,电压也不够——MG90S 要 4.8V 以上才有力气,你给它 3.3V,它会转得软绵绵、抖个不停。

共地:那根不能省的线

独立供电之后,还有一步绝对不能忘把电池盒的负极,和板子的 GND 用一根线连起来。 这叫共地

为什么?因为电压是"相对于地"的一个差值。板子说"我在信号线上输出 3.3V",它真正的意思是"比我这边这个地,高 3.3V"。如果舵机那边用的是另一个地(电池负极),两边没有共同的零点,那板子说的"3.3V",在舵机眼里可能是 8V,也可能是 0.5V。

打个比方:两个人量身高,得背靠背站在同一个地面上。 一个站一楼、一个站三楼,报出来的数根本没法比。

这个坑特别阴。 因为不共地的时候,线看起来全都接对了——电源给了,信号接了,可舵机就是乱转、发抖,或者干脆一动不动。这时候别猜,用第 5 课那套顺序查:电 → 信号 → 代码

  1. 电压档量舵机的电源脚:转的时候有没有 5V?(没有 → 电源的问题)
  2. 通断档量电池负极和板子 GND 之间:通不通?(不通 → 就是共地没接)

五、动手,分四步

第一步:装库(代码路线)

舵机不是"读一个引脚"那么简单,得装个库。先搜一下:

Shell
arduino-cli lib search servo

然后装那个给 ESP32 用的舵机库(通常叫 ESP32Servo):

Shell
arduino-cli lib install "ESP32Servo"

搜出来的名字可能和我写的不完全一样,以你搜到的为准。装不上也别卡在这儿——先走图形化路线,平台里有现成的"舵机"积木,拖出来写上角度就行。你是来学"舵机需要什么"的,不是来跟库管理器较劲的。

第二步:让它转到三个角度

C++
#include <ESP32Servo.h>

Servo myServo;          // 给这个舵机起个名字

void setup() {
  myServo.attach(7);    // 信号线接在 GPIO7
}

void loop() {
  myServo.write(0);     // 转到 0 度
  delay(1000);
  myServo.write(90);    // 转到 90 度
  delay(1000);
  myServo.write(180);   // 转到 180 度
  delay(1000);
}

烧进去。你会看到摇臂一格一格地跳过去。

现在注意那几个 delay(1000)——它们不是可有可无的。

第三步:为什么必须有 delay

这是舵机的一个关键脾气:

write(90) 不是"把它掰到 90 度",而是"告诉它:请去 90 度"。

说完这句话,程序立刻就往下走了,可舵机还在慢慢转。它转过去是要花时间的——MG90S 空载大概 0.1 秒转 60 度

你要是说完立刻说下一句"去 180 度",它转了一半就被喊回来,表现就是抖、顿、嗡嗡响,看着像坏了。

给够时间,它就顺了。 试试这个扫动的程序:

C++
void loop() {
  for (int a = 0; a <= 180; a++) {   // 从 0 扫到 180
    myServo.write(a);
    delay(15);
  }
  for (int a = 180; a >= 0; a--) {   // 再扫回来
    myServo.write(a);
    delay(15);
  }
}

你看,就是"反复做一件小事"而已——for 循环干的就是这个。换个零件,骨架还是那个骨架。

delay(15) 是怎么来的? 上面说了,它每度大概要 1.7 毫秒,给 15 毫秒是留了余量的。这就是第 11 课那个"先测,再定"——你可以自己试:改成 delay(5) 会顿,改成 delay(50) 会很慢但特别顺。自己试出来的数,比抄来的数记得牢。

第四步:故意踩一次坑

这一步是这节课的重点,别跳过。

把舵机的红线从独立电源上拔下来,插到板子的 3.3V(或者拓展板那个 5V 也行,效果弱一点)。串口监视器保持打开,跑上面那个扫动程序。

看着。

你会看到其中一种:舵机抖两下就不动了、板子上的电源灯暗了一下、串口里又打印一遍启动信息、扫到一半突然从头开始。

这就是"舵机一转,板子就重启"。

恭喜你,你把本套课最有名的一个坑亲脚踩了一遍。

为什么我要你故意踩? 因为做第 31 课"追光小雷达"时,你会同时接光敏、舵机、OLED。那个现象会在你最没防备的时候出现,如果你没提前认识它,你一定会怀疑程序写错了、模块坏了、板子烧了——然后花一晚上改代码,而代码一个字都没错。

现在你认识它了。这就是"出错是教学内容,不是事故"。

顺便记一件事:改回独立供电以后,现象是不是立刻消失了? 这就是证据——故障不是猜没的,是被排除掉的。

自测

单选题"让舵机转到 90 度",这件事属于哪种输出?
单选题为什么舵机不能从板子的 3.3V 脚取电?
单选题给舵机接了独立电源后,为什么还要把电源负极和板子的 GND 连起来?
问答题为什么 write(90) 后面一定要给 delay?不给会怎么样?
拓展舵机怎么知道自己现在转到了几度?

这个问题课上不讲,留给你自己找答案——**问 AI 是最好的办法**。

提示:搜"舵机 内部结构 电位器 反馈"、"舵机 闭环控制 原理"。想一想:第 10 课那个电位器,如果被装进舵机里,它能告诉电路什么信息?要是这个电位器坏了,舵机会有什么表现?

拓展舵机为什么规定信号是"每秒 50 次"?改了会怎样?

这个问题课上不讲,留给你自己找答案——**问 AI 是最好的办法**。

提示:搜"舵机 PWM 50Hz 脉宽 1ms 2ms"、"模拟舵机 数字舵机 区别"。想一想:这个频率是随便定的,还是跟舵机内部电路有关?

⚠️ 这个别拿自己的舵机乱试。 频率改错可能让它嗡嗡叫、发热甚至堵转。先查资料搞明白,再决定动不动手。

本节你要动手做的事

  • [ ] 认清三根线:橙=信号、红=电源、棕=地。插之前看颜色,别凭手感(红棕接反会烧)
  • [ ] 接对信号和地:橙线→GPIO7,棕线→板子 GND
  • [ ] 给舵机独立 5V:电池盒或 5V 电源,并且把电源负极和板子 GND 连起来(共地)
  • [ ] 转三个角度090180每一步之间给够 delay
  • [ ] 扫过去再扫回来:用 for 循环,再自己调调 delay 的大小,看手感有什么不同
  • [ ] 故意踩坑:把红线接到板子的 3.3V,亲眼看板子重启——然后再改回来
  • [ ] 用万用表量一次:舵机电源脚在"不动"和"正在转"的时候,电压一样吗?(转的时候会掉一点,那就是"电流大"的证据

这节课你做了一件前面十几节课都没做过的事:让板子输出了一个"既不是开、也不是关"的东西。

从今天起,"让它到 90 度""让它亮三成""让它慢一点"这类话,你都有办法表达了——方法就是那一个:快到人感觉不出来,平均下来就是中间值。

还有那个坑。 你今天是故意踩的,踩完还知道为什么。但很多人是在做实战课题的时候第一次撞上它——正调着机械臂,板子突然重启,然后开始怀疑人生。你现在比那时候的他们,多知道一件事。

下一节课我们学 OLED 显示屏——套件里唯一一个能显示信息的模块。前面所有模块要么"读"、要么"动",从下一课开始,你的作品能说话了。

顺带一提,它也是唯一一个用 I²C 的模块——两根信号线,而不是一根。这里面有个特别省引脚的思路,下一课讲。