首頁 › 動手做 › 會揮手的桌上機器人

會揮手的桌上機器人:ESP32 伺服馬達入門

難度:入門時間:約 45 分鐘免函式庫ESP32 套件 3.x

伺服馬達是機械手臂、機器人關節的基礎。這篇不裝任何函式庫,直接用 ESP32 的 PWM 產生伺服訊號,讓你真正搞懂「角度是怎麼控制的」。

✓ 本文程式已在 2026/09/29 以 Arduino ESP32 開發板套件 3.3.12 編譯通過(開啟全部編譯警告,0 個警告),開發板選 ESP32 Dev Module。

1. 需要的零件

零件數量說明
ESP32 DevKit V11
SG90 伺服馬達1藍色小伺服馬達,附塑膠搖臂
HC-SR04 超音波感測器1選配:有人靠近才揮手
電阻 1 kΩ、2 kΩ各 1ECHO 分壓用
電解電容 100–470 µF1建議:穩定伺服馬達電源
紙盒、冰棒棍或 3D 列印外殼當機器人的身體和手

2. 伺服馬達的原理

SG90 每 20 毫秒(50 Hz)接收一個脈衝,脈衝的「寬度」決定角度:約 0.5 毫秒是 0°,約 2.4 毫秒是 180°,中間等比例。

ESP32 的 LEDC 可以把頻率設成 50 Hz、解析度設成 14 bit(0–16383)。一個週期 20000 微秒對應 16383,所以:

工作週期 = 脈寬(µs)÷ 20000 × 16383

程式裡的 servoWrite() 就是做這個換算。每顆 SG90 的極限略有不同,轉到底時如果發出「嘰嘰」聲,把 PULSE_MIN_US 調高、PULSE_MAX_US 調低一點。

3. 接線

從接到備註
SG90 橘線(訊號)ESP32 GPIO 23
SG90 紅線(電源)ESP32 VIN(USB 供電時約 5V)不要接 3.3V,扭力會不足
SG90 棕線(地)ESP32 GND
電容正極接 VIN,負極接 GND注意極性,長腳是正極
HC-SR04 VCC / GNDVIN / GND
HC-SR04 TRIGGPIO 18
HC-SR04 ECHO1 kΩ → GPIO 19;GPIO 19 經 2 kΩ 接 GND分壓成約 3.3V
一顆 SG90 用 USB 供電可以,兩顆以上請外接 5V 電源。伺服馬達轉動瞬間電流很大,會讓 ESP32 重開機。外接電源時,電源的 GND 要和 ESP32 的 GND 接在一起。

4. 程式

沒有接超音波感測器的話,把 USE_SENSOR 改成 false,機器人會每 5 秒自己揮一次手。

/*
 * TinyBot Forge — 會揮手的桌上機器人(伺服馬達入門)
 * https://tinybotforge.com
 *
 * 不用額外函式庫:直接用 ESP32 的 LEDC 產生 50 Hz 伺服訊號。
 * 硬體:ESP32 DevKit V1、SG90 伺服馬達、HC-SR04(選配,有人靠近才揮手)
 * 環境:Arduino IDE 2.x + ESP32 開發板套件 3.x
 */

const int PIN_SERVO = 23;
const int PIN_TRIG = 18;
const int PIN_ECHO = 19;       // HC-SR04 ECHO 需經 1kΩ/2kΩ 分壓
const bool USE_SENSOR = true;  // 沒接超音波就改成 false,會每 5 秒揮一次

// SG90:週期 20 ms(50 Hz),脈寬約 0.5 ms = 0°、2.4 ms = 180°
const int SERVO_FREQ = 50;
const int SERVO_BITS = 14;           // 14 bit → 0–16383
const int PULSE_MIN_US = 500;
const int PULSE_MAX_US = 2400;
const float WAVE_TRIGGER_CM = 40.0;

void servoWrite(int angle) {
  angle = constrain(angle, 0, 180);
  int us = map(angle, 0, 180, PULSE_MIN_US, PULSE_MAX_US);
  // 脈寬換成工作週期:duty = us / 20000 × 16383
  uint32_t duty = (uint32_t)us * ((1UL << SERVO_BITS) - 1) / 20000UL;
  ledcWrite(PIN_SERVO, duty);
}

// 平滑移動:一度一度轉,動作比較自然,也比較不會拉低電壓
void servoSweep(int from, int to, int stepDelayMs) {
  int step = (to > from) ? 1 : -1;
  for (int a = from; a != to; a += step) {
    servoWrite(a);
    delay(stepDelayMs);
  }
  servoWrite(to);
}

float readDistanceCm() {
  digitalWrite(PIN_TRIG, LOW);  delayMicroseconds(2);
  digitalWrite(PIN_TRIG, HIGH); delayMicroseconds(10);
  digitalWrite(PIN_TRIG, LOW);
  unsigned long us = pulseIn(PIN_ECHO, HIGH, 25000UL);
  return us == 0 ? 999.0 : us / 58.0;
}

void wave() {
  servoSweep(90, 150, 4);          // 舉手
  for (int i = 0; i < 3; i++) {    // 揮三下
    servoSweep(150, 110, 3);
    servoSweep(110, 150, 3);
  }
  servoSweep(150, 90, 6);          // 放下
}

void setup() {
  Serial.begin(115200);
  pinMode(PIN_TRIG, OUTPUT);
  pinMode(PIN_ECHO, INPUT);
  ledcAttach(PIN_SERVO, SERVO_FREQ, SERVO_BITS);
  servoWrite(90);
  delay(500);
}

void loop() {
  if (USE_SENSOR) {
    float cm = readDistanceCm();
    Serial.printf("距離 %.1f cm\n", cm);
    if (cm < WAVE_TRIGGER_CM) {
      wave();
      delay(1500);  // 揮完休息一下,避免一直重複
    }
    delay(100);
  } else {
    wave();
    delay(5000);
  }
}
下載 .ino 檔

5. 組裝建議

  1. 先校正角度:上傳程式後伺服馬達會轉到 90°。這時再把搖臂裝上去,讓手臂「自然下垂」的位置剛好對應 90°。
  2. 把伺服馬達用熱熔膠固定在紙盒側面,冰棒棍黏在搖臂上當手。
  3. 超音波感測器放在盒子正面,兩個圓筒剛好可以當眼睛。

6. 常見問題

伺服馬達抖動不停

通常是電源不穩。加上電容,或改用外接 5V。也確認 GND 有接好。

網路上的教學都用 ESP32Servo 函式庫,差在哪?

函式庫幫你做了同樣的換算,用起來更方便。但自己寫一次,你就知道伺服訊號長什麼樣子,之後換任何開發板都會。

轉動角度不到 180°

很多便宜的 SG90 實際只能轉 160–170°。調整 PULSE_MIN_US 與 PULSE_MAX_US,找出你那顆的極限。

免費領取:ESP32 機器人零件與接線速查表

一張 A4 看懂 ESP32 安全腳位、馬達驅動與感測器接法,可以列印貼在工作桌旁。

免費下載速查表 PDF