在单片机上使用Python实现PWM电机控制与舵机驱动

向北走
向北走
发布时间:2026-10-30 | 9人浏览过
原创

本文详解在单片机(如ESP32/RP2040)上使用MicroPython通过PWM实现直流电机调速与舵机角度控制的核心原理、硬件接线拓扑、数学映射算式与完整工程代码。

在单片机开发中,使用 Python(通常为 MicroPython 或 CircuitPython)驱动电机与舵机,核心在于通过片上硬件定时器生成精确的脉冲宽度调制(PWM)信号。然而,普通直流减速电机与微型舵机对 PWM 信号的响应机制存在根本差异:直流电机通过调制占空比调节等效电压以实现无级调速,而舵机则严格依赖固定频率下的脉冲高电平宽度来定位旋转角度。本文将从信号本质、电气接线规范、数学映射算法到工程代码实现,系统阐述如何使用单片机 Python 环境稳健驱动这两种执行器。

一、核心机制辨析:直流电机调速与舵机定位的 PWM 差异

初学者常将 PWM 泛化为“调节百分比输出”,这一认知在控制直流电机时尚可奏效,但在接入舵机时往往会导致抖动、卡死甚至烧毁。理解两者的根本差异是编写正确驱动代码的前提。

直流减速电机:占空比控制等效功率。直流电机属于感性负载,其转速取决于电枢两端的平均直流电压。当单片机输出高频 PWM 信号时,电机的机械惯性和电感特性会对方波进行平滑滤波。PWM 频率通常配置在 1kHz 至 20kHz 之间:频率过低(低于 1kHz)会导致电枢产生刺耳的音频电流啸叫;频率过高(高于 50kHz)则会加剧驱动芯片 MOSFET 的开关损耗。在确定频率后,改变高电平占整个周期的比例(0% 至 100% 占空比),即线性改变了施加在电机两端的有效电压与平均电流,从而实现转速调节。

位置型舵机(RC Servo):绝对脉冲宽度解码角度。舵机内部包含直流电机、减速齿轮组、反馈电位器和集成解码芯片。舵机控制线接收的 PWM 信号并不是用来给电机直接供电的,而是作为一种位置基准协议。该协议标准通常要求基准频率为 50Hz(即周期恒定为 20ms)。舵机内部芯片仅测量每个 20ms 周期内高电平脉冲的绝对持续时间:典型情况下,0.5ms 高电平对应 0° 极限界,1.5ms 对应 90° 中位,2.5ms 对应 180° 极限界。若强行调整 PWM 频率或仅把占空比按 0~100% 随意拉满,舵机内部控制回路将无法正确测量高电平时间,导致齿轮顶死产生堵转大电流。

在单片机上使用Python实现PWM电机控制与舵机驱动
图 1:直流电机连续占空比调压与舵机 50Hz 基准脉宽协议时序对比

二、硬件拓扑与电气隔离规范

在硬件连接层面,存在一个普遍的误区:“既然小型舵机和微型直流电机工作电压也是 5V 或 3.3V,是否可以直接插在单片机引脚上驱动?”答案是绝对不可行。

单片机 GPIO 引脚的稳态输出电流上限极小(ESP32 单引脚推荐最大持续电流为 12mA~20mA,RP2040 约为 12mA)。而微型空心杯直流电机即使空载启动瞬间电流也达数百毫安,堵转电流甚至超过 1A;常见的小型舵机(如 SG90)在快速变向或带负载时瞬间尖峰电流也可达 500mA 以上。直接相连会导致 GPIO 驱动晶体管热击穿,或瞬间拉低单片机内部电源轨触发硬件欠压复位(Brownout Reset)。

因此,电气系统必须遵循以下两条硬性拓扑规则:

  1. 直流电机必须接入 H 桥驱动芯片:推荐使用双极性 MOSFET 架构驱动器(如 TB6612FNG),相较于老旧的 L298N,TB6612FNG 具有极低的导通内阻,发热量显著降低。单片机输出逻辑电平信号(IN1、IN2 决定旋转方向,PWM 引脚输入脉冲控制转速)。
  2. 执行机构必须采用独立供电并保持共地:电机驱动板 VM 端与舵机 VCC 端应由独立的电池组或大功率稳压降压模块(如 DC-DC 降压至 5V)供电。同时,单片机的 GND 与外部电源的 GND 必须通过物理导线牢固连接(共地),确保控制脉冲信号具有统一的零电位参考参考面。
在单片机上使用Python实现PWM电机控制与舵机驱动
图 2:单片机与电机驱动、舵机电气隔离及统一共地网络规范

三、MicroPython 代码实现与数学算式解析

在 MicroPython 现代规范中,PWM 操作通过 machine.PWM 类实现。早期版本常用的 pwm.duty(val)(0~1023)已被更为规范且具备跨芯片通用性的 16 位整数方法 pwm.duty_u16(val) 所取代,其取值区间严格为 0 至 65535(即 0x0000 至 0xFFFF)。

1. 直流电机控制类封装与换算逻辑

对于直流电机,我们通常习惯用百分比或速度等级(如 -100% 至 +100%)来抽象控制接口。当速度为正时,输入前进电平组合(IN1=1, IN2=0);当速度为负时,输入反转电平组合(IN1=0, IN2=1);当速度为 0 时,通常执行滑行停机(IN1=0, IN2=0)或制动短接(IN1=1, IN2=1)。占空比则由速度绝对值线性映射到 0~65535:

duty_u16 = int((abs(speed) / 100.0) * 65535)

2. 舵机高精度脉宽映射算法

舵机工作在 50Hz,对应单周期时间为 20000 微秒(µs)。在 16 位精度下,每个微秒对应的寄存器计数值为:

counts_per_us = 65535 / 20000 = 3.27675

设标准舵机目标角度为 θ ∈ [0°, 180°],对应的脉冲宽度 W(微秒)计算公式为:

W = min_us + (θ / 180.0) * (max_us - min_us)

其中标准舵机的极限值一般取 min_us = 500, max_us = 2500。最终输入 duty_u16 的整型数值为:

duty_u16 = int(W * 3.27675) = int((W / 20000.0) * 65535)
在单片机上使用Python实现PWM电机控制与舵机驱动
图 3:单片机内部 PWM 信号解算处理与安全约束流程

3. 完整且可工程运行的 MicroPython 驱动源码

以下示例代码采用面向对象封装,支持在 ESP32、RP2040(树莓派 Pico)等主流微控制器上直接运行:

import time
from machine import Pin, PWM

class DCMotor:
    """基于 TB6612FNG 的直流电机双向 PWM 控制器"""
    def __init__(self, pwm_pin, in1_pin, in2_pin, freq=10000):
        self.in1 = Pin(in1_pin, Pin.OUT)
        self.in2 = Pin(in2_pin, Pin.OUT)
        self.pwm = PWM(Pin(pwm_pin))
        self.pwm.freq(freq)  # 设定为 10kHz,消除电枢电磁啸叫
        self.stop()

    def set_speed(self, speed):
        """
        设置转速:区间 -100 到 +100
        正值正转,负值反转,0 停止
        """
        # 边界约束
        speed = max(-100, min(100, speed))
        
        if speed > 0:
            self.in1.value(1)
            self.in2.value(0)
        elif speed < 0:
            self.in1.value(0)
            self.in2.value(1)
        else:
            self.stop()
            return
            
        # 计算 16 位占空比寄存器值
        duty = int((abs(speed) / 100.0) * 65535)
        self.pwm.duty_u16(duty)

    def stop(self):
        """滑行停机"""
        self.in1.value(0)
        self.in2.value(0)
        self.pwm.duty_u16(0)

    def brake(self):
        """制动快速锁死 (短接电枢)"""
        self.in1.value(1)
        self.in2.value(1)
        self.pwm.duty_u16(65535)


class Servo:
    """标准 50Hz 模拟/数字位置型舵机驱动器"""
    def __init__(self, pin_num, min_us=500, max_us=2500, max_angle=180):
        self.min_us = min_us
        self.max_us = max_us
        self.max_angle = max_angle
        self.pwm = PWM(Pin(pin_num))
        self.pwm.freq(50)  # 舵机标准工作频率:50Hz (周期 20000us)
        self._current_angle = None

    def write_angle(self, angle):
        """设定舵机转动绝对角度 (0 ~ max_angle)"""
        # 严格限制角度,防止机械堵转
        angle = max(0, min(self.max_angle, angle))
        self._current_angle = angle

        # 换算对应的高电平时间 (微秒)
        pulse_us = self.min_us + (angle / self.max_angle) * (self.max_us - self.min_us)

        # 映射到 16 位占空比 (0 ~ 65535 对应 0 ~ 20000us)
        duty = int((pulse_us / 20000.0) * 65535)
        self.pwm.duty_u16(duty)

    def deinit(self):
        """释放 PWM 资源"""
        self.pwm.deinit()


# ================= 业务演示循环 =================
if __name__ == "__main__":
    # 引脚定义(依据实际硬件板卡调整)
    # ESP32 示例:电机 PWM 使用 GPIO18, IN1/IN2 使用 GPIO19/21
    motor = DCMotor(pwm_pin=18, in1_pin=19, in2_pin=21)
    
    # 舵机信号线接入 GPIO15
    servo = Servo(pin_num=15)

    try:
        print("初始化完成,启动协同测试...")
        
        # 1. 舵机回零,电机平滑加速
        servo.write_angle(0)
        time.sleep(0.5)

        for speed in range(0, 81, 10):
            motor.set_speed(speed)
            time.sleep(0.1)

        # 2. 舵机从中位摆动至最大位,同时电机反转
        servo.write_angle(90)
        time.sleep(0.5)
        servo.write_angle(180)
        
        motor.set_speed(-60)
        time.sleep(1.0)

        # 3. 电机制动,舵机平滑复位
        motor.brake()
        servo.write_angle(90)
        time.sleep(0.5)

    except KeyboardInterrupt:
        print("用户终止程序")
    finally:
        # 安全停机释放
        motor.stop()
        servo.deinit()
        print("安全退出,硬件已复位")

四、常见故障表现、成因剖析与工程避坑指南

在实际硬件调试过程中,读者常常会遭遇以下典型反常表现。通过现象倒查物理成因,是嵌入式开发排障的高效路径:

1. 舵机抖动、角度乱偏,伴随单片机频繁软重启

成因:当舵机大步长变向时,内部电机启动瞬间的反向电动势和电流脉冲造成瞬间压降,触发了单片机稳压芯片的欠压保护(Brownout)。同时,劣质舵机内部无去耦滤波,会将高频毛刺反灌至电源线。

解决方案:在舵机电源的正负极两端(离舵机插头最近处)并联一颗 100µF~470µF 的电解电容,以及一颗 0.1µF (104) 贴片独石陶瓷电容,用于高频去耦和瞬间大电流缓冲。

2. 电机低速不转并持续发出高频蜂鸣声

成因:电机的机械转轴存在静摩擦力。如果初始给定的占空比过低(例如 15% 以下),驱动器输出的平均电压不足以克服死区转矩,电流流经绕组产生交变磁场但无法驱动转子,能量全部转化为热量并伴随线圈振动啸叫。

解决方案:引入“死区补偿算法”或“启动冲量控制”:在启动瞬时先输出 0.05 秒的高占空比(如 40%)打破静摩擦,再立即回落到目标低速;或者在软件中将最低有效速度限制在摩擦力阈值以上。

3. 舵机转到极限角度时发烫并发出持续滋滋声

成因:不同厂家生产的舵机(如 SG90 的不同批次)机械行程存在制造公差,部分舵机的实际物理止动位仅对应 0.6ms 到 2.4ms。若程序死板套用 0.5ms~2.5ms 的脉冲,舵机摇臂在触碰到物理机械限位块后依然被指令驱使旋转,内部电机被迫处于死锁堵转状态,电流激增发热。

解决方案:使用角度微调测试脚本,逐步放宽脉宽,找到舵机在自由运转下的实际安全极限(例如实测为 550µs 至 2450µs),并将这些测得值传入 Servo(min_us=..., max_us=...) 构造函数中完成校准。

在单片机上使用Python实现PWM电机控制与舵机驱动
图 4:直流电机与舵机驱动关键参数与陷阱对比总结矩阵
总结与设计建议

在单片机上使用 Python 开发机电控制系统,语法的高层抽象极大地缩短了开发周期,但底层的物理电气规律依然起决定性作用。在实际部署时,牢记“电机控高频占空比、舵机控 50Hz 绝对脉宽、电源与逻辑信号电气隔离、共地参考电位统一”四大准则,即可从根本上规避单片机复位、执行器抖动和驱动模块过热损坏等绝大多数工程问题。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表