4. 直流电机驱动基础:L298N与H桥实战
直流电机驱动,说白了就是让电机转起来、转得快、转得准。很多初学者一上来就想着用GPIO直接怼电机,结果不是烧IO口就是电机纹丝不动。嗯,这里有个基本常识——树莓派的GPIO输出电流也就十几毫安,而一个普通直流电机启动瞬间可能吃掉几百毫安甚至几安培。所以,驱动芯片是必须的。
我个人习惯用L298N,这芯片皮实、便宜、资料多,非常适合教学和原型验证。今天我们就把它彻底讲透。
4.1 L298N电机驱动模块解剖
L298N本质上是一个双H桥驱动芯片,内部集成了四个半桥驱动电路。每个H桥可以独立控制一路直流电机,或者两路一起控制一个步进电机。我最早用这芯片是在一个智能小车项目上,当时图省事没看散热,结果跑了几分钟芯片烫得能煎鸡蛋——后来才知道,L298N的饱和压降有2V左右,电流一大发热量相当可观。
| 引脚/端子 | 功能说明 | 典型连接 |
|---|---|---|
| VCC (12V) | 电机供电正极 | 6-12V直流电源 |
| GND | 电源地 | 与树莓派共地 |
| 5V | 逻辑供电输出/输入 | 接树莓派5V(跳帽选择) |
| ENA / ENB | 使能A/B通道 | 接GPIO(PWM调速) |
| IN1 / IN2 | 电机A方向控制 | 接GPIO |
| IN3 / IN4 | 电机B方向控制 | 接GPIO |
| OUT1 / OUT2 | 电机A接线端 | 接直流电机 |
| OUT3 / OUT4 | 电机B接线端 | 接直流电机 |
4.2 H桥电路工作模式
H桥为什么叫H桥?你想想看,四个开关管排成H形,电机在中间横杠上。通过控制四个开关的通断组合,就能实现电机的正转、反转、刹车和惰行。
下面这张图是我手绘的H桥工作模式示意,四种状态一目了然:
4.3 电机正反转与调速控制
控制逻辑其实很简单。以电机A为例,IN1和IN2决定方向,ENA决定是否使能以及速度。我习惯把ENA接在支持硬件PWM的GPIO上,这样调速更平滑。
| IN1 | IN2 | ENA | 电机状态 |
|---|---|---|---|
| 0 | 0 | X | 惰行(自由停止) |
| 0 | 1 | PWM | 反转(速度由PWM占空比决定) |
| 1 | 0 | PWM | 正转(速度由PWM占空比决定) |
| 1 | 1 | 1 | 刹车(急停) |
调速的原理说白了就是PWM——通过快速开关电源,改变平均电压。占空比50%意味着电机得到一半的电压,转速大约是一半。但要注意,PWM频率不能太低,否则电机会发出嗡嗡声。我一般用1kHz到10kHz,太低会听到明显的开关噪声,太高的话驱动芯片的开关损耗会增大。
4.4 Python控制代码实现
下面是我写的一个完整控制类,支持双电机独立控制。代码里我用了RPi.GPIO库,这是树莓派上最常用的GPIO库。
import RPi.GPIO as GPIO
import time
class L298NMotor:
def __init__(self, ena, in1, in2):
"""
初始化一个电机通道
:param ena: 使能引脚(PWM)
:param in1: 方向引脚1
:param in2: 方向引脚2
"""
self.ena = ena
self.in1 = in1
self.in2 = in2
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.ena, GPIO.OUT)
GPIO.setup(self.in1, GPIO.OUT)
GPIO.setup(self.in2, GPIO.OUT)
# 初始化PWM,频率1kHz
self.pwm = GPIO.PWM(self.ena, 1000)
self.pwm.start(0)
# 默认停止
GPIO.output(self.in1, GPIO.LOW)
GPIO.output(self.in2, GPIO.LOW)
def forward(self, speed=50):
"""正转,speed为0-100的百分比"""
GPIO.output(self.in1, GPIO.HIGH)
GPIO.output(self.in2, GPIO.LOW)
self.pwm.ChangeDutyCycle(speed)
def backward(self, speed=50):
"""反转"""
GPIO.output(self.in1, GPIO.LOW)
GPIO.output(self.in2, GPIO.HIGH)
self.pwm.ChangeDutyCycle(speed)
def stop(self, brake=False):
"""停止,brake=True为刹车,False为惰行"""
if brake:
GPIO.output(self.in1, GPIO.HIGH)
GPIO.output(self.in2, GPIO.HIGH)
self.pwm.ChangeDutyCycle(100)
else:
GPIO.output(self.in1, GPIO.LOW)
GPIO.output(self.in2, GPIO.LOW)
self.pwm.ChangeDutyCycle(0)
def set_speed(self, speed):
"""运行时动态调速"""
self.pwm.ChangeDutyCycle(speed)
def cleanup(self):
"""清理资源"""
self.pwm.stop()
GPIO.cleanup()
# 使用示例
if __name__ == "__main__":
# 电机A:ENA=18, IN1=23, IN2=24
motor_a = L298NMotor(ena=18, in1=23, in2=24)
try:
print("正转,速度50%")
motor_a.forward(50)
time.sleep(3)
print("调速到80%")
motor_a.set_speed(80)
time.sleep(3)
print("反转,速度30%")
motor_a.backward(30)
time.sleep(3)
print("刹车停止")
motor_a.stop(brake=True)
time.sleep(1)
print("惰行停止")
motor_a.stop(brake=False)
except KeyboardInterrupt:
pass
finally:
motor_a.cleanup()
- PWM频率设为1kHz,兼顾平滑度和开关损耗
- stop()方法区分刹车和惰行,实际项目中刹车更常用
- set_speed()可以在电机运行时动态调整,不需要先停止
- cleanup()一定要在程序退出时调用,否则GPIO状态会残留
- 树莓派GND ↔ L298N GND(必须共地)
- L298N 5V输出 → 树莓派5V(如果跳帽插着,L298N会输出5V给树莓派)
- 电机电源正极 → L298N 12V输入(注意电压范围6-12V)
- ENA/ENB → 树莓派支持硬件PWM的GPIO(如18、13)
- IN1-IN4 → 任意空闲GPIO
- 电机线 → OUT1/OUT2(极性无所谓,反了换线或改代码)
实际跑起来你会发现,电机在低速时(占空比低于20%)可能转不动,这是直流电机的通病——启动转矩不够。我的经验是,如果项目需要低速平稳运行,最好加编码器做闭环控制,或者换用带减速箱的电机。不过那是后面章节的内容了,今天先把L298N玩转再说。