树莓派4 L298N驱动电机机器人如何添加PWM调速控制
树莓派L298N驱动电机50%功率调速实现
核心逻辑
L298N电机驱动的调速引脚为ENA(控制A通道即左电机)、ENB(控制B通道即右电机),之前代码电机满速是因为直接将这两个引脚置为高电平,没有通过PWM占空比调节输出功率。用RPi.GPIO库自带的PWM功能即可实现调速,PWM频率选1000Hz是L298N适配的常用值,不会产生明显电机啸叫。
PWM初始化与功率设置函数
PWM对象只需要在程序启动时初始化一次,不需要每次运动重复创建。功率设置函数接收0-100的数值,对应电机输出0%-100%的满功率,要实现50%功率传值50即可。
import RPi.GPIO as GPIO import time # 引脚定义 按实际接线修改为对应的BCM编号 IN1 = 17 # 左电机正转控制 IN2 = 27 # 左电机反转控制 IN3 = 22 # 右电机正转控制 IN4 = 23 # 右电机反转控制 ENA = 12 # 左电机PWM调速引脚 ENB = 13 # 右电机PWM调速引脚 # 全局PWM实例 初始化后全局复用 pwm_left = None pwm_right = None def init_hardware(): global pwm_left, pwm_right GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) # 所有控制引脚设为输出模式 for pin in [IN1, IN2, IN3, IN4, ENA, ENB]: GPIO.setup(pin, GPIO.OUT) # 初始化PWM 频率1000Hz 初始占空比0(上电静止) pwm_left = GPIO.PWM(ENA, 1000) pwm_right = GPIO.PWM(ENB, 1000) pwm_left.start(0) pwm_right.start(0) def set_motor_power(percent): """设置所有电机输出功率,percent取值0-100对应0%-100%满功率""" # 边界校验 防止输入超范围触发报错 percent = max(0, min(100, percent)) pwm_left.ChangeDutyCycle(percent) pwm_right.ChangeDutyCycle(percent)
运动逻辑中的调用方法
所有运动控制函数(前进、后退、左转、右转)在设置完四个方向引脚的电平后,直接调用set_motor_power(50)即可让电机以50%功率运转;停止时调用set_motor_power(0)切断动力输出。
def forward(run_time): # 方向设置:双电机正转 GPIO.output(IN1, GPIO.HIGH) GPIO.output(IN2, GPIO.LOW) GPIO.output(IN3, GPIO.HIGH) GPIO.output(IN4, GPIO.LOW) # 设置50%功率 set_motor_power(50) time.sleep(run_time) # 到达运行时间后停车 stop() def backward(run_time): # 方向设置:双电机反转 GPIO.output(IN1, GPIO.LOW) GPIO.output(IN2, GPIO.HIGH) GPIO.output(IN3, GPIO.LOW) GPIO.output(IN4, GPIO.HIGH) set_motor_power(50) time.sleep(run_time) stop() def left_rotate(run_time): # 原地左转:左电机反转、右电机正转 GPIO.output(IN1, GPIO.LOW) GPIO.output(IN2, GPIO.HIGH) GPIO.output(IN3, GPIO.HIGH) GPIO.output(IN4, GPIO.LOW) set_motor_power(50) time.sleep(run_time) stop() def right_rotate(run_time): # 原地右转:左电机正转、右电机反转 GPIO.output(IN1, GPIO.HIGH) GPIO.output(IN2, GPIO.LOW) GPIO.output(IN3, GPIO.LOW) GPIO.output(IN4, GPIO.HIGH) set_motor_power(50) time.sleep(run_time) stop() def stop(): # 所有方向引脚拉低 功率设为0 for pin in [IN1, IN2, IN3, IN4]: GPIO.output(pin, GPIO.LOW) set_motor_power(0)
完整可运行代码
import RPi.GPIO as GPIO import time # 引脚定义 请根据实际接线修改BCM编号 IN1 = 17 IN2 = 27 IN3 = 22 IN4 = 23 ENA = 12 ENB = 13 pwm_left = None pwm_right = None def init_hardware(): global pwm_left, pwm_right GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) for pin in [IN1, IN2, IN3, IN4, ENA, ENB]: GPIO.setup(pin, GPIO.OUT) pwm_left = GPIO.PWM(ENA, 1000) pwm_right = GPIO.PWM(ENB, 1000) pwm_left.start(0) pwm_right.start(0) def set_motor_power(percent): percent = max(0, min(100, percent)) pwm_left.ChangeDutyCycle(percent) pwm_right.ChangeDutyCycle(percent) def forward(run_time): GPIO.output(IN1, GPIO.HIGH) GPIO.output(IN2, GPIO.LOW) GPIO.output(IN3, GPIO.HIGH) GPIO.output(IN4, GPIO.LOW) set_motor_power(50) time.sleep(run_time) stop() def backward(run_time): GPIO.output(IN1, GPIO.LOW) GPIO.output(IN2, GPIO.HIGH) GPIO.output(IN3, GPIO.LOW) GPIO.output(IN4, GPIO.HIGH) set_motor_power(50) time.sleep(run_time) stop() def left_rotate(run_time): GPIO.output(IN1, GPIO.LOW) GPIO.output(IN2, GPIO.HIGH) GPIO.output(IN3, GPIO.HIGH) GPIO.output(IN4, GPIO.LOW) set_motor_power(50) time.sleep(run_time) stop() def right_rotate(run_time): GPIO.output(IN1, GPIO.HIGH) GPIO.output(IN2, GPIO.LOW) GPIO.output(IN3, GPIO.LOW) GPIO.output(IN4, GPIO.HIGH) set_motor_power(50) time.sleep(run_time) stop() def stop(): for pin in [IN1, IN2, IN3, IN4]: GPIO.output(pin, GPIO.LOW) set_motor_power(0) if __name__ == "__main__": try: init_hardware() # 测试流程:前进2s -> 后退2s -> 左转1s -> 右转1s -> 停车 forward(2) time.sleep(0.5) backward(2) time.sleep(0.5) left_rotate(1) time.sleep(0.5) right_rotate(1) except KeyboardInterrupt: # Ctrl+C中断时安全停车 stop() finally: GPIO.cleanup()
常见踩坑提醒
- L298N上ENA、ENB默认插的跳线帽必须拔掉,否则PWM信号被短路,电机永远满速,调占空比完全无效
- PWM初始化只需要在程序启动时执行一次,别在每个运动函数里重复创建PWM对象,不然会触发GPIO占用错误,电机还会莫名抖动
- 如果两个电机转速不一致,可以单独给左右电机的PWM实例设不同占空比做校准,不需要强制两个电机功率完全一致
- 运行代码需要用sudo权限,否则RPi.GPIO库无法操作硬件引脚
内容的提问来源于stack exchange,提问作者Olteanu Vasile
相关产品推荐
相关产品推荐

