
简介这是一份基于STM32与HAL库的智能小车红外循迹完整工程资源面向嵌入式初学者、智能车爱好者和课程设计学生帮助读者打通红外传感器检测、GPIO中断响应、定时器触发采样到电机差速纠偏的完整链路。资源共72个文件以45个h头文件和18个c源码文件为主体覆盖HAL库驱动、GPIO与定时器配置、传感器读取和控制逻辑同时附带MDK-ARM工程文件、CubeMX的ioc配置、hex烧录文件、启动文件以及清理脚本压缩包整体仅515KB方便直接打开工程查看或重新生成配置。目前已有5634人学习下载。通过该工程可以边阅读边调试地理解红外循迹的工作原理例如传感器遇黑白线时电平变化如何触发中断、主循环如何依据四路传感器状态判断偏移并调整电机PWMioc文件可用于重新配置引脚便于根据自己小车的硬件布局修改。工程内函数划分明确、注释清晰适合逐段研读后直接改造对准备智能车竞赛或完成STM32课程设计都有实际参考价值。 做好电机控制和基础GPIO之后很多人第一反应就是能让小车自己跑起来吗这篇文章讲的就是智能小车最经典的玩法之一——红外循迹而且是基于HAL库的完整实现。从传感器原理、选型、接线到CubeMX配置、循迹逻辑、PID升级再到调试阶段那些让人头疼的坑一次性讲透。适合已经跑通电机PWM控制、准备做循迹功能的同学也适合想借项目把HAL库的GPIO、定时器、串口这些外设串起来练手的初学者。1. 红外循迹到底在循什么1.1 反射式红外传感器的工作原理红外循迹的核心是反射式红外传感器最常见的型号是TCRT5000。它内部有一个红外发射管和一个光电接收管发射管持续发出波长约940nm的红外光接收管负责接收被物体表面反射回来的红外线。不同颜色、不同材质的表面对红外光的反射率差异很大白色、浅色表面反射率高黑色表面吸收红外光的能力强、反射率极低。于是接收管输出的电流会随表面颜色变化经过采样电阻转换为电压变化后就能区分“黑线”和“白底”。市面上常见的红外循迹模块在TCRT5000基础上还集成了一个LM393比较器和一个电位器。LM393把接收管的模拟电压与电位器设定的阈值电压做比较输出数字电平——高电平或低电平。这意味着我们不需要用ADC采样直接在STM32的GPIO上读取高低电平就能判断传感器是否压在黑线上。这个模块上那颗蓝色的电位器经常被忽略但它决定了整个系统的灵敏度。旋钮决定了比较器的阈值电压阈值调得太高传感器对黑线的反应会变迟钝调得太低又会把浅色地面误判成黑线。不同环境光、不同地面材质、不同贴纸反光率都需要重新标定所以它不是出厂设好就不用管的。1.2 为什么是TCRT5000而不是其他方案很多新手会纠结不用TCRT5000行不行当然行但入门阶段它是最省心的选择。对比几种常见循迹方案方案成本抗干扰能力开发难度适用场景TCRT5000模块带比较器低几块钱一个中受强光影响低GPIO直接读电平入门首选黑白线循迹灰度传感器中中需要ADC校准中每个传感器都要标定多级灰度赛道摄像头视觉循迹K210/OpenMV高强可软件处理高涉及图像处理竞赛级、复杂赛道TCRT5000模块的优势在于接口简单、逻辑直观、响应速度够快。对于普通黑线循迹它比摄像头方案少了一个数量级的调试工作量。如果后续想挑战更高难度再过渡到视觉循迹也不迟。1.3 硬件电路从传感器到STM32引脚传感器模块一般是三根线VCC、GND、OUT。供电电压通常为3.3V~5V实测5V供电更稳定因为LM393比较器在5V供电下输出摆幅更大逻辑电平更可靠。OUT直接接STM32的GPIO输入引脚即可不需要额外上拉或下拉因为模块输出级已经有驱动能力。一个必须强调的点传感器和单片机必须共地。也就是说传感器GND和STM32的GND要接在同一个地网络上否则OUT引脚的电平参考点不一致读到的信号完全是乱的。我见过不少新手接了VCC和OUT就是忘了GND结果数据在0和1之间乱跳一查全是共地问题。供电方面也要留意。如果电机驱动模块比如L298N或TB6612和传感器共用一个电源电机启动瞬间的电流冲击会拉低电压可能导致STM32复位。这一点在后面“常见问题”章节会详细展开。2. 基于HAL库的工程配置与接线2.1 CubeMX配置GPIO输入与PWM输出假设你已经按照系列文章第一篇搭好了基础工程系统时钟配置为72MHz左右电机分别通过定时器的PWM通道驱动方向控制引脚也初始化完毕。这次新增的核心配置是传感器输入的GPIO引脚。在STM32CubeMX中打开工程把选定的传感器引脚设置为GPIO_Input模式。这里的关键是不要开内部上拉或下拉因为传感器模块OUT引脚本身是推挽输出电平状态由模块决定内部上下拉反而可能和模块输出打架。为了调试方便强烈建议同时开启一个串口比如USART1重定向printf函数这样可以把传感器状态、ADC值、PID输出等实时打印到串口助手。循迹调试最怕的就是“盲调”肉眼盯着小车根本看不出传感器在瞬间发生了什么变化而串口数据能让你精确还原现场。电机PWM沿用上一篇的配置即可。一般用TIM2或TIM3产生两路PWM分别控制左右电机通过修改定时器比较寄存器的值来改变占空比也就是改变电机转速。2.2 接线实践与电源注意事项以常用的STM32F103C8T6最小系统板为例我习惯把三路传感器接到PB0、PB1、PB10五路传感器再加PB11、PB12。选引脚的时候注意避开JTAG引脚PB3、PB4、PA15这三个引脚在芯片上电默认是JTAG调试功能如果直接当普通GPIO用必须先在CubeMX里把调试模式改为Serial WireSWD否则GPIO配置不生效。一个常被忽略的细节传感器安装高度。模块距离地面1~2cm效果最好。太近了容易磨到地面而且比较器输出容易受微小起伏干扰太远了红外光扩散反射回来的信号弱阈值就很难调准。建议用铜柱或3D打印支架把传感器固定在一个稳定的高度千万不要用胶带随便缠在车底——传感器角度只要偏一点跑起来的状态表就完全不对了。电源分配是我的血泪教训电机驱动电源和单片机电源尽量分开。如果非要共用一个电源在单片机供电端并联一个大容量电解电容470uF以上和一个小容量的陶瓷电容104可以帮助扛住电机启动瞬间的电压跌落。2.3 读取传感器状态的核心代码HAL库读取GPIO电平非常简单如下所示uint8_t left_sensor HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0); uint8_t center_sensor HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1); uint8_t right_sensor HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10);这里返回的是GPIO_PIN_SET1或GPIO_PIN_RESET0。需要注意的是不同模块的“压线输出电平”可能不同——有的模块压到黑线输出高电平有的输出低电平。所以在写逻辑之前先用串口把三个传感器的电平状态打印出来贴上黑线前后对比一下确认极性再往下写。有些传感器模块输出信号没有硬件消抖如果电机电刷产生的电磁干扰比较强GPIO电平可能会瞬间抖动。我一般会在工业环境中加一个简单的软件滤波连续读取三次取多数值作为最终结果。循迹场景本身对响应速度要求不算极端三次读取的开销可以忽略不记。3. 循迹逻辑从单路到多路状态机3.1 单路循迹的局限性只用一个传感器也能“循迹”原理很简单传感器压到黑线时说明车偏了就往一个方向转传感器离开黑线说明转回来了就直行。听起来挺合理实际跑起来根本不是那么回事。单路传感器只能告诉你“压线了”或“没压线”完全无法区分“车身偏左”和“车身偏右”。结果就是小车走S形在黑线两侧来回摆荡速度稍微快一点就直接冲出赛道。这也是为什么市面上的循迹小车至少都是三路传感器起步。3.2 三路/五路传感器状态表与控制策略三路传感器从左到右编号L左、C中、R右分别读到的状态组合与控制策略如下。这里的1表示检测到黑线0表示在白底上LCR车体位置控制动作000全白疑似出线掉头/原地旋转回到黑线001偏右向左修正右轮减速010正中直行011略微偏右小幅度左转100偏左向右修正左轮减速110略微偏左小幅度右转111交叉线/终点直行或停车你需要根据自己传感器的极性把表里的“1”和“0”反过来但逻辑结构是一样的。这张状态表是整个循迹代码的主干用一个switch语句就能实现。五路传感器是在三路基础上增加两个检测点状态组合从8种变成32种可区分的位置信息更细腻转向动作也能做得更平滑。但五路的调试难度明显上升因为每个传感器的阈值都要单独校准。我的建议是先跑通三路把状态响应和转向逻辑调顺再升级五路加PID。3.3 差速转向的PWM输出实现转向的原理是差速左轮快、右轮慢车身向右转反过来就向左转。通过PWM占空比精确控制每一侧的速度比简单的正反转切换更平滑。void set_motor_speed(uint8_t left_speed, uint8_t right_speed) { __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, left_speed); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_2, right_speed); }基础速度我一般设置在50%~65%占空比的区间太高的话小车在弯道里会飞出去太低的话转向修正效果不明显。把转向分为两档小幅度修正时左右轮速差为20%~30%大幅度修正比如检测到1 0 0状态时速差拉到60%以上。这样既保证直线行驶稳定又能在急弯处迅速拉回车头。不要图省事直接把转向写成“一侧全速、一侧停止”这样车的运动轨迹是一个以静止轮为圆心的圆弧虽然转向力度大但车身姿态突变容易把传感器甩离黑线。4. 升级方案PID循迹的核心思路4.1 为什么状态机不够用状态机方案的缺陷很明显转向力度只有几个固定档位。车速一高尤其是连续过弯时小车会像打乒乓球一样在黑线两侧来回震荡。根本原因是状态机对偏离程度的描述是离散的无法输出“偏了一点点就修一点点”这种连续修正量。PID控制的思路则是把偏离程度量化为一个连续的误差值然后根据误差大小和变化趋势实时调节左右轮速差转向力度和偏离程度成正比。小车过弯时会提前、平滑地向内侧修正跑出来的轨迹要顺滑得多。4.2 误差模型与PID离散化实现以五路传感器为例给每个传感器位置设定一个权重从左到右依次为-2、-1、0、1、2。然后计算当前误差int error -2*s1 - 1*s2 0*s3 1*s4 2*s5;当中央传感器压线时error为0当黑线偏左时error为负值偏右时为正值。误差范围是-6到6。如果下一步想做到更细腻可以把传感器的模拟量用ADC采集做加权灰度重心计算误差连续性会更好但先搞懂离散权重版本就够用了。一个完整的PD控制函数float kp 30.0f, kd 8.0f; int last_error 0; int pid_control(int error) { int derivative error - last_error; last_error error; return (int)(kp * error kd * derivative); }不使用积分项的原因循迹是在持续运动中的定向任务积分项会累积历史误差容易在连续弯道中造成积分饱和反而让转向产生滞后和超调。PD控制对循迹场景通常已经足够。最终输出到电机int base_speed 50; int adjustment pid_control(error); set_motor_speed(base_speed - adjustment, base_speed adjustment);如果adjustment为正值说明偏右右轮速度会降低、左轮速度升高车身向左修正方向与修正方向一致。4.3 调参心得PID调参没有万能数值因为每个底盘的电机特性、轮胎摩擦、传感器布局都不一样但调参顺序有通用规律。先把kd设为0只保留kp。从小往大加直到小车在直线上不再蛇形在弯道处能勉强跟住黑线。这时候通常车会有一点震荡没关系下一步就是抑制震荡逐渐增加kd直到小车过弯平滑、直线不发飘。实测下来在车速40~50%占空比下我的底盘kp取25~40、kd取5~15效果不错这个区间可以当作初始参考。如果发现小车朝某个方向持续跑偏先检查传感器安装是否对称而不是急着调参。PID只能修正动态偏差无法弥补机械结构的不对称。把两个前轮的气压、胎纹磨损、传感器离地高度都对齐之后PID参数才能真正发挥作用。5. 调试实录常见问题与排查方法5.1 传感器误判环境光与反光干扰室内灯光和太阳光都包含大量红外成分如果环境光从侧面照进传感器接收管可能会被环境红外光“冲昏头脑”导致OUT一直输出高电平小车表现为完全无视黑线直直往前冲。排查方法很简单用手遮住传感器模块上方观察串口打印的电平是否恢复正常。如果遮光后正常说明是环境光干扰。对策有两种一是给传感器加装遮光罩用黑色热缩管或3D打印一个浅U形罩子扣在传感器上方二是把模块安装位置尽量降低让发射管和接收管形成的三角形检测区域更贴近地面天然屏蔽侧面杂光。5.2 阈值标定用串口实测数据说话传感器阈值调不准是最常见的“玄学问题”。很多同学对着电位器拧了半天感觉“差不多”结果小车一到光线变化的地方就翻车。正确的标定方法把传感器OUT引脚的电压引到单片机的ADC通道然后用串口以一定的频率打印ADC读数。分别把传感器放在白底和黑线上记录两组ADC值的范围。比如白底读数为3200~3400黑线读数为1500~1800那么阈值取两者中间偏下一点比如2500左右然后小心调整电位器让比较器在那个点附近翻转。同理如果板子引出的是AO模拟输出也可以直接给ADC通道在软件里做阈值判断这样连电位器都不用调代码里改阈值就行。两种方式选一个适合你的即可但推荐优先用数字输出的比较器版本逻辑简单、实时性好。5.3 小车冲出赛道的机械与逻辑排查速度一快就冲出赛道这是老问题。先把速度降下来再检查以下因素传感器间距是否与赛道黑线宽度匹配。传感器间距太宽车轮还没偏多少传感器就已经完全离开黑线转向响应必然迟钝。黑线宽度一般在1.5~2cm传感器的检测点间距建议维持在1.5cm以内确保黑线始终落在某个传感器检测范围内。车身重心是否稳定。很多小车的电池、控制板是后装的重心偏高或偏后高速过弯时外侧车轮附着力不足车尾向外甩传感器自然就离开黑线了。把电池尽量放在底盘中央偏后的位置降低重心对循迹稳定性提升非常明显。5.4 常见问题速查表现象可能原因解决方法传感器输出一直为高/低环境光干扰、阈值未标定加遮光罩、重新标定电位器小车走S形传感器间距过大、转向差速过猛缩小检测间距、降低转向速差直线正常但弯道冲出去基础速度过高、重心不稳降低速度、调整重心一上电就复位电机启动拉低电源电压独立供电、加滤波电容接在PB3/PB4/PA15不生效JTAG复用功能未关闭CubeMX里改为SWD模式左右修正方向相反传感器极性理解反了串口打印确认压线输出电平翻转逻辑最后说一个我调试所有智能底盘都会用的小技巧永远先把数据变成可观测的。不管是传感器电平、ADC值、PID输出还是目标速度全部通过串口打印到调试助手。你可以做一个简单的“传感器模拟表”把三路或五路传感器状态实时显示成类似[1 0 1]这种形式再看小车实际姿态就能一眼定位问题出在传感器还是执行器。没有这套观测手段所有排查都是盲人摸象。做完整套红外循迹小车之后我记得最清楚的一件事是硬件稳定远远比代码优化重要把供电弄干净、把传感器固定结实、把阈值标定准确剩下来的逻辑其实非常简单。基础版的循迹跑起来之后你还可以继续加超声波避障、蓝牙遥控、速度闭环一步一步把小车做成一个真正能应对复杂任务的移动平台。本文还有配套的精品资源点击获取