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接线端 接直流电机
共地警告: 树莓派的GND必须与L298N的GND连接。我曾经见过有人没共地,结果控制信号完全乱飘,电机时而转时而不转。记住,所有数字信号都是相对地电平,地不连,信号就是浮空的。

4.2 H桥电路工作模式

H桥为什么叫H桥?你想想看,四个开关管排成H形,电机在中间横杠上。通过控制四个开关的通断组合,就能实现电机的正转、反转、刹车和惰行。

核心逻辑: H桥的本质是控制电流流过电机的方向。电流从左到右,电机正转;从右到左,电机反转。

下面这张图是我手绘的H桥工作模式示意,四种状态一目了然:

H桥四种工作模式 正转 Q1/Q4导通 Q2/Q3截止 电流:左→右 IN1=1 IN2=0 ENA=PWM 反转 Q2/Q3导通 Q1/Q4截止 电流:右→左 IN1=0 IN2=1 ENA=PWM 刹车 Q1/Q3导通 Q2/Q4截止 两端短路制动 IN1=1 IN2=1 ENA=1 惰行 全部截止 电机自由旋转 无制动效果 IN1=0 IN2=0 ENA=0 关键说明 • 正转/反转:通过改变电流方向实现,配合PWM调速 • 刹车:电机两端短路,利用反电动势快速停止 • 惰行:电机断电,靠惯性继续旋转(类似空挡滑行) • 注意:不要同时让Q1/Q2或Q3/Q4导通,会烧管子 • 使能脚ENA/ENB接PWM信号,占空比控制转速
避坑指南: 我曾经在调试时发现电机刹车后还有微弱转动,查了半天发现是使能脚没拉低。记住,刹车模式不仅要设置IN1=IN2=1,还要确保ENA=1(使能导通)。如果ENA=0,即使IN1=IN2=1,电机也会进入惰行状态。

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玩转再说。

调试小技巧: 如果你发现电机只朝一个方向转,检查IN1和IN2的电平。用万用表量一下,确保一个高一个低。我曾经被一根杜邦线虚焊坑了整整一下午——信号线接触不良,电机死活不反转。