
简介基于STM32F103C8T6的循迹避障小车完整工程源码包面向嵌入式入门学习者与电子设计竞赛备赛学生能够帮助理解循迹、避障两种工作模式的代码组织与实时切换思路。压缩包共292个文件、约7.73MB内部除.c/.h源码外还包含.uvproj/.ewp工程文件、.hex烧录文件、.bat批处理脚本及.map/.axf等编译链接产物各模块按功能拆分并配有详细注释直接打开工程即可阅读和编译。主函数中循迹循环与避障循环彼此独立注释掉一个即可选用另一个已实测可正常运行源码结构清晰适合在此基础上二次开发如增加蓝牙遥控、舵机转向等。目前已有21867人学习下载对有课程设计或竞赛项目需求者是一份易于上手的参考实现。 把基于STM32F103C8T6的循迹避障小车标上“完成”两个字看起来像是收工了但真正调过车的人都知道这恰恰是最磨人的阶段才刚刚开始。我手里的这版小车功能上确实做到了循迹和避障都能跑但每一个“能”字背后都是PWM波形、传感器阈值、电源纹波和状态机逻辑反复拉锯的结果。这篇记录不打算贴整包代码就完事而是把这辆小车的设计思路、硬件选型、代码结构、调参过程和踩过的坑完整梳理一遍给准备做同类型项目的朋友一份可以直接参照的“完成版”路线图。如果你手头正好有一块STM32F103C8T6最小系统板或者正被课程设计里“循迹避障”的需求折磨这篇内容应该能帮你少走不少弯路。我不讲那些大而全的理论只讲从硬件搭建到路上实测这段最真实的经历。1. 项目拆解循迹避障小车到底考的是什么1.1 两个任务背后其实是三套机制循迹和避障看起来是两个功能真正做完以后你会发现它们是三套机制在同时工作感知、决策、执行。感知层依靠两类传感器。循迹我用的是4路TCRT5000红外对管靠黑线和白底对红外光的反射率差异来检测路径避障用的是HC-SR04超声波模块通过声波往返时间测出前方障碍物距离。决策层是STM32F103C8T6读取传感器状态按预设逻辑决定下一步动作执行层则是通过PWM控制两个直流减速电机的转速差实现直行、转向、后退和原地掉头。很多新手一上来就到处找“循迹函数”和“避障函数”但真正需要先理清楚的是这三层之间怎么配合。比如车速越快传感器采样周期就要越短超声波测距本身有响应时间小车不能等到撞上了才做反应循迹传感器离地高度偏差两三毫米判定结果就完全不一样。这些都属于机制设计层面的问题不是单纯堆代码能解决的。1.2 为什么主控选STM32F103C8T6市面上做小车的主控很多51、Arduino、ESP32都有人用。我选STM32F103C8T6不是因为它最便宜而是这个选择在性能和折腾空间之间取了一个很实用的平衡。先看性能。STM32F103C8T6是Cortex-M3内核主频最高72MHzFlash 64KBRAM 20KB。对一辆巡线小车来说“循迹避障”并行处理还要输出多路PWM硬件定时器刚好够用。我的实际资源分配是TIM1输出两路PWM控制左侧电机TIM2输出两路控制右侧电机TIM4给超声波测距做计时剩下的GPIO读传感器。算下来资源非常紧凑但不至于捉襟见肘。更关键的是生态。这块芯片的资料和例程多到爆炸中文手册、标准外设库、HAL库版本都齐全。网上关于定时器PWM输出、串口调试的案例数不胜数遇到问题基本都能搜到解决办法。对做一个课程项目或者入门作品来说这种可查可问的生态比芯片本身的纸面参数重要得多。1.3 功能优先级循迹为主、避障为辅这辆小车的功能定义是“循迹避障”听起来是并列关系但实际设计里必须明确优先级。我的方案是无障碍时按循迹模式跑遇到障碍物时临时切入避障逻辑绕过障碍后再重新寻线。这个设计意味着主循环不能简单写成“循迹”和“避障”两个if分支互相独立而要有一个状态变量标记当前处于什么模式。状态切换写得好不好直接决定小车在真实路径上稳不稳定。后文我会专门用一节来讲这套状态机的具体实现。2. 硬件底座的搭建复盘从电机驱动到传感器布局2.1 电机驱动模块选L298N还是TB6612循迹避障小车最常用的两种驱动方案是L298N模块和TB6612FNG模块两者我都用过结论比较明确对比项L298NTB6612FNG逻辑电压5V2.7~5.5V输出压降较大约2V较小体积大很小发热明显较低价格便宜稍贵适合场景大功率电机小型减速电机我的小车用的是两个N20减速电机标称电压6V电流不大。最初装的是L298N结果发现模块压降把电机端电压吃掉不少高速直行时转速提不上去转弯响应也偏慢。后来换TB6612FNG同样一个PWM占空比车速明显不一样。所以如果你用的是小功率电机建议直接上TB6612FNG电源利用率和动态响应都会好很多。2.2 循迹传感器的数量和安装位置循迹原理并不复杂红外发射管发出红外光白色路面反射强黑色轨迹线吸收强接收管收到的反射量不同输出电平就不一样。我用的是4路TCRT5000模块一字排开安装在车头下方。这里有个关键点传感器间距要跟轨迹线宽度匹配。如果线宽2cm传感器探测中心点间距最好在1.5cm到2cm之间这样才能保证小车压线行驶时至少有一两路传感器落在黑线上。间距太大转弯时容易四路全部离开黑线间距太小又对直线上的微小偏移过于敏感。我最终把4路传感器总宽度控制在底盘宽度的一半左右。安装高度同样有讲究。TCRT5000的有效检测距离通常在0.5mm到10mm之间实际使用中离地5mm到8mm效果最好。太高反射信号衰减误判率上升太低又容易蹭到地面凸起。我的做法是用铜柱把传感器支架悬空固定在底盘前面再通过模块上的电位器调节灵敏度直到“压到黑线时输出电平翻转”这个临界状态稳定可靠。2.3 超声波模块的避障视角问题避障我选了HC-SR04超声波测距模块测距范围大约2cm到400cm对本项目来说足够用。它的工作方式是给Trig引脚一个10微秒以上的高电平脉冲模块自动发出8个40kHz的超声波然后Echo引脚输出一个高电平高电平持续时间就是声波往返的时间。距离 高电平时间 × 声速 / 2。不过超声波模块有个特点它的探测波束角比较大测到的是前方一个圆锥形区域内的最近物体分辨不了障碍物偏左还是偏右。为了补上这个方向盲区我做了两件事。一是把超声波模块安装在车头正前方偏上位置避开前面循迹传感器支架的遮挡。二是在程序里做了分档判断距离大于25cm按正常循迹跑小于25cm且大于12cm时减速并准备转向小于12cm时直接执行避障转向。这样单个超声波模块虽然只能测距但配合固定转向逻辑已经能应付大多数简单障碍场景。3. 代码核心逻辑逐段拆解PWM、测距与状态决策3.1 引脚分配与定时器PWM配置这辆小车的引脚分配是这样的左电机PWMPA8TIM1_CH1、PA11TIM1_CH4右电机PWMPA0TIM2_CH1、PA1TIM2_CH2循迹传感器输入PB0~PB3超声波TrigPB4EchoPB5串口调试PA9TX、PA10RX为什么PWM要交固定时器输出而不是用普通GPIO翻转模拟因为直流电机调速需要频率稳定的PWM波形用GPIO手动翻转会导致占空比不稳定而且极度浪费CPU时间。STM32的定时器自带PWM输出配置好周期和占空比后输出完全由硬件完成主循环只需要改比较寄存器的值。配置用的标准外设库核心代码大致是这样// TIM1 PWM输出初始化关键部分 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 时基配置72MHz / 72 1MHz计数1000次 1kHz PWM TIM_TimeBaseStructure.TIM_Prescaler 71; TIM_TimeBaseStructure.TIM_Period 999; TIM_TimeBaseInit(TIM1, TIM_TimeBaseStructure); // 通道1 PWM模式1 TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 500; TIM_OC1Init(TIM1, TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); // 高级定时器主输出使能少了这一步PA8永远没有波形 TIM_CtrlPWMOutputs(TIM1, ENABLE);TIM1属于高级定时器除了配置CCER里的输出使能外还要配置BDTR寄存器里的MOE主输出使能位。我调这个坑调了很久后面调试章节会细说。3.2 循迹状态判定从4路传感器到方向决策4路循迹传感器从右到左编号为R1、R2、L2、L1输出电平是“检测到黑线时为低”。我把4路信号组合成几个关键状态全白0000车在直线段上直行左偏0001或0011车头偏右需要左转修正右偏1000或1100车头偏左需要右转修正中间压黑0110等正常行驶小幅修正方向全黑1111遇到十字交叉或终点停止或进入特殊处理代码实现时优先判断L1、R1这两个边缘传感器因为它们一旦压黑说明小车已经偏得比较厉害必须加大转向时的PWM差值。中间两路传感器对应小幅修正。这种分级修正比单阈值反馈稳定得多也更容易调参。3.3 超声波测距与模式切换超声波测距的代码分两步先发送Trig脉冲并测量Echo高电平时间再把时间换算成厘米。// HC-SR04测距函数返回距离cm float HCSR04_GetDistance(void) { uint32_t t1, t2; GPIO_SetBits(GPIOB, GPIO_Pin_4); Delay_us(12); GPIO_ResetBits(GPIOB, GPIO_Pin_4); while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_5) 0) {} t1 GetTimerTick(); while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_5) 1) {} t2 GetTimerTick(); return (t2 - t1) * 0.034 / 2.0; }高电平时间测量我用的是查询方式。逻辑上没问题但要注意如果距离很近Echo高电平时间很短主循环里一旦有耗时操作就容易漏掉跳变。所以我单独封装了测距函数测距期间不让其他任务干扰。模式切换方面我在主循环里维护了一个mode变量MODE_TRACK表示循迹MODE_AVOID表示避障转向。循迹模式下每个循环先读一次超声波距离小于阈值就切到MODE_AVOID在MODE_AVOID里小车不再读循迹传感器按固定差速转向同时持续测距直到前方障碍物消失再切回MODE_TRACK。用状态机代替if嵌套逻辑清晰后期加功能也不需要大改主循环。3.4 转向策略差速控制是核心差速转向的原理不复杂左右轮速度不一样车体就会向慢的一侧转弯。我的左右电机各由两路PWM控制一路用于正转一路用于反转组合起来能实现正转、反转、停止。最简单的转向是左转时左轮减速、右轮保持。但实测发现直行速度较高时这种单侧减速的转向半径偏大急弯容易冲出去。后来我改成“一侧正转一侧反转”的原地转向需要左转时左轮反转、右轮正转差速最大小车几乎原地掉头。缺点是地面太滑时轮子会空转。最终我在代码里留了一个转向模式切换参数根据比赛场地情况手动选择直线多就单侧减速模式急弯多就原地转向模式。4. 实测调试记录阈值、速度和干扰的反复拉锯4.1 TIM1启用PWM后引脚没波形的坑这是本项目里最莫名其妙的一个问题。TIM1配置完成后用逻辑分析仪看PA8引脚始终没有波形。检查时钟、GPIO复用、CCR和ARR全都没问题最后翻了芯片参考手册才发现TIM1和TIM8是高级定时器PWM要真正输出到引脚必须设置BDTR寄存器的MOE主输出使能位。不加这一步定时器其实在工作但输出引脚一直处于无效电平。解决办法就是在初始化函数末尾加一行主输出使能操作。这个坑只针对高级定时器普通定时器不会遇到。以后但凡用TIM1或者TIM8做PWM记住先确认MOE不然排查起来非常浪费时间。4.2 循迹传感器阈值的现场标定方法TCRT5000模块上的电位器不是随便拧的。我的标定步骤是把小车放到纯白色路面上观察模块指示灯拧电位器直到指示灯刚好翻转记住临界位置再把小车放到黑线上确认指示灯能够反向翻转。反复几次找到一个“白面出一种电平、黑线出另一种电平”的稳定窗口。关键还在于标定必须在实际光照环境下做。室内灯光、太阳光、地面材质差异都会影响红外反射强度。我的习惯是标定完后不马上封箱先在跑道上推着走一圈看四路传感器在压线、离线、过弯时是否都能可靠翻转。如果某一路反应慢就单独微调那个模块的电位器别只调一路就默认全车都好用。4.3 速度与采样周期的匹配代码刚跑通的时候小车在直线段表现还可以一进弯道就往外飘。后来排查发现是车速太快而主循环每执行一次循迹判断的时间相对固定等于传感器采样频率不够。车速上去后同样的采样周期对应更长的行驶距离弯道里车已经冲出一截才执行转向自然就飘。我做了两个调整一是把基础PWM占空比从70%降到50%左右二是压缩测距函数里不必要的延时。最终测下来车速大约0.3m/s时稳定性最好弯道也能顺畅通过。这个数值不一定是标准答案跟你用的轮径、电机减速比、底盘重量都有关系但调试思路是一致的先降速跑通再逐步提速找极限。4.4 电源波动引发的传感器误触发有个现象很有代表性电池电压充足时小车跑得很好跑一会儿电压下降超声波测距值就开始跳变偶尔还会误判成“前方有障碍”然后原地打转。用万用表测模块供电脚发现电压跌落明显。原因是超声波模块和电机驱动共用一组电源电机启动瞬间电流很大把电源电压拉低了几百毫伏。单片机还能工作但超声波模块对电压波动比较敏感于是产生误触发。解决办法是把电机电源和控制电源分开布线或者至少加大容量电解电容做储能滤波。我用的是一个470uF电解电容并联一个100nF陶瓷电容并尽量靠近电源输入端放置。电容加上之后电机启动时电压跌落幅度明显减小误触发基本消失。5. 项目收尾复盘这套平台还能延展出什么5.1 低成本可扩展的功能模块这套小车的底层平台其实是一个标准的移动机器人底盘传感器和控制逻辑都可以继续扩展。循迹传感器可以换成灰度传感器阵列识别灰度和颜色实现更精确的线检测超声波可以并排加两个用两端测距值差来判断障碍物偏左还是偏右通信方面串口预留出来后续接蓝牙模块或无线透传模块就非常方便手机远程遥控只需要改一个模式状态。算力方面F103C8T6的64KB Flash在跑完当前逻辑后还有不少余量。我个人的建议是如果加OLED显示和状态机日志辅助调参会非常舒服跑起来也不会占用太多资源。5.2 对新手做同类项目的几点建议第一不要并行推进硬件和软件。先把底盘拼好确认电机能转、传感器能读到值再开始写逻辑。第二调参时一次只调一个变量比如先把循迹跑顺再上避障上来就同时调速度和阈值出了问题完全分不清是哪个环节。第三务必留串口日志。我在调试时通过串口把传感器状态、mode变量、PWM占空比实时输出很多偶发问题都是靠日志定位的单靠看小车跑姿猜原因太难了。把“完成”标在项目名里对我来说更像是一个阶段句号。这版到此为止但下一版随时可以从现在这辆车上长出来。希望这篇记录对正在做同款小车或者正准备入坑STM32小车项目的朋友能有一点实际的参考价值。本文还有配套的精品资源点击获取