1. 项目概述与核心思路大家好我是老陈一个在嵌入式开发和机器人领域摸爬滚打了十多年的老玩家。今天想和大家分享一个我最近带学生做的、也特别适合个人爱好者复现的项目一个基于Raspberry Pi Pico的智能目标追踪机器人小车。这个项目的核心目标是让一个小车能够自主地“感知”前方目标比如一个人、一个移动的物体并朝着目标的方向移动实现基础的追踪功能。听起来是不是有点像电影里的情节其实用我们手边常见的开源硬件和传感器完全可以低成本地实现。整个项目的核心思路非常清晰“感知-决策-执行”。我们用超声波传感器来“看”清前方障碍物的距离用红外传感器来辅助判断路径由Pico这颗“大脑”来处理这些信息最后通过L298N电机驱动模块精准控制四个麦克纳姆轮让小车做出前进、后退、转向等动作从而实现对目标的动态跟随。为什么选择这个方案首先Raspberry Pi Pico是一款性价比极高的微控制器双核ARM Cortex-M0性能足够处理多路传感器数据和控制逻辑而且原生支持MicroPython对新手极其友好。L298N是经典的直流电机驱动芯片驱动能力足控制逻辑简单是入门机器人驱动的首选。超声波传感器HC-SR04和红外避障传感器TCRT5000都是几块钱一个的模块成本低、易用性高。麦克纳姆轮则是一个亮点它能让小车实现全向移动前后左右平移甚至原地旋转这让追踪时的机动性大大提升动作更灵活、更精准。这个项目非常适合有一定电子和编程基础想深入理解机器人底层控制逻辑的朋友。它不只是一个简单的“接线-烧录”实验而是涵盖了嵌入式系统开发、传感器数据融合、PWM电机控制、运动学基础等多个关键知识点。做完这个项目你不仅能收获一个会跑会追的智能小车更能透彻理解一个自主移动机器人是如何“思考”和“行动”的。2. 核心硬件选型与设计解析2.1 主控与驱动为什么是Pico和L298NRaspberry Pi Pico作为主控其优势在于极致的性价比和灵活性。它搭载的RP2040微控制器拥有264KB的SRAM和2MB的板载Flash运行我们这种级别的控制程序绰绰有余。更重要的是它提供了丰富的GPIO26个多功能引脚并且支持硬件PWM这对于需要同时控制多个电机速度PWM和读取多个传感器超声波、红外的场景至关重要。使用MicroPython进行开发意味着你可以用高级语言快速实现复杂逻辑调试起来也比传统的C语言在8位MCU上要直观得多。L298N电机驱动模块的选择是基于其经典的双H桥设计。一个L298N芯片可以独立驱动两个直流电机每个电机都可以实现正转、反转和调速通过PWM。对于我们的四轮麦克纳姆轮小车通常需要两个L298N模块来驱动四个电机。但为了简化初版设计我们可以先采用一个L298N驱动两个“虚拟”电机组左侧两轮并联右侧两轮并联实现差速转向。这种接法虽然牺牲了单个轮子的独立控制但极大地简化了电路和代码非常适合入门。L298N模块自带散热片和5V稳压输出可以直接给Pico供电省去了额外的电源模块让整体布线更简洁。注意并联电机时务必确保两个电机的型号、参数尽可能一致否则会因为转速差异导致小车跑偏。如果条件允许为每个电机单独配置一个驱动通道即使用两个L298N是最理想的方案能实现真正的全向移动控制。2.2 传感器的分工与协同超声波与红外传感器是机器人的“眼睛”。在这个项目中我们使用了两种传感器它们各司其职又相互配合。超声波传感器HC-SR04是我们的“主力测距仪”。它通过发射超声波并接收回波根据时间差来计算距离。其有效测距范围通常在2cm到400cm之间精度在厘米级完全满足室内追踪的需求。它的工作原理决定了它擅长检测前方较大物体的距离但对颜色、光照不敏感且对斜面、细小物体的检测效果会打折扣。我们将它安装在小车正前方用于持续测量与目标物体之间的距离这是决定小车“前进”或“停止”的核心依据。红外避障传感器TCRT5000在这里扮演了“近场卫士”和“路径辅助”的角色。它由一个红外发射管和一个接收管组成。当红外光遇到障碍物被反射回来时接收管导通输出低电平反之则输出高电平。它的检测距离很短通常几毫米到几厘米但对物体的表面反射率敏感白色反射强黑色吸收强。我们可以在小车左右两侧各安装一个用于检测非常近的侧向障碍物防止在追踪过程中撞到墙壁或其他物体。更高级的玩法是让它在特定场景下比如沿着一条黑线辅助导航。传感器融合的简单逻辑Pico会以几十毫秒为周期不断读取超声波的距离值和两个红外传感器的状态。主决策逻辑基于超声波距离如果距离大于某个安全阈值比如30cm就让小车前进如果距离小于安全阈值但大于停止阈值比如10cm就减速或保持距离如果距离过近小于5cm就后退。同时红外传感器作为“紧急刹车”或“微调”信号如果左侧红外触发说明左边太近程序会让小车稍微向右调整反之亦然。这种优先级设计超声波主导追踪红外辅助避障简单有效。2.3 动力与结构麦克纳姆轮与底盘设计麦克纳姆轮是实现全向移动的关键。每个轮子周边都有一圈倾斜45度的小辊子。通过控制四个轮子的转速和方向组合小车可以产生平面内任意方向的合力从而实现前后、左右、斜向移动以及原地旋转。这给追踪算法带来了巨大的灵活性小车不需要像普通差速小车那样先转弯再前进它可以直接向目标的侧向移动调整自身方位追踪效率更高。对于底盘原文提到了3D打印和亚克力激光切割两种方案。3D打印的优势在于可以设计非常复杂、一体化的结构方便安装传感器和走线适合有建模能力的朋友。亚克力激光切割则更快速、成本更低适合批量制作或快速迭代。在设计时有几点必须考虑电机安装座的强度电机运行时会有震动和扭力安装座必须牢固否则容易松动或断裂。建议增加加强筋或使用更厚的材料。电池仓的位置18650电池较重应尽量放置在底盘中心或靠近驱动轮的位置以降低重心提高小车高速运动时的稳定性。传感器安装高度和角度超声波传感器应水平向前安装前方不应有底盘或其它部件遮挡声波锥形区域。红外传感器则应贴近地面离地1-2cm并根据需要调整检测距离电位器。走线槽与固定点提前设计好电线走向的通道和扎带固定孔避免线材缠绕进轮子里这是很多新手容易忽略却会导致致命故障的地方。3. 电路连接详解与避坑指南电路连接是项目的骨架接错了轻则功能异常重则烧毁元件。下面我以最清晰的方式结合我踩过的坑把接线要点说透。3.1 电源系统安全供电是第一步整个系统的供电分为两部分电机动力电源和控制逻辑电源。电机动力电源使用两节18650锂电池串联约7.4V-8.4V直接接入L298N模块的VCC和GND输入端子。这个电压直接用于驱动电机。务必确认你的电机额定电压常用的是3-6V或6-12V。如果电机是6V的7.4V的锂电池组勉强可用电机转速会略快但长期使用可能发热最好在VCC输入前串联一个二极管降压或使用稳压模块降至6V。控制逻辑电源L298N模块上有一个5V输出引脚它是由板载稳压芯片从输入的VCC降压得到的。将这个5V引脚连接到Raspberry Pi Pico的VBUS引脚同时将L298N的GND连接到Pico的GND。这样就为Pico和所有传感器超声波、红外提供了稳定的5V电源。千万不要将电池电压直接接到Pico上Pico的输入电压范围是1.8-5.5V超过5.5V极易损坏重大避坑提示务必确保整个系统共地即电池的负极、L298N的GND、Pico的GND、所有传感器的GND最终都必须连接在一起。不共地会导致信号紊乱传感器读数不准Pico无法正确控制L298N。最简单的检查方法用万用表蜂鸣档测量Pico的GND和L298N的GND端子是否导通。3.2 电机驱动接线让轮子听指挥我们以一个L298N驱动左右两组电机为例假设电机已并联好。L298N引脚连接至 Pico GPIO功能说明ENAGP7左侧电机组PWM速度控制。必须连接到支持PWM输出的引脚。IN1GP6左侧电机方向控制线1。IN2GP5左侧电机方向控制线2。IN3GP4右侧电机方向控制线1。IN4GP3右侧电机方向控制线2。ENBGP2右侧电机组PWM速度控制。必须连接到支持PWM输出的引脚。方向控制逻辑以左侧电机组为例IN11 (HIGH), IN20 (LOW)电机正转小车左侧前进。IN10 (LOW), IN21 (HIGH)电机反转小车左侧后退。IN10, IN20或IN11, IN21电机刹车停止。PWM速度控制ENA和ENB引脚接收的是Pico发出的PWM信号。PWM占空比0-65535决定了电机的平均电压从而控制转速。占空比越大转速越快。实操心得在焊接电机线到L298N端子前先用杜邦线接好所有电路单独写一个简单的测试程序让左右电机分别正转、反转、调速。确认每个电机响应正确后再进行最终焊接。这能避免因接线错误导致的反复拆焊。3.3 传感器接线赋予小车感知力超声波传感器 (HC-SR04) 接线VCC- Pico的3.3V或VBUS (5V)。HC-SR04兼容5V和3.3V接5V探测距离更远。GND- Pico的GND。Trig(触发) - Pico的GP0。Pico从这个引脚发送一个10微秒的高脉冲。Echo(回响) - Pico的GP1。该引脚在超声波发出后会变为高电平直到接收到回波。Pico需要测量这个高电平的持续时间。红外传感器 (TCRT5000) 接线(以右侧为例)VCC- Pico的3.3V。红外传感器工作电流小3.3V足够。GND- Pico的GND。OUT(信号输出) - Pico的GP8。当检测到障碍物时此引脚输出低电平(0)无障碍物时输出**高电平(1)**或取决于模块设计有些是反相的务必测试确认。一个关键细节超声波传感器的Echo引脚输出是5V电平。而Pico的GPIO引脚耐受电压是3.3V直接将5V信号接入有损坏风险解决方案在Echo引脚和Pico的GP1之间串联一个1kΩ的电阻或者使用两个电阻例如1kΩ和2kΩ组成分压电路将5V分压至约3.3V再输入。这是保护Pico的重要一步很多教程会忽略。4. 核心代码实现与逻辑剖析代码是项目的灵魂。下面我将逐段解析核心代码并解释其背后的控制逻辑和参数调优思路。4.1 硬件初始化与参数定义from machine import Pin, PWM, Timer import time # 超声波传感器引脚定义 trigger Pin(0, Pin.OUT) # 触发引脚输出 echo Pin(1, Pin.IN) # 回响引脚输入 # 红外传感器引脚定义 right_ir Pin(8, Pin.IN, pullPin.PULL_UP) # 右侧红外启用内部上拉电阻 left_ir Pin(9, Pin.IN, pullPin.PULL_UP) # 左侧红外启用内部上拉电阻 # L298N电机驱动引脚定义 # 左侧电机组 ENA PWM(Pin(7)) # 左侧PWM速度控制 IN1 Pin(6, Pin.OUT) IN2 Pin(5, Pin.OUT) # 右侧电机组 ENB PWM(Pin(2)) # 右侧PWM速度控制 IN3 Pin(4, Pin.OUT) IN4 Pin(3, Pin.OUT) # PWM频率设置 ENA.freq(1000) # 设置PWM频率为1kHz ENB.freq(1000) # 频率太高电机可能啸叫太低可能抖动1k是个常用值 # 全局变量与阈值 TARGET_DISTANCE 20 # 期望保持的目标距离 (厘米) TOO_CLOSE 10 # 判定为太近的距离阈值 TOO_FAR 40 # 判定为太远的距离阈值 BASE_SPEED 30000 # 基础前进速度 (PWM占空比范围0-65535) TURN_SPEED 20000 # 转向时的速度 IR_OBSTACLE_DETECTED 0 # 假设红外检测到障碍物输出0代码解读使用machine库这是MicroPython操作硬件的核心。为红外传感器引脚设置了Pin.PULL_UP内部上拉电阻。这是因为很多红外模块在无检测时输出高阻态上拉电阻能将其稳定在逻辑高电平避免误触发。PWM.freq(1000)设置了PWM的频率。对于普通直流有刷电机50Hz到几kHz都可以。频率太低如50Hz可能会听到电机嗡嗡声频率太高则可能因MOS管开关损耗导致驱动芯片发热。1000Hz是一个兼顾性能和静音的常见值。BASE_SPEED等参数需要根据你的具体电池电压、电机型号、小车重量来实地调试。建议从小值开始慢慢增加。4.2 超声波测距函数精准测量的关键def get_distance(): 测量超声波传感器前方的距离返回厘米值 # 发送一个10微秒的高脉冲触发信号 trigger.low() time.sleep_us(2) trigger.high() time.sleep_us(10) trigger.low() # 等待回响引脚变高并记录时间 while echo.value() 0: pass pulse_start time.ticks_us() # 等待回响引脚变低并记录时间 while echo.value() 1: pass pulse_end time.ticks_us() # 计算高电平持续时间微秒 pulse_duration time.ticks_diff(pulse_end, pulse_start) # 计算距离声速约343米/秒 (34300厘米/秒) 距离 (时间 * 声速) / 2 # 注意单位转换时间(us)是10^-6秒声速是34300 cm/s所以系数是 0.0343 / 2 distance_cm (pulse_duration * 0.0343) / 2 # 过滤掉明显错误的读数如超出传感器量程 if distance_cm 400 or distance_cm 2: return -1 # 返回-1表示测量无效 return round(distance_cm, 1)避坑指南time.sleep_us()是微秒级延时用于产生精确的10us触发脉冲这对HC-SR04正常工作很重要。time.ticks_us()和time.ticks_diff()是MicroPython提供的用于计算时间间隔的高效函数避免了整数溢出问题。超时处理上面的代码有一个潜在风险——如果超声波没有收到回波比如前方没有障碍物while echo.value() 1:这个循环会永远等待下去导致程序“卡死”。必须增加超时机制改进版带超时def get_distance(): trigger.low() time.sleep_us(2) trigger.high() time.sleep_us(10) trigger.low() timeout 100000 # 超时时间单位微秒 (100ms) start time.ticks_us() # 等待echo变高带超时 while echo.value() 0: if time.ticks_diff(time.ticks_us(), start) timeout: return -1 pulse_start time.ticks_us() # 等待echo变低带超时 while echo.value() 1: if time.ticks_diff(time.ticks_us(), pulse_start) timeout: return -1 pulse_end time.ticks_us() pulse_duration time.ticks_diff(pulse_end, pulse_start) distance_cm (pulse_duration * 0.0343) / 2 if 2 distance_cm 400: return round(distance_cm, 1) else: return -14.3 电机控制函数让运动更丝滑def motor_stop(): 停止所有电机 IN1.low(); IN2.low() IN3.low(); IN4.low() ENA.duty_u16(0) ENB.duty_u16(0) def move_forward(speed_left, speed_right): 前进可分别设置左右轮速度 IN1.high(); IN2.low() # 左侧正转 IN3.high(); IN4.low() # 右侧正转 ENA.duty_u16(speed_left) ENB.duty_u16(speed_right) def move_backward(speed): 后退 IN1.low(); IN2.high() IN3.low(); IN4.high() ENA.duty_u16(speed) ENB.duty_u16(speed) def turn_left(speed): 原地左转差速 IN1.low(); IN2.high() # 左侧反转 IN3.high(); IN4.low() # 右侧正转 ENA.duty_u16(speed) ENB.duty_u16(speed) def turn_right(speed): 原地右转差速 IN1.high(); IN2.low() # 左侧正转 IN3.low(); IN4.high() # 右侧反转 ENA.duty_u16(speed) ENB.duty_u16(speed)关键点duty_u16(值)中的值范围是0-65535对应0%-100%的占空比。speed_left和speed_right可以不同用于实现差速转向或修正跑偏。这里的turn_left和turn_right是原地旋转。如果你想实现更柔和的弧线转向可以让一侧电机停转或低速另一侧正常前进。例如柔和左转ENA.duty_u16(speed//2); ENB.duty_u16(speed)。4.4 主控制逻辑状态机与决策这是整个项目的大脑我们用一个简单的状态机来实现追踪策略。def main_control_loop(): 主控制循环 while True: # 1. 读取传感器数据 distance get_distance() left_ir_val left_ir.value() right_ir_val right_ir.value() # 2. 紧急避障红外传感器优先级最高 if left_ir_val IR_OBSTACLE_DETECTED and right_ir_val IR_OBSTACLE_DETECTED: # 左右都有障碍先后退再右转尝试 move_backward(BASE_SPEED) time.sleep(0.5) turn_right(TURN_SPEED) time.sleep(0.3) motor_stop() time.sleep(0.1) continue # 跳过本次循环的后续距离判断 elif left_ir_val IR_OBSTACLE_DETECTED: # 左侧有障碍向右微调 move_forward(BASE_SPEED // 2, BASE_SPEED) # 右轮快左轮慢 time.sleep(0.2) continue elif right_ir_val IR_OBSTACLE_DETECTED: # 右侧有障碍向左微调 move_forward(BASE_SPEED, BASE_SPEED // 2) time.sleep(0.2) continue # 3. 基于超声波距离的目标追踪逻辑 if distance -1: # 测量无效可能是目标丢失或超出范围原地缓慢旋转寻找 turn_left(TURN_SPEED // 2) time.sleep(0.5) motor_stop() elif distance TOO_CLOSE: # 太近了后退 move_backward(BASE_SPEED) time.sleep(0.3) # 后退一段时间 motor_stop() elif distance TOO_FAR: # 太远了全速前进 move_forward(BASE_SPEED, BASE_SPEED) elif TOO_CLOSE distance TOO_FAR: # 在理想区间内维持距离 if abs(distance - TARGET_DISTANCE) 5: # 距离目标很近小幅调整或停止 motor_stop() else: # 缓慢前进或后退以微调到目标距离 adjust_speed int(BASE_SPEED * 0.3) # 使用较低速度微调 if distance TARGET_DISTANCE: move_forward(adjust_speed, adjust_speed) else: move_backward(adjust_speed) else: motor_stop() # 其他情况安全停止 # 4. 控制循环延时避免CPU占用过高和传感器读数冲突 time.sleep(0.05) # 50ms的循环周期 # 启动主循环 if __name__ __main__: motor_stop() # 程序启动时先确保电机停止 time.sleep(1) # 等待系统稳定 main_control_loop()逻辑剖析优先级红外避障的优先级高于超声波追踪。这是安全第一的原则防止小车在追踪过程中撞上侧面的障碍物。状态划分根据超声波测距结果将小车与目标的距离划分为几个状态区间太近、理想范围、太远、丢失每个状态对应不同的动作。这是一种简单的**有限状态机(FSM)**思想。死区与滞后if abs(distance - TARGET_DISTANCE) 5:这行代码定义了一个“死区”。当距离非常接近目标距离时小车停止而不是持续地前后抖动。这能避免电机频繁启停让运动更平滑。循环延时time.sleep(0.05)控制了主循环的频率约20Hz。这个值不能太快否则超声波传感器可能来不及完成一次完整的测距周期HC-SR04最小测量周期约60ms。也不能太慢否则控制响应迟钝。50ms是一个比较折中的值。5. 系统调试、优化与问题排查代码烧录进去小车动起来了但很可能不是你期望的样子。别急调试是机器人项目的必修课。5.1 分模块调试法不要一上来就运行完整的追踪程序。按照以下顺序逐一验证每个模块电机测试写一个简单的测试程序只控制电机。分别测试move_forward,move_backward,turn_left,turn_right观察每个轮子的转向是否正确。如果某个轮子转向反了只需交换接到L298N对应通道的两个线序即可例如交换IN1和IN2的接线。超声波测试在循环中打印get_distance()函数的返回值。用手在传感器前移动观察打印的距离值是否变化且大致准确。如果一直返回-1或超大值检查接线、电源以及前面提到的Echo引脚5V转3.3V问题。红外测试打印左右红外传感器的值。用白纸或手在传感器前晃动观察输出是否在0和1之间变化。注意有些模块的检测距离需要调节板载电位器。集成测试将传感器数据和电机控制简单关联。例如写一个程序当超声波距离小于20cm时后退大于20cm时前进。先让小车在一个空旷地方跑起来验证最基本的“遇障则退”逻辑。5.2 常见问题与解决方案速查表现象可能原因排查步骤与解决方案电机完全不转1. 电源未接通或电压不足。2. L298N使能端(ENA/ENB)未置高或PWM占空比为0。3. 电机线未接牢或电机损坏。1. 用万用表测量电池电压和L298N输入电压。2. 检查代码中是否设置了ENA.duty_u16(xxx)且值大于0。3. 直接将电机两端接电池看是否转动。电机只朝一个方向转方向控制引脚(IN1/IN2等)设置错误或始终为同一电平。检查代码中move_forward等函数内的IN1/IN2电平设置是否正确。用逻辑分析仪或简单LED检查GPIO输出。小车跑偏1. 左右电机转速不一致机械差异或PWM占空比不同。2. 轮子打滑或地面不平。3. 电池电量下降导致两侧供电不均。1. 在代码中为左右电机设置不同的BASE_SPEED进行补偿校准。2. 确保轮子安装紧固在平整地面测试。3. 更换满电电池。超声波读数不稳定或总是-11.Echo引脚5V电平损坏Pico或信号未正确转换。2. 测量超时前方障碍物太远或吸声。3. 传感器模块质量问题。1.重点检查确保有分压电路或串联电阻。2. 在get_distance函数中增加print调试看卡在哪个while循环。3. 更换一个传感器试试。红外传感器一直触发或不触发1. 检测距离未调节。2. 环境光干扰强光下红外传感器可能失效。3. 上拉电阻未启用信号线浮空。1. 调节模块上的蓝色电位器。2. 在室内自然光或弱光下测试或为传感器加遮光罩。3. 在初始化引脚时确认设置了pullPin.PULL_UP。Pico频繁重启1. 电机启动瞬间电流过大导致电源电压被拉低Pico欠压复位。2. 接线短路。1. 在电池和L298N之间并联一个大电容如1000uF 16V缓冲电流冲击。2. 仔细检查所有接线尤其是电源正负极。追踪时小车剧烈抖动控制循环延时太短或距离判断的“死区”设置太小导致控制指令频繁正反切换。增大主循环的time.sleep()值如从0.05调到0.1或扩大距离控制的死区范围。5.3 性能优化与进阶思路当基础功能实现后你可以尝试以下优化让小车更聪明、更稳定传感器数据滤波超声波读数会有跳变。可以使用滑动平均滤波或中值滤波来平滑数据。例如保存最近5次测距结果取中值作为当前距离能有效滤除偶然的误读数。PID控制现在的控制逻辑是“开关式”的远了就全速前进近了就后退。可以引入PID控制器来让速度控制更平滑。根据距离误差当前距离-目标距离来计算一个动态的PWM值误差越大输出PWM越大这样小车在接近目标时会自动减速实现更柔和的跟踪。更智能的避障当前红外避障是直接打断追踪进程。可以结合超声波数据实现更复杂的策略比如“绕行”。当侧面有障碍时不是简单后退而是结合前方距离尝试沿着障碍物边缘移动。使用编码器给电机加上编码器可以获取轮子的实际转速和行走距离实现更精确的闭环控制和里程计为后续的路径规划打下基础。无线遥控与调试给Pico接上一个蓝牙或Wi-Fi模块如ESP-01S就可以通过手机或电脑无线发送控制指令、实时查看传感器数据调试起来会方便得多。这个基于Raspberry Pi Pico的目标追踪小车项目从硬件选型、电路搭建到软件编程、调试优化完整地走完了一个小型机器人系统的开发流程。它像一块很好的敲门砖敲开了嵌入式控制、传感器应用和机器人运动学的大门。过程中遇到的每一个问题解决的每一个bug都会让你对“机器如何感知世界并做出反应”有更深的理解。我建议你在实现基本功能后不要停下来试着去改动参数尝试不同的控制算法甚至增加新的传感器比如摄像头进行视觉追踪你会发现这个小小的平台能玩出的花样远超你的想象。