从L298N驱动到智能小车:掌控板电机控制与PWM调速实战

发布时间:2026/7/28 9:36:49
从L298N驱动到智能小车:掌控板电机控制与PWM调速实战 1. 项目缘起从“仰望”到“仰望3”的动力困境手头有个老项目叫“仰望”是个基于掌控板的小车底盘。当初为了快速验证移动逻辑直接用了两个普通的N20减速电机通过一个简单的MOS管驱动板来驱动。跑是能跑但问题很快就暴露了动力太“肉”。平地上慢悠悠遇到个稍微有点坡度的地毯边缘或者小门槛直接就“趴窝”了轮子空转就是上不去。更别提想让它驮点传感器、摄像头之类的额外负载了那速度简直不忍直视。这让我意识到在机器人或者智能小车项目里驱动方案的选择绝不是“能转就行”。电机的扭矩、响应速度直接决定了项目的“行动力”上限。我那套旧方案驱动电流有限无法让电机发挥出应有的扭矩PWM调速范围也窄低速时抖动严重高速又无力。于是“仰望3”的动力升级计划就被提上了日程。核心目标很明确换装更大扭矩的直流电机并搭配一个足够强劲、控制灵活的驱动模块。经过一番筛选L298N这个经典的红色驱动板成了我的首选。它价格低廉、资料丰富、驱动能力强正好契合我从“玩具级”向“准工程级”过渡的需求。2. 驱动核心选型为什么是L298N市面上电机驱动模块很多从几块钱的L9110S到上百元的专业驱动都有。为“仰望3”选择L298N是基于以下几个非常实际的考量2.1 足够的驱动能力与电压适应性L298N是一款双H桥电机驱动芯片单片就能驱动两个直流电机或者一个步进电机。它的关键参数很亮眼单桥峰值输出电流可达2A持续工作电流也能达到1.5A。这意味着我可以驱动那些工作电流在1A-1.5A左右的、扭矩更足的TT马达或者N20电机而旧方案可能连0.5A都吃力。供电电压范围从5V到35V这给了电机选型极大的灵活性。我可以选择7.4V甚至12V的电机来获取更高的转速和扭矩而驱动板本身只需要一个5V逻辑电源可以从输入电源降压获得给控制部分供电即可。2.2 完善的控制逻辑与保护机制L298N的每个H桥都有三个输入两个逻辑输入IN1, IN2和一个使能端ENA。这种设计带来了清晰、灵活的控制方式方向控制通过IN1和IN2的高低电平组合可以轻松实现电机的正转、反转和刹车。速度控制将PWM脉冲宽度调制信号接入ENA端就能实现电机的无极调速。这是让小车运动平滑的关键。内置续流二极管电机是感性负载在突然停止或转向时会产生很高的反向电动势。L298N内部集成了续流二极管可以保护芯片免受这些反向电压的冲击提高了系统的可靠性。虽然对于大电流场合可能还需要外接但对于我们这种中小功率应用内置的已经足够。2.3 极高的生态成熟度与成本优势L298N面世多年几乎成了单片机驱动直流电机的“教科书”级方案。无论是Arduino、STM32还是我们的掌控板都有海量的示例代码、接线图和问题排查经验。这对于项目快速推进、降低学习成本至关重要。同时基于L298N的驱动模块就是那块红色板子价格极其亲民这让它在教育、创客和原型开发领域占据了绝对主流地位。当然它也有缺点比如效率相对较低发热量较大需要外接散热片逻辑部分和电机部分最好隔离供电以减少干扰。但对于“仰望3”这个阶段的项目其优点远远盖过了缺点。3. 硬件连接详解掌控板与L298N的对话拿到红色的L298N模块上面排针和接线端子不少初次接触容易眼花。我们一步步拆解确保接线无误。下图清晰地展示了核心的连接关系flowchart TD subgraph A [电源部分] direction LR A1[“外部电源br7-12V”] -- A2[“L298N模块br12V输入与GND”] end subgraph B [控制信号部分] B1[“掌控板P0引脚”] -- B2[“L298N ENA使能端”] B3[“掌控板P1/P2引脚”] -- B4[“L298N IN1/IN2逻辑端”] end subgraph C [电机部分] C1[“L298N OUT1/OUT2”] -- C2[“直流电机A”] end A2 -- C1 B2 -- C1 B4 -- C1 A --“共地”-- B3.1 电源系统的连接最关键的一步很多驱动不稳定、芯片发烫甚至烧毁的问题都源于电源接错。请务必遵循以下顺序准备电源你需要一个独立的直流电源电压在7V到12V之间根据你的电机额定电压选择常见的是6V或12V。一个旧的路由器电源适配器输出12V/1A以上就是很好的选择。绝对不要试图用掌控板的USB口或3.3V引脚来给电机供电电流远远不够。连接驱动板主电源将外部电源的正极VCC接到L298N模块标有“12V”或“VCC”的接线端子上负极GND接到模块的“GND”端子。连接逻辑电源L298N模块上有一个“5V输出/输入”的跳线帽和引脚。这里的逻辑是如果外部电源电压≤12V你可以插上这个跳线帽。此时模块会通过内部稳压芯片从你的外部电源如12V中降压出一个5V这个5V会供给L298N芯片的逻辑部分控制电路使用。同时这个5V引脚还可以作为输出给你的掌控板供电如果外部电源电压12V比如24V必须拔掉跳线帽否则会烧坏内部稳压芯片。然后你需要从外部引入一个独立的5V电源比如USB口或5V稳压模块接到这个“5V”引脚专门给逻辑部分供电。为掌控板供电为了简化布线并确保共地我强烈建议使用上述第3点中提到的“5V输出”功能。即插上跳线帽然后用一根杜邦线从L298N模块的“5V”引脚连接到掌控板的“VCC”或“5V”引脚。同时用另一根线将L298N模块的“GND”与掌控板的“GND”连接起来。这样一个电源就同时解决了电机驱动和主控板的供电问题且保证了地线一致。注意务必确保掌控板和L298N之间有一个共用的GND地线这是所有数字信号正确通信的基础。没有共地控制信号将是混乱的。3.2 控制信号的连接这里以驱动一个电机为例电机接在OUT1和OUT2上使能端 ENA连接到掌控板的一个支持PWM输出的引脚例如P0。这个引脚将输出PWM波来控制电机速度。逻辑输入端 IN1 和 IN2连接到掌控板的任意两个数字IO口例如P1和P2。这两个引脚输出高低电平来控制电机方向。3.3 电机的连接将电机的两根线分别接入L298N模块的“OUT1”和“OUT2”接线端子。极性暂时不用管如果转向反了在软件中调换IN1和IN2的逻辑即可或者直接调换电机线。4. 软件驱动逻辑从基础控制到运动函数封装硬件连接妥当后我们来编写掌控板的程序。我们将使用mPython X基于Blockly或Python进行开发。4.1 引脚定义与基础控制函数首先我们需要初始化对应的引脚。from mpython import * import time # 引脚定义 ENA PWM(Pin(Pin.P0), freq1000, duty0) # P0作为PWM输出初始占空比为0停止 IN1 Pin(Pin.P1, Pin.OUT) IN2 Pin(Pin.P2, Pin.OUT) # 基础电机动作函数 def motor_stop(): 电机停止/刹车 IN1.value(0) IN2.value(0) ENA.duty(0) # 同时关闭PWM输出更稳妥 def motor_forward(speed): 电机正转speed范围0-1023 IN1.value(1) IN2.value(0) ENA.duty(speed) # 设置PWM占空比 def motor_backward(speed): 电机反转speed范围0-1023 IN1.value(0) IN2.value(1) ENA.duty(speed) def motor_brake(): 电机刹车短接两端 IN1.value(1) IN2.value(1) # 同时为高电机两端短接快速制动 ENA.duty(1023) # 使能端保持高电平代码解读PWM(Pin(Pin.P0), freq1000, duty0)在P0引脚上创建一个PWM对象频率设为1000Hz对于电机调速这个频率比较合适听不到尖叫声初始占空比为0。IN1.value(1)和IN2.value(0)的组合对应了L298N内部H桥的开关状态从而使电流从一个方向流过电机实现正转。反转则调换高低电平。刹车有两种motor_stop()是让电机惯性滑行停止motor_brake()是主动刹车将电机两端短接利用电机的反电动势产生制动力矩停止更快。4.2 PWM调速的深入理解与应用PWM调速的本质不是调节电压而是通过高速开关来调节平均功率。ENA.duty(speed)中的speed参数在掌控板中通常是0-1023对应0%-100%的占空比。duty(0)一直低电平电机无电压。duty(512)50%的时间高电平50%低电平电机获得约一半的平均电压。duty(1023)一直高电平电机获得全电压。实操心得一找到电机的“启动阈值”很多电机在低占空比下无法启动表现为抖动或“嗡嗡”响而不转。你需要做一个简单的测试写一个循环让占空比从0逐渐增加到1023观察电机从多少开始能平稳启动。假设这个值是150。那么在你的运动函数里对于任何小于150的speed输入你都应该将其视为0停止或者至少映射到150以上以避免堵转发热。def safe_motor_forward(speed): 带死区保护的正转函数 START_THRESHOLD 150 if speed 0 and speed START_THRESHOLD: speed START_THRESHOLD elif speed 0: motor_stop() return # 确保speed不超过1023 speed min(speed, 1023) motor_forward(speed)4.3 封装小车运动库对于一个双电机差速驱动的小车“仰望3”就是这种我们需要同时控制左右两个电机。我们可以封装一个更高级的Car类。class L298N_Car: def __init__(self, ena_l, in1_l, in2_l, ena_r, in1_r, in2_r): # 初始化左电机引脚 self.pwm_l PWM(Pin(ena_l), freq1000, duty0) self.in1_l Pin(in1_l, Pin.OUT) self.in2_l Pin(in2_l, Pin.OUT) # 初始化右电机引脚 self.pwm_r PWM(Pin(ena_r), freq1000, duty0) self.in1_r Pin(in1_r, Pin.OUT) self.in2_r Pin(in2_r, Pin.OUT) self._stop() # 初始化时停止 def _motor_control(self, pwm_obj, in1, in2, speed): 内部电机控制方法 if speed 0: in1.value(1) in2.value(0) pwm_obj.duty(abs(speed)) elif speed 0: in1.value(0) in2.value(1) pwm_obj.duty(abs(speed)) else: in1.value(0) in2.value(0) pwm_obj.duty(0) def _stop(self): 停止所有电机 self._motor_control(self.pwm_l, self.in1_l, self.in2_l, 0) self._motor_control(self.pwm_r, self.in1_r, self.in2_r, 0) def move(self, left_speed, right_speed): 核心运动函数独立设置左右轮速度范围-1023~1023负值为反转 # 可以在这里加入死区保护、速度限幅等 left_speed max(-1023, min(1023, left_speed)) right_speed max(-1023, min(1023, right_speed)) self._motor_control(self.pwm_l, self.in1_l, self.in2_l, left_speed) self._motor_control(self.pwm_r, self.in1_r, self.in2_r, right_speed) def forward(self, speed): 前进 self.move(speed, speed) def backward(self, speed): 后退 self.move(-speed, -speed) def turn_left(self, speed): 原地左转差速 self.move(-speed, speed) # 左轮后退右轮前进 def turn_right(self, speed): 原地右转 self.move(speed, -speed) # 左轮前进右轮后退 def stop(self): 停止 self._stop() # 实例化小车对象假设左电机接P0,P1,P2右电机接P3,P4,P5 my_car L298N_Car(ena_lPin.P0, in1_lPin.P1, in2_lPin.P2, ena_rPin.P3, in1_rPin.P4, in2_rPin.P5) # 使用示例 my_car.forward(500) # 半速前进 time.sleep(2) my_car.turn_right(300) # 中速右转 time.sleep(1) my_car.stop()通过这样的封装上层应用只需要调用my_car.forward(600)这样简单的接口底层复杂的引脚控制和PWM设置都被隐藏起来代码可读性和可维护性大大提升。5. 实测调优与常见问题排查硬件连接和基础代码都完成后上电测试往往不会一帆风顺。以下是几个我踩过的坑和对应的解决方案。5.1 电机不转但驱动板指示灯正常排查步骤1检查使能端。L298N模块上通常有一个“ENA”的跳线帽。如果这个跳线帽插着意味着使能端被直接拉高始终有效。此时掌控板P0引脚输出的PWM信号是无效的。解决方案拔掉ENA的跳线帽确保我们的PWM信号能接入。排查步骤2检查逻辑电平。用掌控板的代码控制IN1和IN2分别输出高电平(1)和低电平(0)同时用万用表测量L298N模块上对应引脚的电平是否变化。确保信号成功送达。排查步骤3测试电机和电源。直接将电机两端接到一个3V-5V的电池上看电机是否正常转动。将外部电源直接接到电机驱动板的“12V”和“GND”用万用表测量“OUT1”和“OUT2”之间的电压在代码控制正反转时电压极性是否变化。5.2 电机抖动、异响或转速不稳定原因1PWM频率不合适。频率太低如100Hz以下电机会听到明显的“滋滋”声并抖动。频率太高如20kHz以上可能超出L298N的开关响应极限导致控制异常。解决方案将PWM频率调整在500Hz到2kHz之间我通常用1kHz这是一个比较均衡的值。原因2电源功率不足。电机启动瞬间电流很大如果电源适配器最大电流不足比如只有500mA会导致电压被拉低整个系统复位或工作不稳定。解决方案使用额定电流更大的电源建议1A以上或者在电机电源输入端并联一个较大容量的电解电容如470uF/16V来缓冲瞬间电流需求。原因3未接续流二极管对于大电流情况。虽然L298N内置了二极管但在驱动较大电流电机时内置二极管可能不足以快速泄放能量导致芯片发热甚至损坏。解决方案在电机的两个端子之间反向并联一个快速恢复二极管如1N4007阳极接OUT2阴极接OUT1为反向电动势提供额外的泄放通路。5.3 L298N芯片异常发热原因1散热不良。L298N在工作时会有导通损耗发热是正常的但如果烫到无法触摸就不正常了。解决方案务必给L298N芯片贴上散热片甚至加一个小风扇。确保模块周围通风良好。原因2电机堵转或负载过大。当电机被卡住时电流会急剧上升接近短路状态所有能量都转化为热量耗散在芯片上。解决方案在机械设计上避免卡死在软件上加入电流检测或超时保护例如电机使能后如果编码器反馈一段时间内转速为0则自动停止。原因3电源电压过高或逻辑/电机电源未隔离。如果电机电源电压远高于电机额定电压或者逻辑5V和电机电源共地不良引入干扰也会导致异常发热。解决方案检查电压匹配确保共地可靠在逻辑电源和电机电源之间可以加一个磁珠或小电感进行隔离。实操心得二利用掌控板的OLED进行实时调试在调试运动控制时将关键参数打印到掌控板自带的OLED屏幕上非常有用。你可以实时显示左右轮的目标速度、实际PWM占空比、电池电压通过ADC读取等。这能帮你直观地看到控制指令是否按预期发出以及系统的工作状态。def display_car_info(left_spd, right_spd, battery_voltage): oled.fill(0) # 清屏 oled.DispChar(L: {:4d}.format(left_spd), 0, 0) oled.DispChar(R: {:4d}.format(right_spd), 0, 16) oled.DispChar(Bat: {:.2f}V.format(battery_voltage), 0, 32) oled.show()6. 从基础驱动到“仰望3”的智能升级基础驱动稳定后就可以为“仰望3”赋予更智能的行为了。这超越了简单的“通电即转”进入了机器人控制的领域。6.1 闭环速度控制开环PWM控制就是我们目前做的有个问题电池电压下降、地面摩擦变化、负载不同都会导致同样的PWM占空比下电机的实际转速不同。为了实现精确的直行或转弯需要引入反馈。最经济的方式是使用编码电机。这种电机尾部自带一个霍尔编码器可以输出两路相位差90度的方波A相和B相通过捕捉方波的频率和顺序就能计算出电机的转速和方向。我们需要将编码器的A、B相信号接到掌控板的中断引脚上。然后在代码中为每个脉冲触发中断进行计数。from mpython import * import time # 编码器计数变量 volatile 在中断中修改 left_encoder_count 0 right_encoder_count 0 def left_encoder_handler(pin): global left_encoder_count left_encoder_count 1 def right_encoder_handler(pin): global right_encoder_count right_encoder_count 1 # 假设左编码器A相接P6右编码器A相接P7 left_encoder_pin Pin(Pin.P6, Pin.IN, Pin.PULL_UP) right_encoder_pin Pin(Pin.P7, Pin.IN, Pin.PULL_UP) # 设置中断在下降沿触发 left_encoder_pin.irq(triggerPin.IRQ_FALLING, handlerleft_encoder_handler) right_encoder_pin.irq(triggerPin.IRQ_FALLING, handlerright_encoder_handler) def get_speed(count_last, count_now, time_interval): 计算速度脉冲数/时间间隔 - 转速需根据轮子周长换算为线速度 pulse_diff count_now - count_last return pulse_diff / time_interval # 返回脉冲频率 # 在主循环中定期计算速度并调整PWM last_time time.ticks_ms() last_left_count 0 last_right_count 0 target_speed 100 # 目标脉冲频率 while True: current_time time.ticks_ms() dt time.ticks_diff(current_time, last_time) / 1000.0 # 转换为秒 if dt 0.1: # 每100ms计算一次 left_speed get_speed(last_left_count, left_encoder_count, dt) right_speed get_speed(last_right_count, right_encoder_count, dt) # 简单的P比例控制器调整PWM left_pwm my_car.current_left_pwm 0.5 * (target_speed - left_speed) # 假设my_car对象能记录当前PWM right_pwm my_car.current_right_pwm 0.5 * (target_speed - right_speed) # 限幅并应用新的PWM left_pwm max(0, min(1023, int(left_pwm))) right_pwm max(0, min(1023, int(right_pwm))) my_car.move(left_pwm, right_pwm) # 更新上一次的计数和时间 last_left_count left_encoder_count last_right_count right_encoder_count last_time current_time time.sleep_ms(10)这是一个非常简化的P控制器。通过比较目标速度和实际速度的差值来动态调整PWM输出从而让电机抵抗外部干扰保持稳定转速。这样即使左右轮摩擦力略有不同小车也能走得更直。6.2 运动路径规划与执行有了稳定的速度控制就可以规划更复杂的运动。例如让小车走一个正方形def run_square(side_time, turn_time, speed): 走一个正方形 for _ in range(4): my_car.forward(speed) # 前进 time.sleep(side_time) my_car.turn_right(speed) # 右转90度需要根据实际情况调整turn_time time.sleep(turn_time) my_car.stop()更高级的可以结合掌控板上的蓝牙或Wi-Fi模块接收来自手机或电脑的指令实现遥控。或者利用板载的加速度计、陀螺仪如果掌控板型号支持实现自平衡或姿态控制。6.3 电源管理与低功耗考虑“仰望3”作为移动平台续航很重要。除了选择容量合适的电池在软件上也可以优化空闲时进入低功耗模式当小车长时间待命时可以停止PWM输出并将L298N的使能端拉低如果硬件支持甚至让掌控板进入睡眠模式。动态电压调节如果电池电压可测可以在电池电压较低时适当降低电机的最大PWM占空比即限制最高速度以延长工作时间防止电池过放。7. 项目总结与进阶思考经过这一轮从选型、接线、编程到调试的完整流程“仰望3”的动力系统成功地从“孱弱”升级到了“充沛”。L298N模块虽然古老但其稳定、易用的特性让它依然是学习电机驱动原理和进行中小型项目原型开发的绝佳选择。回顾整个过程有几个关键点值得再次强调电源是根基独立、足功率的电机电源以及正确、共地的逻辑电源连接是系统稳定的前提。超过一半的奇怪问题都出在电源上。理解数据手册即使使用现成的模块花10分钟看看L298N芯片的数据手册了解其内部H桥结构、逻辑真值表和最大额定参数能让你在出错时更快地定位问题。软件分层从最底层的引脚操作封装成电机函数再封装成小车对象最后实现业务逻辑如走正方形。这种分层结构让代码清晰调试方便也便于后续增加编码器、PID控制等更复杂的功能。调试工具万用表、OLED屏显示、甚至一个简单的串口打印都是你延长的手臂和眼睛能极大提升调试效率。这次升级只是一个起点。L298N有其性能天花板当未来“仰望”项目需要驱动更大型的电机、追求更高的效率比如使用TB6612FNG等MOSFET驱动芯片或更精确的控制如FOC矢量控制时就需要探索新的驱动方案。但通过L298N打下的坚实基础——对H桥、PWM、方向控制、电源管理的深刻理解——会让你在面对任何更复杂的驱动芯片时都能从容应对。动手去接一次线写一行代码调一个参数远比读十篇教程收获更大。