Chapter 06 · 树莓派与嵌入式 Linux

PWM 与电机控制

让树莓派驱动机械:舵机转到指定角度、直流电机调速正反转、步进电机精确定位。关键是别让电机的大电流烧了树莓派。

课程进度50%

关键概念

软件 PWM vs 硬件 PWM
软件 PWM:靠 CPU 定时翻转引脚模拟 PWM,任意 GPIO 都能用,但精度受系统调度影响、可能抖动。硬件 PWM:由专用外设电路生成,波形稳定精准,但树莓派只有少数引脚支持(GPIO12/13/18/19)。控制舵机、要求稳定的场合优先硬件 PWM。gpiozero 的 pin factory 选 pigpio 时软件 PWM 也更稳。
占空比与频率
PWM 有两个参数:频率(每秒开关多少次)和占空比(每个周期通电时间比例)。调 LED 亮度/电机转速靠占空比(0~100%);舵机则要求固定 50Hz 频率、用脉冲宽度编码角度。频率选择因负载而异:LED 几百 Hz、电机 1~20kHz、舵机严格 50Hz。
舵机(Servo)
内部有位置反馈的电机,能转到并保持指定角度(如 0°~180°)。用 50Hz PWM(20ms 周期)控制,脉冲宽度决定角度:约 1ms→0°,1.5ms→90°,2ms→180°。gpiozero 的 Servo/AngularServo 类封装了这套编码,直接给角度即可。
直流电机(DC Motor)
通电就转、电压越高转越快、正负极对调则反转的普通电机。树莓派 GPIO 无法直接驱动电机——电机启动电流可达数百毫安,远超 GPIO 的 16mA 上限,会烧芯片。必须通过电机驱动板(如 L298N)用外部电源供电,GPIO 只负责发控制信号。
H 桥与 L298N
H 桥是由 4 个开关组成的电路,通过控制开关组合改变电机两端电压极性,实现正转/反转/刹车。L298N 是集成双 H 桥的经典驱动模块,可同时驱动 2 个直流电机或 1 个步进电机。它接外部电源(如 12V 电池)给电机供电,用树莓派 GPIO 控制方向和 PWM 调速。
步进电机(Stepper Motor)
把一圈分成许多固定步(如 200 步 = 1.8°/步),可精确旋转到任意步数,无需位置反馈,常用于 3D 打印机、机械臂。通过按特定顺序给多个线圈通电(激励序列)来一步步转动。常配 ULN2003(28BYJ-48 小电机)或 A4988/DRV8825(大电机)驱动。
外部供电与共地
电机绝不能从树莓派取电(会拉垮供电导致死机、烧芯片)。电机用独立电源(电池/适配器)经驱动板供电。但驱动板的 GND 必须与树莓派 GND 连通(共地),否则控制信号没有共同参考电平,电机行为异常。共地是多电源系统的铁律。
反向电动势与续流保护
电机是感性负载,断电瞬间线圈会产生反向高压尖峰,可能击穿驱动器件。驱动板通常内置续流二极管吸收它。自己搭电路驱动继电器/电机时,务必并联续流二极管保护。这是驱动感性负载的常识。

电机控制系统接线总览

核心原则一图看懂:信号走 GPIO,动力走外部电源,两边共地

树莓派 + L298N + 直流电机 供电与信号分离 ═══════════════════════════════════════════════════════════════ 外部电源 L298N 驱动板 ┌────────┐ ┌──────────────────┐ │ 12V 电池│──── +12V ──────►│ VMS(电机电源) │ 电机 │ │──── GND ───┬────►│ GND │──OUT1──┐ └────────┘ │ │ │ [M] │ │ ENA (PWM 调速)◄──┤──OUT2──┘ 树莓派 │ │ IN1 (方向) ◄───┤ ┌────────┐ │ │ IN2 (方向) ◄───┤ │ GPIO18 │──ENA──────────── │ │ │ GPIO23 │──IN1──────────── │ 只吃信号,不供电 │ │ GPIO24 │──IN2──────────── │ │ │ GND │────────────┴────► 共地!必须连通 │ └────────┘ └──────────────────┘ 铁律: 电机电流大 → 走外部电源;GPIO 只发控制信号两电源共地
绝不用树莓派直接驱动电机

直流电机、步进电机、大功率舵机的电流都远超 GPIO 的 16mA 上限(甚至超过树莓派整机供电能力)。直接接会导致:欠压死机、SD 卡损坏、烧毁 GPIO 或 SoC。必须用驱动板 + 外部电源。哪怕是小舵机,多个一起动也建议外部 5V 供电。GPIO 永远只发信号。

舵机控制:转到指定角度

舵机三根线:棕/黑=GND,红=电源(5V),橙/黄=信号(接 GPIO18 硬件 PWM 脚)。单个小舵机可从树莓派 5V 脚取电,多个则用外部 5V。

# servo.py — 舵机角度控制(AngularServo 直接给角度)
from gpiozero import AngularServo
from time import sleep

# min/max_pulse_width 需按舵机型号微调(单位秒)
servo = AngularServo(
    18,                          # GPIO18 硬件 PWM
    min_angle=0, max_angle=180,
    min_pulse_width=0.0005,        # 0.5ms → 0°
    max_pulse_width=0.0025,        # 2.5ms → 180°
)

while True:
    servo.angle = 0      # 转到 0 度
    sleep(1)
    servo.angle = 90     # 转到 90 度(居中)
    sleep(1)
    servo.angle = 180    # 转到 180 度
    sleep(1)
舵机抖动怎么解决?

用默认软件 PWM 时舵机常轻微抖动(系统调度不稳)。解决:安装 pigpio 守护进程并让 gpiozero 用它作为 pin factory——sudo apt install pigpio; sudo systemctl enable --now pigpiod,然后设环境变量 GPIOZERO_PIN_FACTORY=pigpio。pigpio 用 DMA 生成稳定 PWM,舵机纹丝不动。

直流电机:调速与正反转

L298N 接好后(见上图),gpiozero 的 Motor 类封装了方向+PWM,PWMOutputDevice 控制使能脚调速:

# dc_motor.py — 直流电机调速与换向(L298N)
from gpiozero import Motor, PWMOutputDevice
from time import sleep

# IN1=GPIO23 IN2=GPIO24 控制方向;ENA=GPIO18 控制速度
motor = Motor(forward=23, backward=24)
enable = PWMOutputDevice(18)

print("正转 50% 速度")
enable.value = 0.5     # PWM 占空比 = 速度
motor.forward()
sleep(2)

print("正转全速")
enable.value = 1.0
sleep(2)

print("反转 70%")
enable.value = 0.7
motor.backward()
sleep(2)

motor.stop()          # 停止
enable.off()

如果驱动板把使能脚直接跳线到高电平(不调速,只控方向),可用更简单的 Motor(..., pwm=True)

# motor 自带 PWM:value 从 -1(全速反) 到 1(全速正)
from gpiozero import Motor
motor = Motor(forward=23, backward=24, pwm=True)
motor.value = 0.6    # 正转 60%
motor.value = -0.4   # 反转 40%
motor.stop()

步进电机:精确定位

以最常见的 28BYJ-48 步进电机 + ULN2003 驱动板为例(4 相,用 4 个 GPIO 控制)。它靠激励序列一步步转动:

# stepper.py — 28BYJ-48 步进电机(ULN2003,半步驱动)
from gpiozero import OutputDevice
from time import sleep

# 4 根信号线接 IN1-IN4
pins = [OutputDevice(p) for p in (6, 13, 19, 26)]

# 8 拍半步激励序列(每一行=四个线圈的通断状态)
SEQ = [
    [1,0,0,0], [1,1,0,0], [0,1,0,0], [0,1,1,0],
    [0,0,1,0], [0,0,1,1], [0,0,0,1], [1,0,0,1],
]

def step(steps, delay=0.002, direction=1):
    for _ in range(steps):
        for state in (SEQ if direction > 0 else reversed(SEQ)):
            for pin, val in zip(pins, state):
                pin.value = val
            sleep(delay)

# 28BYJ-48 半步一圈约 4096 步
step(4096, direction=1)    # 正转一圈
step(2048, direction=-1)   # 反转半圈
for p in pins: p.off()     # 全部断电(防发热)

三类电机选型对照

类型控制方式驱动模块特点适用
舵机50Hz PWM 定角度无需(直接接)可保持角度,0~180°云台、机械关节、开关阀门
直流电机方向脚 + PWM 调速L298N / TB6612转速快,无位置反馈轮子、风扇、水泵
步进电机激励序列脉冲ULN2003 / A4988精确定位,扭矩大3D 打印、机械臂、精密进给
步进电机记得断电

步进电机保持位置时线圈持续通电会发热(尤其长时间不动)。定位完成后若不需要保持扭矩,把所有线圈引脚 off() 断电,既省电又防止 ULN2003 和电机过热。需要保持位置的场合(如举着重物)才保持通电。

本章小结

PWM 通过占空比控制平均功率,是电机控制的基础。软件 PWM 任意脚可用但可能抖动,硬件 PWM(GPIO12/13/18/19)稳定,用 pigpio 作 pin factory 可让软件 PWM 也很稳。舵机用 50Hz PWM 编码角度,gpiozero 的 AngularServo 直接给角度;抖动就上 pigpio。直流电机必须经 L298N(H 桥)+ 外部电源驱动,gpiozero 的 Motor 控方向、PWM 使能脚调速。步进电机(28BYJ-48+ULN2003)靠激励序列精确定位,定位后记得断电防热。贯穿全章的铁律:电机的大电流绝不从树莓派取,走外部电源,GPIO 只发信号,两电源必须共地;感性负载注意续流保护。下一章我们接上摄像头,让树莓派"看见"世界。