舵机 (Servo Motor) 是一种集成了直流电机、减速齿轮组、电位器和控制电路的微型直流电机。它能够精确地控制输出轴的角度位置,通常在 0 到 180 度之间旋转(标准舵机),或者实现连续旋转(连续旋转舵机)。在机器人、航模、自动化控制等领域,舵机因其小巧、控制简单、定位精确而广受欢迎。

核心思想:通过调整 PWM (脉冲宽度调制) 信号的脉冲宽度来控制舵机的角度。ESP32 凭借其强大的 LEDC (LED Controller) 模块,能够轻松、精确地驱动多个舵机。


一、舵机工作原理

舵机通过接收一个PWM (脉冲宽度调制) 信号来确定其旋转角度。

  • 供电:舵机通常需要 5V 的电源供电。请注意,单个舵机在工作时可能会消耗数百毫安的电流,多个舵机同时工作时电流需求会更大,因此需要一个外部电源为舵机供电,而不是直接由 ESP32 的 3.3V 或 5V 引脚供电(除非是微型舵机且数量极少)。
  • 控制信号:舵机的控制线接收一个周期为 20 毫秒(即 50 Hz)的 PWM 信号。
  • 脉冲宽度决定角度
    • 1.5 毫秒的脉冲宽度通常对应舵机的中心位置 (90 度)。
    • 1 毫秒的脉冲宽度通常对应舵机的最小角度 (0 度)。
    • 2 毫秒的脉冲宽度通常对应舵机的最大角度 (180 度)。

这些脉冲宽度值是舵机的标准定义,但不同的舵机型号可能会有轻微差异,有时 0 度可能对应 0.5ms,180 度可能对应 2.5ms。通常,舵机的规格书会给出这些参数。

二、ESP32 驱动舵机的优势

传统的 AVR Arduino (如 Uno) 驱动多个舵机时,通常会受限于定时器资源,Servo 库会占用 Timer1,且同时驱动的舵机数量有限。

ESP32 在驱动舵机方面具有显著优势:

  1. 多个 PWM 通道:ESP32 的 LEDC (LED Controller) 模块提供多达 16 个独立通道 (Arduino 库通常只开放 8 个通道),每个通道都可以独立配置频率和占空比。这意味着 ESP32 可以轻松同时驱动多个舵机,而不会互相干扰。
  2. 高精度 PWM:LEDC 模块支持 1 到 16 位的占空比分辨率,这使得舵机角度控制更加精细和平滑。
  3. 硬件驱动:PWM 信号由硬件生成,不占用 CPU 资源,CPU 可以同时执行其他任务。

三、ESP32 Arduino ESP32Servo 库详解

Arduino IDE 为 ESP32 提供了一个专门的 ESP32Servo 库,它基于 ESP32 的 LEDC 模块,使得舵机控制变得非常简单。这个库的使用方式与标准的 Arduino Servo 库非常相似。

3.1 库的安装

如果你的 Arduino IDE 已经安装了 ESP32 板支持,通常 ESP32Servo 库也会随之安装。如果没有,可以通过以下步骤手动安装:

  1. 打开 Arduino IDE。
  2. 工具 -> 管理库...
  3. 搜索 ESP32Servo 并安装。

3.2 常用函数

ESP32Servo 库的核心是 Servo 类。

  • #include <ESP32Servo.h>:在代码开头包含库。
  • Servo myservo;:创建一个 Servo 对象。你可以创建多个 Servo 对象来控制多个舵机。
  • myservo.attach(pin);:将舵机对象附加到指定的 GPIO 引脚。这个函数会自动配置 LEDC 通道。
    • pin: ESP32 的任何 GPIO 引脚都可以用作 PWM 输出。
    • attach(pin, minPulse, maxPulse): 可以选择性地指定最小和最大脉冲宽度(微秒),覆盖库的默认值 (通常是 500us 和 2400us)。
  • myservo.write(angle);:将舵机移动到指定角度。
    • angle: 0 到 180 度的整数值。
  • myservo.writeMicroseconds(us);:直接设置 PWM 脉冲宽度(微秒)。这在需要更精细控制或处理不同规格舵机时很有用。
    • us: 500 到 2500 微秒之间的整数值。
  • myservo.read();:读取舵机当前的角度值。
  • myservo.attached();:检查舵机是否已附加到引脚。
  • myservo.detach();:将舵机从引脚分离,释放 LEDC 通道资源。

3.3 示例:控制一个标准舵机

这个例子将连接到 GPIO 13 的舵机从 0 度旋转到 180 度,然后再返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <ESP32Servo.h> // 包含 ESP32Servo 库

// 定义舵机连接的GPIO引脚
// 你可以选择任何一个PWM支持的GPIO引脚
// 常见的PWM引脚包括:13, 14, 27, 26, 25, 33, 32, 18, 19, 23 (注意有些引脚在开发板上可能已被其他功能占用)
#define SERVO_PIN 13 // 例如,使用GPIO 13

// 创建一个Servo对象
Servo myServo;

void setup() {
Serial.begin(115200); // 初始化串口用于调试输出
Serial.println("ESP32 Servo Demo Start!");

// 允许使用GPIO进行PWM输出 (ESP32Servo库的特殊设置)
// 这是为了确保库正确初始化了PWM通道
ESP32PWM::allocateTimer(0); // 为PWM分配一个计时器 (0-3)

// 将舵机对象附加到指定的GPIO引脚
// myServo.attach(SERVO_PIN); // 默认频率是50Hz (20ms周期),脉宽范围是500us到2400us
// 可以自定义脉宽范围来更精确地控制舵机,比如SG90通常是500us到2400us或1000us到2000us
// 建议查阅舵机的数据手册以获取准确的脉宽范围
myServo.attach(SERVO_PIN, 500, 2400); // 附加舵机,设置最小和最大脉宽 (微秒)

// 初始位置设置,移动到0度
myServo.write(0);
Serial.println("Initial position: 0 degrees");
delay(1000); // 等待1秒让舵机移动到位
}

void loop() {
// 从0度缓慢移动到180度
Serial.println("Moving from 0 to 180 degrees...");
for (int pos = 0; pos <= 180; pos += 1) { // 每次增加1度
myServo.write(pos); // 告诉舵机移动到指定角度
delay(15); // 延迟一点时间,让舵机缓慢移动
}
delay(1000); // 在180度位置停留1秒

// 从180度缓慢移动到0度
Serial.println("Moving from 180 to 0 degrees...");
for (int pos = 180; pos >= 0; pos -= 1) { // 每次减少1度
myServo.write(pos); // 告诉舵机移动到指定角度
delay(15); // 延迟一点时间,让舵机缓慢移动
}
delay(1000); // 在0度位置停留1秒
}

3.4 示例:使用 writeMicroseconds() 精确控制

如果你需要更精确的控制,或者你的舵机不是标准的 1ms-2ms 脉冲范围,可以使用 writeMicroseconds()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <ESP32Servo.h>

Servo myservo;
const int servoPin = 13;

void setup() {
Serial.begin(115200);
Serial.println("ESP32 Servo writeMicroseconds Example");

ESP32PWM::setup();
// 使用自定义的最小和最大脉冲宽度,这里我们使用库的默认值来示例
// minPulseWidth: 500us, maxPulseWidth: 2400us 是库的默认值
myservo.attach(servoPin, 500, 2400);
}

void loop() {
// 移动到最小角度 (例如 0度,对应 500us)
myservo.writeMicroseconds(500);
Serial.println("Moved to 500us (Min Angle)");
delay(2000);

// 移动到中心位置 (例如 90度,对应 1500us)
myservo.writeMicroseconds(1500);
Serial.println("Moved to 1500us (Center)");
delay(2000);

// 移动到最大角度 (例如 180度,对应 2400us)
myservo.writeMicroseconds(2400);
Serial.println("Moved to 2400us (Max Angle)");
delay(2000);
}

四、舵机供电注意事项

这是使用舵机时最关键的考虑因素之一,尤其是使用 ESP32 这样的微控制器:

  1. 独立电源
    • 切勿直接使用 ESP32 开发板的 3.3V 或 5V 引脚为多个舵机供电。ESP32 板载稳压器的电流输出能力有限,通常只有几百毫安。
    • 单个标准舵机在空载时可能消耗 100-200mA,在堵转或带载时可能瞬间达到 500mA - 1A 甚至更高。
    • 多个舵机同时启动或工作时,瞬时电流会非常大,可能导致 ESP32 复位、工作不稳定,甚至损坏开发板。
    • 正确做法:使用一个独立的、足够强大的 5V 外部电源为舵机供电。
  2. 共地连接
    • 极其重要! 尽管舵机由外部电源供电,但它的地线必须与 ESP32 的地线连接在一起(共地),否则控制信号将无法正确传递。
    • 接线示意图:
      • 舵机红色线 (VCC) -> 外部 5V 电源正极
      • 舵机棕色/黑色线 (GND) -> 外部 5V 电源负极 AND ESP32 的 GND 引脚
      • 舵机黄色/橙色/白色线 (Signal) -> ESP32 的任意 GPIO 引脚

五、连续旋转舵机 (Continuous Rotation Servo)

连续旋转舵机的工作原理与标准舵机类似,但它的目标不是特定的角度,而是旋转速度和方向

  • 1.5 毫秒脉冲:停止旋转。
  • 小于 1.5 毫秒脉冲 (例如 1 毫秒):以不同的速度向一个方向旋转。
  • 大于 1.5 毫秒脉冲 (例如 2 毫秒):以不同的速度向另一个方向旋转。

使用 ESP32Servo 库控制连续旋转舵机时:

  • myservo.write(90); 会让舵机停止。
  • myservo.write(0); 会让舵机以最大速度向一个方向旋转。
  • myservo.write(180); 会让舵机以最大速度向另一个方向旋转。
  • 通过 myservo.write(angle); 传入 0 到 90 之间的值,或 90 到 180 之间的值,可以控制不同的旋转速度。
  • myservo.writeMicroseconds() 提供更精细的速度控制。

六、总结

ESP32 凭借其强大的 LEDC 模块和 ESP32Servo 库,为舵机控制提供了高效、灵活且易于使用的解决方案。无论是单个还是多个舵机,标准型还是连续旋转型,ESP32 都能胜任。

关键要点回顾:

  • 舵机通过 PWM 信号的脉冲宽度控制角度。
  • ESP32 的 LEDC 模块提供多个高精度 PWM 通道,适合驱动多个舵机。
  • 使用 ESP32Servo 库简化舵机编程。
  • 务必使用独立电源为舵机供电,并确保舵机电源的地线与 ESP32 的地线共地。
  • 理解标准舵机与连续旋转舵机的控制差异。

掌握这些知识,你就可以在你的 ESP32 项目中自信地集成和控制舵机,为你的机器人、自动化设备或创意项目赋予运动能力。