跳转至

PWM 舵机通信协议与堵转保护功能说明

适用范围

本文适用于所有支持标准 PWM 控制接口的数字舵机,具体电气参数、机械规格与接线定义请参见各型号的规格书。


1. PWM 控制协议

1.1 信号定义

PWM 舵机使用单线脉宽调制信号进行控制。主机只需向舵机输入一路 PWM 脉冲,舵机根据脉冲的高电平持续时间(脉宽)将输出轴转动到对应的角度位置。

术语 含义 是否承载角度信息
周期 (Period) PWM 脉冲的重复周期,决定刷新频率
脉宽 (Pulse Width) 单个脉冲的高电平持续时间
占空比 (Duty Cycle) 脉宽 / 周期 间接

单片机 PWM 外设实际配置的参数是占空比,需要按"目标脉宽 → 占空比"换算后写入。

1.2 PWM 周期范围

PWM 信号的周期 (Period) 决定了舵机接收命令的刷新频率,虽然不直接承载角度信息,但对控制效果有重要影响。规格书一般会给出舵机可接受的周期范围(如 3 ~ 20 ms,即约 50 ~ 333 Hz),在此范围内,周期可以由用户根据应用场景选择。

周期与刷新率

周期 T 与刷新率 f 互为倒数:

刷新率 f (Hz) = 1 / 周期 T (s)
  • T = 20 ms → f = 50 Hz (每秒更新 50 次目标位置)
  • T = 10 ms → f = 100 Hz
  • T = 3 ms → f ≈ 333 Hz

刷新率越高,主机对舵机的命令更新越频繁,响应越平滑、越跟手;但相应的,主机侧 PWM 外设的开销也越大。

50 Hz 的历史由来

20 ms 周期 (50 Hz) 是 PWM 舵机的传统标准,源自航模遥控(R/C)行业,沿用至今。绝大多数通用 PWM 舵机的规格书会以 50 Hz 为"推荐/默认"周期。该标准的好处是:

  • 周期足够长,电机在每个周期内能完成一次完整的"测速-驱动"循环
  • 占空比变化对脉宽精度影响小
  • 与航模接收机直接兼容,生态最成熟

1.3 角度-脉宽映射

舵机目标角度与输入脉宽呈线性映射关系,通用公式如下:

角度 = (脉宽 - 中位脉宽) / 满量程脉宽 × 转角范围

其中:

  • 中位脉宽 — 对应输出轴机械中位的脉宽
  • 满量程脉宽 — 中位到一端极限位置的脉宽差
  • 转角范围 — 舵机总转角(度)

具体的中位脉宽、极限脉宽与转角范围因型号而异,请查阅对应舵机规格书。

方向约定

舵机旋转方向遵循统一约定:脉宽增大,角度沿约定的方向单调增加(具体转向因型号而异,见规格书)。控制系统设计时,建议按"目标脉宽 → 目标角度"的顺序进行计算,避免在算法中混淆方向。

1.4 与总线舵机的差异

PWM 舵机与 UART / CAN 等总线舵机在协议层有本质区别:

维度 PWM 舵机 总线舵机 (UART / CAN)
信号方向 单向(主机 → 舵机) 双向(命令 + 应答)
参数回读 不支持 支持(位置 / 电流 / 温度 / 等)
舵机识别 无 ID 概念 舵机 ID
多机控制 需独立 PWM 通道 总线级联
堵转状态获取 需主机侧额外检测(见第 3 节) 可通过指令回读

2. 基本控制方法

2.1 兼容性概述

PWM 舵机协议因其简洁性,广泛兼容各类主机平台与控制器,常见支持方式包括:

  • Arduino — 通过 Servo.h 库或软 PWM 输出
  • ESP32 — 使用 LEDC 外设产生高频 PWM
  • 树莓派 — 通过 pigpio / RPi.GPIO 库产生硬件定时 PWM
  • STM32 — 使用 HAL 库的 TIM 定时器 PWM 模式
  • 通用舵机控制器 — 通过 UART / USB 配置目标角度,内部生成 PWM 信号

上述所有平台最终都通过占空比控制 PWM 信号的脉宽,再由脉宽控制舵机角度。换算关系:

占空比 (%) = 目标脉宽 (μs) / 周期 (μs) × 100

2.2 Arduino

使用 Arduino 内置的 Servo.h 库,直接以微秒为单位写入脉宽,不受默认 0-180° 角度映射的限制。

#include <Servo.h>

Servo myservo;

void setup() {
    // attach(引脚, 最小脉宽μs, 最大脉宽μs)
    myservo.attach(9, 500, 2500);
    myservo.writeMicroseconds(1500);  // 中位
}

void loop() {
    // 写目标脉宽 (μs),范围参见舵机规格书
    int pulse_us = 1500;
    myservo.writeMicroseconds(pulse_us);
    delay(15);
}

要点:

  • Servo.h 在 Uno / Nano 等 8 位板上使用 Timer1,占用定时器资源
  • 若使用 Mega / Due 等多定时器板,可在多路舵机间分散定时器
  • writeMicroseconds() 直接以微秒写入,适合需要精细脉宽控制的场景

完整示例:带错误处理的舵机扫描

以下示例展示如何在 Arduino 上实现舵机往返扫描,并包含边界检查与错误处理:

#include <Servo.h>

// 舵机参数配置(根据实际舵机规格书调整)
const int SERVO_PIN      = 9;      // 信号线接 D9
const int PULSE_MIN      = 500;    // 最小脉宽 (μs)
const int PULSE_MAX      = 2500;   // 最大脉宽 (μs)
const int PULSE_CENTER   = 1500;   // 中位脉宽 (μs)
const int STEP_DELAY     = 15;     // 每步延时 (ms)
const int SWEEP_STEP     = 10;     // 每次增量 (μs)

Servo myServo;
int currentPulse = PULSE_CENTER;
int direction = 1;  // 1:正向, -1:反向

void setup() {
    Serial.begin(115200);

    // 初始化舵机
    if (!myServo.attach(SERVO_PIN, PULSE_MIN, PULSE_MAX)) {
        Serial.println("[ERROR] 舵机初始化失败,检查引脚连接!");
        while(1);  // 停止运行
    }

    Serial.println("[INFO] 舵机已初始化");
    Serial.print("[INFO] 脉宽范围: ");
    Serial.print(PULSE_MIN);
    Serial.print(" - ");
    Serial.print(PULSE_MAX);
    Serial.println(" μs");

    // 归中位
    myServo.writeMicroseconds(PULSE_CENTER);
    delay(500);
}

void loop() {
    // 更新目标脉宽
    currentPulse += direction * SWEEP_STEP;

    // 边界检查与反向
    if (currentPulse >= PULSE_MAX) {
        currentPulse = PULSE_MAX;
        direction = -1;
        Serial.println("[INFO] 到达最大脉宽,开始回程");
    } else if (currentPulse <= PULSE_MIN) {
        currentPulse = PULSE_MIN;
        direction = 1;
        Serial.println("[INFO] 到达最小脉宽,开始正向");
    }

    // 写入舵机
    myServo.writeMicroseconds(currentPulse);

    // 周期性输出当前状态(每500ms)
    static unsigned long lastPrint = 0;
    if (millis() - lastPrint > 500) {
        Serial.print("[STATUS] 当前脉宽: ");
        Serial.print(currentPulse);
        Serial.println(" μs");
        lastPrint = millis();
    }

    delay(STEP_DELAY);
}

运行效果: - 舵机从中位开始,缓慢向一侧运动至极限,然后反向扫描至另一侧,往复循环 - 串口监视器每 500ms 输出当前脉宽值,便于调试 - 若舵机未连接或引脚错误,程序会在 setup() 中报错并停止


2.3 ESP32

ESP32 Arduino 核心提供 LEDC (LED Control) 外设,可产生高频 PWM 并精确控制占空比。16 位分辨率足够覆盖 0.1 μs 级脉宽精度。

const int servoPin  = 18;     // 信号线接 GPIO 18
const int channel   = 0;      // LEDC 通道号
const int freq      = 50;     // 50 Hz PWM 周期
const int resBits   = 16;     // 16 位分辨率
const int periodUs  = 20000;  // 50 Hz 对应 20000 μs

void setup() {
    ledcSetup(channel, freq, resBits);
    ledcAttachPin(servoPin, channel);
}

void loop() {
    // 目标脉宽 (μs),例:中位 1500
    int pulse_us  = 1500;
    int max_duty  = (1 << resBits) - 1;  // 65535
    int duty      = (long)max_duty * pulse_us / periodUs;
    ledcWrite(channel, duty);
    delay(15);
}

要点

  • ESP32 的 LEDC 支持 16 个独立通道,可同时驱动多路舵机
  • 频率与分辨率是矛盾的,需在初始化时选定(本例 50 Hz / 16 bit 是常用组合)
  • 改占空比时按 32 位整数重新计算,避免浮点误差累积

2.4 树莓派 + pigpio

树莓派通过 pigpio 守护进程使用硬件定时器产生稳定 PWM,精度可达 1 μs,且不占用 CPU。

import pigpio
import time

SERVO_PIN = 18  # 信号线接 GPIO 18 (Pin 12)

pi = pigpio.pi()
if not pi.connected:
    raise RuntimeError("pigpio 守护进程未启动 (sudo pigpiod)")

def set_pulse(pulse_us: int) -> None:
    """写入目标脉宽 (μs),0 表示停止 PWM 输出"""
    pi.set_servo_pulsewidth(SERVO_PIN, pulse_us)

try:
    set_pulse(1500)  # 中位
    while True:
        # 在此更新 set_pulse(<新脉宽>) 即可控制角度
        time.sleep(0.02)
except KeyboardInterrupt:
    set_pulse(0)      # 停止输出
    pi.stop()

要点

  • 需先启动守护进程:sudo pigpiod
  • 支持任意 GPIO 引脚,但同一时间硬件 PWM 通道有限(树莓派为 2 路)
  • set_servo_pulsewidth() 内部将脉宽限制在 500-2500 μs,超出范围舵机不响应

2.5 STM32

STM32 通过 TIM 定时器的 PWM 模式产生 50 Hz 脉冲,精度由定时器时钟决定。以下示例使用 HAL 库、72 MHz 系统时钟、TIM2_CH1 输出。

CubeMX 配置要点

参数 说明
Prescaler 71 72 MHz / 72 = 1 MHz 计数频率
Counter Mode Up 向上计数
Counter Period 19999 1 MHz / 20000 = 50 Hz
Pulse (初始) 1500 中位脉宽 1500 μs
CH Polarity High 高电平有效

示例代码

/* main.c */
#include "main.h"
#include "tim.h"

static void set_servo_pulse(uint16_t pulse_us) {
    /* 改变 CCR1 寄存器即可更新脉宽 */
    __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, pulse_us);
}

int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_TIM2_Init();
    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);  // 启动 PWM

    while (1) {
        set_servo_pulse(1500);                  // 中位
        HAL_Delay(15);
    }
}

要点

  • 通用 / F1 / F4 系列均适用,只需调整预分频与重载值匹配系统时钟
  • 多个舵机可分配到不同 TIM 通道,实现多路独立控制
  • 修改 __HAL_TIM_SET_COMPARE 即可实时调整脉宽,无延迟

2.6 通用舵机控制器

通用舵机控制器(俗称"舵机驱动板")通常自带 PWM 生成电路,主机通过 UART / USB / I2C 下发目标角度,控制器内部转换为 PWM 脉宽。

┌────────┐  UART/USB  ┌──────────────┐  PWM  ┌──────┐
│  主机  │ ──────────> │ 通用舵机控制器 │ ─────>│ 舵机 │
└────────┘             └──────────────┘       └──────┘
                              │
                              ├── 电源输入(单独供电)
                              └── GND

使用提示

  • 接线时舵机电源独立供电,控制器只提供信号线,避免控制器过流
  • 主机侧协议多为私有(各厂商不同),需对照控制器手册配置波特率 / 数据格式
  • 适合"主机资源紧张"或"需要并联多路舵机"的场景

3. 堵转保护功能 (Stall Protection)

说明:堵转保护是部分 PWM 数字舵机提供的特别功能,本文统一描述其行为。具体型号是否支持,请参见对应舵机规格书。

3.1 功能目的

当舵机输出轴被外力阻挡而无法转动时,电机将持续输出满功率以维持位置,电流急剧上升。长时间堵转会导致:

  • 电机线圈过热,绝缘老化甚至烧毁
  • 齿轮组承受超额扭矩,加速磨损
  • 驱动电路过流,缩短寿命

堵转保护 (Stall Protection) 用于在检测到持续堵转后,主动降低输出功率,直至堵转解除后自动归位、恢复正常工作。

3.2 触发条件

堵转持续时间 ≥ 1 秒。

短时抖动或瞬态阻挡(小于 1 秒)不会触发保护,确保舵机在常规抗扰场景下保持满功率输出。

3.3 保护状态参数

保护激活后,舵机进入受限工作模式:

参数 保护前(正常) 保护激活后
输出功率 100% 30%
工作电流 视负载 降低约 70%
锁力 满扭矩 约 30% 扭矩
控制信号接收 正常 正常

3.4 状态机时序

stateDiagram-v2
    [*] --> 正常
    正常 --> 保护激活 : 堵转持续 ≥ 1s
    保护激活 --> 回位中 : 堵转解除 / 以 30% 功率到达目标位置
    回位中 --> 正常 : 抵达目标位置 / 恢复全功率

三个状态的行为:

  1. 正常 — 满功率运行,响应主机控制信号,电流视负载
  2. 保护激活 — 堵转 ≥ 1s 后进入,功率降至 30%
  3. 回位中 — 堵转解除后,以 30% 功率驱动舵机到达目标位置,抵达后自动恢复全功率正常状态

4. 常见问题与故障排查

4.1 舵机无响应

可能原因 排查方法 解决方案
接线错误 检查信号线、VCC、GND 顺序 参照规格书重新接线
电源未供电 测量舵机 VCC 引脚电压 确保供电(电压范围参考对应规格书)
脉宽超出范围 确认发送的脉宽值 调整至规格书规定范围(通常 500~2500μs)
PWM 周期过短/过长 检查 PWM 频率设置 使用推荐的 50Hz(20ms 周期)

4.2 舵机抖动或异响

可能原因 排查方法 解决方案
电源纹波过大 示波器测量电源波形 电源端并联 100μF~470μF 电解电容
信号线受干扰 检查信号线是否与电源线并行 信号线与电源线分离走线,或使用屏蔽线
脉宽抖动 检查 MCU 定时器中断是否被打断 提高 PWM 生成优先级或使用硬件 PWM
负载过重 手动转动输出轴检查阻力 减小负载或更换大扭矩舵机

4.3 转角不准确

可能原因 排查方法 解决方案
脉宽映射错误 测量实际输出的 PWM 波形 重新标定脉宽-角度映射关系
舵机机械磨损 手动转动检查有无卡顿 更换舵机或检修齿轮组
占空比计算精度不足 检查浮点运算是否溢出 使用整数运算或增加定时器分辨率
周期设置错误 确认 PWM 周期配置 确保周期在规格书规定范围内(通常 3~20ms)

4.4 舵机发热严重

可能原因 排查方法 解决方案
长时间堵转 检查机械结构是否有干涉 排除机械干涉,启用堵转保护
供电电压过高 测量实际供电电压 调整至规格书推荐电压
频繁大幅度运动 观察工作模式 降低运动频率或增加冷却时间
舵机过载 检查负载扭矩 减小负载或更换更大扭矩型号

附录

A. 术语表

术语 英文 含义
脉宽 Pulse Width PWM 信号的高电平持续时间,决定舵机目标角度
周期 Period PWM 信号的重复周期,决定刷新频率
占空比 Duty Cycle 脉宽与周期的比值,单位为百分比

B. 参考资源