做循迹避障小车这件事我前前后后折腾了大半个月最后用STM32F103C8T6最小系统板从零搭了一台能跑、能躲、能沿黑线走的完整小车。整个过程踩了不少坑也总结了一套非常稳定的方案现在把它整理成上下两篇上篇先把第1~3部分讲透——也就是项目整体设计、硬件选型与材料清单、最小系统板原理与引脚规划。下篇再讲循迹避障控制逻辑、CubeMX初始化、代码调试和整机联调。这篇上篇适合刚入门STM32、想动手做一个完整项目的同学也适合手里有一块STM32F103C8T6最小系统板但不知道怎么把外设串起来的人。1. 项目整体设计与方案选型1.1 循迹避障小车要解决什么问题所谓循迹避障就是让小车在已知的赛道上沿黑线行驶同时能探测前方障碍物并做出避开动作。听起来很简单但拆开来看它其实是一个典型的嵌入式系统闭环案例包含三个核心环节感知、决策、执行。感知层要回答“我现在在哪、前面有没有东西”这个问题。循迹用红外反射式传感器检测地面黑线的位置避障用超声波模块测量前方障碍物的距离。决策层则是主控芯片根据传感器数据判断小车的下一步动作直行、左转、右转、后退还是停车。执行层用电机驱动模块控制两个直流减速电机的转速和方向让小车实际动起来。这个项目最大的价值在于它把所有嵌入式基础知识点串了起来GPIO读写、定时器PWM输出、外部中断或输入捕获、ADC采样、状态机编程、串口调试。做完之后你会发现自己从一个只会点灯的初学者变成了能独立设计一个小型自动控制系统的人。所以我一直跟身边人说循迹避障小车是嵌入式入门的最佳实战项目没有之一。1.2 系统架构与模块化分工在动手之前我先画了一张系统框图把所有模块之间的关系理清楚。小车整体上可以分为四个部分感知层TCRT5000循迹传感器模块4路、HC-SR04超声波测距模块。决策层STM32F103C8T6最小系统板负责采集信号、运行控制逻辑、输出PWM。执行层TB6612FNG电机驱动模块驱动两个带减速箱的TT电机。供电层7.4V锂电池组经过稳压模块降压为5V和3.3V分别给电机驱动、传感器和主控供电。感知层把信号传给决策层决策层根据预设的循迹和避障逻辑运算之后输出两路PWM信号控制左、右电机的转速差实现转向。整个系统是典型的“采集-处理-输出”闭环结构。我在设计时特意做了模块化而不是把所有电路焊在一块板子上。原因很简单模块化方便调试和更换。比如循迹传感器坏了直接拔下来换一个新的就行不用动主控和驱动。对于新手来说模块化可以大幅降低排查故障的难度哪个模块工作异常一目了然。1.3 为什么选STM32F103C8T6而不是51或者Arduino很多人在选主控的时候会纠结51单片机便宜、资料多Arduino上手快为什么要选STM32F103C8T6我的理由很直接51单片机的性能上限太低主频只有12MHz左右8位处理能力跑不了复杂的算法而且外设资源匮乏想做PWM调速和超声波测距必须依赖定时器频繁中断代码写起来非常憋屈。Arduino虽然上手简单但封装层次太高很多引脚细节和寄存器背后的原理被隐藏了对理解底层帮助不大。STM32F103C8T6是意法半导体推出的Cortex-M3内核芯片主频72MHz64KB Flash20KB SRAMLQFP48封装。最关键是它内部集成了丰富的外设多个定时器、ADC、USART、I2C、SPI、CAN还能通过PWM通道直接驱动电机调速性能对这个小车来说绰绰有余。而且现在这块芯片的价格已经被打到几块钱学习成本非常低。另外要提一句“国产替代”。近几年GD32E103、APM32F103、MM32F103这些国产兼容芯片在市场上非常活跃引脚定义和寄存器和ST原厂基本一致很多项目直接替换就能跑。对于学生和业余爱好者来说这是个好消息意味着芯片供应链不再被单一厂商卡死。我后面在第三部分会单独讲国产替代和加密相关的内容这里先留个悬念。2. 硬件选型与材料清单2.1 传感器选型循迹与避障核心模块循迹传感器我选的是TCRT5000红外反射式传感器模块。它的工作原理很简单模块上有一个红外发射管和一个红外接收管发射管持续发射红外光接收管负责接收地面反射回来的光。当小车在黑线上方时黑色表面吸收大部分红外光接收管接收到的反射光弱模块输出高电平当小车在白色地面时反射光强输出低电平。模块上还有一个比较器芯片和电位器可以调节触发阈值。这里需要注意一个点TCRT5000模块有两种输出形式一种是数字量输出已经经过比较器另一种是模拟量输出。我强烈建议买数字量输出的版本因为模拟量输出需要主控额外接ADC引脚做阈值判断浪费引脚资源不说还会引入电源噪声干扰。数字量版本直接接GPIO就能用调节电位器就能适应不同环境光线。避障模块选的是HC-SR04超声波测距模块这应该是市面上最流行、最便宜的一种测距方案了。它有一个Trig引脚和一个Echo引脚。使用时主控先给Trig一个10微秒以上的高电平脉冲模块内部会发射8个40kHz的超声波脉冲然后Echo引脚会输出一个高电平高电平持续时间就是声波从发射到返回的时间。距离的计算公式是距离(cm) Echo高电平时间(us) ÷ 58或者用更精确的公式距离 时间 × 0.034 / 2。后面代码部分我会详细讲这个计算过程。HC-SR04的测量范围是2cm到400cm精度大概在3mm左右对小车的避障需求来说完全够用。它最大的优势是便宜、简单、抗干扰能力强不需要像红外测距那样根据环境光做复杂校准。2.2 电机驱动与减速电机选型小车的动力部分我选了两个带减速箱的TT电机配橡胶轮使用。TT电机本身转速很高但扭矩很小加了塑料减速箱之后转速降下来扭矩增大才能推动小车跑直线和爬坡。我选的是1:48减速比的版本实测在10V电压下转速大约280转/分钟这个速度对循迹小车来说刚刚好太快会导致传感器采样跟不上、转向过冲。电机驱动模块是很多人容易忽略的地方。市面上最常见的驱动方案有两个L298N模块和TB6612FNG模块。我一开始用的是L298N但后来换成了TB6612FNG原因很简单L298N用的是三极管H桥方案导通压降很大接近2V电源利用率低而且模块自身体积大、发热严重7.4V电源经过它之后实际到电机上的电压可能只剩5V多小车跑起来软弱无力。TB6612FNG用的是MOSFET H桥导通电阻只有零点几欧姆发热小PWM频率支持高很多体积和重量也更小。我给自己总结了一套电驱选型口诀预算充足就买TB6612FNG追求极限性能用DRV8833L298N只适合当教学演示用。这一点在后面的材料清单里也会再次提到。供电方案我用了一节7.4V 2000mAh的锂电池组通过一个LM2596降压模块稳压到5V给电机驱动逻辑部分和传感器供电再从5V经过AMS1117-3.3降到3.3V给STM32最小系统板供电。这里有一个很关键的经验电机启动瞬间电流很大如果主控和传感器直接从同一个电源取电会导致电压跌落严重轻则传感器误触发重则主控复位。我的解决办法是把电源分成两路一路是动力电电池直供电机驱动另一路是逻辑电经过稳压给主控和传感器中间用共地接法保持参考电位一致。2.3 完整材料清单与采购避坑经验我把我实际采购的材料整理成了一份清单按模块分类列出来方便大家直接对照下单。模块型号/规格数量参考预算备注主控STM32F103C8T6最小系统板110-20元带SWD调试接口循迹传感器TCRT5000模块数字量43-5元/个建议买4路带支架的套件超声波模块HC-SR0415-8元注意供电是5V电机驱动TB6612FNG模块18-15元代替L298N直流电机TT马达 1:48减速箱25-8元/个带橡胶轮胎更好小车底盘亚克力三层板或铝合金底盘115-40元带电池仓电池7.4V 18650锂电池组120-40元带保护板稳压模块LM2596降压模块13-8元调到5V输出3.3V稳压AMS1117-3.3模块11-3元也可直接买5V/3.3V双输出调试器ST-Link V2 或 DAPLink110-20元DAPLink性价比高杜邦线母对母、公对母若干若干3-5元买质量好一点的螺丝铜柱扎带配套若干5元左右固定用这里我要单独说一下杜邦线的坑。我买的第一次杜邦线线芯很细外皮看着正常一接上去就出现接触不良的情况小车一个转向就断信号花了我一个下午排查最后才发现是线的问题。建议买线芯是22AWG或者纯铜的杜邦线或者干脆买一盒彩色硅胶线自己压端子可靠性高很多。还有一个容易踩的坑是超声波模块的供电。HC-SR04的VCC引脚标称需要5V供电但它输出的Echo高电平也是5V直接接到STM32的3.3V GPIO上长期运行可能损坏芯片引脚。解决办法有两个一是用两个电阻做分压比如1kΩ和2kΩ串联把Echo降到3.3V二是买一个电平转换模块或者干脆选那些已内置电平转换的超声波模块版本。实测下来分压是最省钱最稳定的方案。3. 最小系统板原理与引脚规划3.1 最小系统板三大关键电路STM32F103C8T6最小系统板是很多人的第一块STM32开发板但真正把它看懂的人并不多。所谓“最小系统”就是让芯片能正常工作的最简外围电路。我拆成三个部分来讲。第一是供电电路。芯片有多个VDD/VSS引脚需要统一接3.3V和地。VBAT引脚接3.3V用于给备份域RTC供电不需要后备电池时直接连3.3V即可VDDA引脚必须接3.3V且需要并联一个小容量的去耦电容滤除高频噪声。最小系统板上一般已经集成了AMS1117-3.3稳压芯片可以直接从USB的5V取电降压但要注意它的最大稳定输出电流只有1A如果同时给很多外设供电板子会发烫电压会跌落。所以我更建议用前面的双路供电方案让最小系统板只负责给自身和少量逻辑模块供电。第二是时钟电路。STM32F103C8T6内部有一个RC振荡器但精度不够且频率只有8MHz不能满足定时器PWM和串口通信对时钟稳定性的要求所以必须外接一个8MHz的石英晶振。晶振两端各接一个20pF左右的负载电容连到OSC_IN和OSC_OUT引脚。芯片内部通过PLL锁相环把8MHz倍频到最高72MHz这个倍频系数在CubeMX的时钟树里配置后面我会详细说明。如果晶振虚焊或者电容选错会出现的一个典型症状是芯片下载程序后不运行或者串口输出乱码。第三是复位与启动模式电路。NRST引脚连接一个RC复位电路上电瞬间NRST被拉低约几十毫秒然后电容充电到高电平完成复位。BOOT0引脚一般通过10k电阻下拉到GND让芯片从用户Flash启动。BOOT1引脚悬空或下拉都可以。如果BOOT0不小心被拉高了芯片会进入ISP下载模式表现就是正常上电后程序不跑这在新手里非常常见。3.2 引脚功能分配与接线表STM32F103C8T6共有48个引脚其中可用GPIO大约37个对这个项目来说绰绰有余。我给你列出关键引脚的功能分布方便之后查数据手册和管脚图参考。引脚名引脚号类型默认功能本项目用途PA011ADC/GPIOPA0/WKUP循迹传感器1路PA112ADC/GPIOPA1循迹传感器2路PA213USART2_TXPA2循迹传感器3路PA314USART2_RXPA3循迹传感器4路PA415SPI1_NSSPA4超声波TrigPA516SPI1_SCKPA5超声波EchoPA617SPI1_MISO/定时器PA6左电机PWMPA718SPI1_MOSI/定时器PA7右电机PWMPA930USART1_TXPA9串口调试TXPA1031USART1_RXPA10串口调试RXPB027ADC/GPIOPB0左电机方向IN1PB128ADC/GPIOPB1左电机方向IN2PB1044I2C2_SCLPB10右电机方向IN3PB1145I2C2_SDAPB11右电机方向IN4PA1334SWDIOPA13SWD调试PA1437SWCLKPA14SWD调试PC132GPIOPC13板载LED接线时有一个细节循迹传感器的输出引脚我特意选在了PA0、PA1、PA2、PA3因为后续如果用FreeRTOS实时系统或者DMA进行多路传感器采样这几个引脚可以灵活配置为外部中断响应速度有保证。另外PA2、PA3虽然默认复用为USART2但只要不开启USART2外设把它们当普通GPIO用完全没问题我在CubeMX里会做具体配置。还有一点STM32F103的引脚并不全是5V容忍的。像PA0、PA1这些ADC输入引脚就是模拟引脚只能承受3.3V电压。超声波模块Echo输出的5V电平如果直接接PA5长时间运行有烧引脚的风险。所以我在前面强调的分压方案一定要接好。3.3 国产替代、加密与调试器选择现在市面上流通的STM32F103C8T6芯片有很大一部分其实是国产替代型号。我之前用GD32E103C8T6替换过ST原厂芯片引脚定义完全兼容代码直接用ST标准库编译烧录就能跑。不过有两个细节需要注意第一GD32的USART波特率误差比ST原厂大使用115200以上波特率时建议实测一下第二GD32的部分芯片主频可以跑到108MHz但按ST的72MHz配置运行完全没问题。关于“STM32F103C8T6加密”这个问题很多做产品的工程师非常关心。芯片加密指的是设置读保护级别RDP防止程序被通过调试接口读出。操作方法是在ST-Link工具或CubeProgrammer里选择Level 1级别的读保护。设置之后SWD接口的调试和读取功能会被禁止只有全片擦除才能解除。对学习项目来说一般不推荐设置加密因为一不小心就把芯片锁死了还得用串口ISP方式做全片擦除才能恢复。调试器方面我推荐DAPLink而不是ST-Link。DAPLink的核心优势是便宜十几块钱就能拿下而且协议开源支持Windows/Linux/Mac全平台免驱配合Keil或IAR都很稳定。ST-Link V2虽然性能更好但市面上仿品太多经常出现固件丢失的问题需要频繁重刷反而耽误时间。我在调试这台小车的时候用的是一个十几元的DAPLink下载速度虽然不如ST-Link但胜在稳定不折腾。4. 循迹避障控制原理与关键参数4.1 循迹原理与阈值标定循迹控制的核心是理解TCRT5000的输出特性。前面说过传感器检测到黑线时输出高电平检测到白地时输出低电平。但实际情况要复杂一些环境光不同、地面材质不同、传感器安装高度不同都会影响反射强度。所以每一路传感器在执行任务之前都必须做阈值标定。标定方法很简单把小车放在白色地面上用螺丝刀旋转模块上的电位器直到板上指示灯刚好熄灭再把传感器移到黑线上方指示灯应该亮起。重复几次找到黑白两种状态之间最明显的切换点。这里有一个技巧不要追求黑白状态都完美而是保证在白底低电平和黑线高电平之间有足够的电压余量。我实测发现TCRT5000的最佳安装高度是距离地面1.5cm到2.5cm之间太高会导致反射强度不够阈值很难调太低又容易撞到地面凸起。4路循迹传感器在车头呈一字排开从左到右依次为S1到S4。我采用的循迹策略是传感器状态S1-S4小车状态控制策略0001偏右左转0011略偏右轻微左转0110中间直行1100略偏左轻微右转1000偏左右转1111掉线/十字直行或急转0000离线保持上一状态或停车这个表其实就是一个最原始的决策查找表。如果追求更高的循迹效果可以把传感器换成模拟量输出的版本用ADC读取每个传感器返回的灰阶值再通过PID控制器调节转向力度。但对于入门项目来说数字量查找表已经完全够用了而且逻辑清晰方便调试。4.2 超声波避障与波门设置HC-SR04的时序看起来简单实际用起来有几个容易忽略的细节。先说标准流程主控给Trig引脚一个至少10微秒的高电平脉冲模块自动发出8个40kHz超声波脉冲并等待回波当检测到回波后Echo引脚输出高电平高电平持续的时间就是声波往返的时间。距离计算公式有两种写法距离(cm) (Echo高电平时间(us) / 58)距离(cm) (Echo高电平时间(us) × 0.034) / 2第二种其实更好理解声波在空气中的传播速度约为340m/s也就是0.034cm/微秒乘上时间再除以2往返就是单程距离。举个具体的例子如果Echo高电平时间是1000微秒那么距离 1000 × 0.034 / 2 17cm。在程序中Echo引脚有两种读取方式。第一种是用GPIO轮询发送Trig脉冲后用一个while循环不停地读取Echo引脚的电平直到它从高变低然后用定时器的计数值计算时间差。这种方式简单但会阻塞CPU在做多任务处理时比较被动。第二种是用定时器输入捕获把Echo引脚连接到定时器的输入捕获通道利用硬件自动记录上升沿和下降沿的计数值精度更高CPU不需要空转等待。我在这台小车里先用GPIO轮询方式把功能跑通验证没问题之后再改成输入捕获优化建议新手也按这个顺序来。所谓“波门设置”就是设置避障动作的触发阈值。我设为25厘米也就是说当超声波检测到前方25厘米内有障碍物时小车进入避障模式。波门值不是越大越好也不是越小越好设太大小车会频繁转向导致循迹路线混乱设太小等发现障碍物时已经来不及刹车了。25厘米对这台小车的车速来说是一个比较安全的距离按我前面选的电机的转速从检测到障碍到机械结构执行转向留给系统的反应时间大约有0.5秒足够完成整个避障动作。4.3 控制策略与状态机设计小车的控制逻辑我用了一个简单的状态机来实现这是嵌入式编程里非常实用的一种设计模式。状态机的基本思想是把小车的行为划分为若干个离散状态每个状态对应一种处理逻辑状态之间通过事件触发进行转移。我的小车核心状态定义如下状态STATE_TRACK循迹模式按照循迹查找表执行直行或转向。状态STATE_AVOID_STOP检测到障碍物立即停车并判断障碍物方位。状态STATE_AVOID_TURN执行避障转向动作原地旋转或边转边前进。状态STATE_BACK_TRACK绕过障碍后重新搜索黑线恢复到循迹模式。在循迹模式下超声波传感器每50毫秒进行一次测距测距结果只在距离小于25厘米时才会触发状态切换否则不干预循迹逻辑。为什么要这样做因为超声波测距本身需要大约30毫秒的处理时间如果每次测距都影响到循迹PWM输出小车跑起来会一顿一顿的。把两种控制逻辑解耦循迹用快速查找表处理避障用慢速轮询处理整个系统的响应速度和控制稳定性都能兼顾。避障转向的细节检测到障碍物时我先让小车原地停车100毫秒同时读取超声波回波判断障碍物在正前方还是偏某一边。如果偏左就右转绕过如果偏右就左转绕过如果正前方有障碍而两侧没有明显差异则统一右转绕过相当于一个统一的避让方向。绕过之后小车继续前进一小段距离然后开始左右搜索黑线找到黑线后重新进入循迹状态。5. 开发环境搭建与CubeMX初始化5.1 Keil与IAR开发环境怎么选编译环境的选型是很多新手遇到的第一个拦路虎。ST官方主推的开发工具是Keil MDK和IAR EWARM两者我都在实际项目中用过它们的核心功能没有本质区别选哪个主要看你的使用习惯和团队协作要求。Keil MDK的优点是中文资料多教程一搜一大把报错信息网上基本都能查到插件和烧录工具兼容性也好。缺点是对代码的优化相对保守生成的目标文件体积稍大。IAR的优点是代码优化能力强编译速度快调试时能看的寄存器信息更详细。缺点是界面操作方式和Keil完全不同很多从Keil转过来的初学者会不适应。我的建议是如果你没有特殊要求就用Keil MDK5。因为它跟STM32CubeMX的配合最方便下载算法、烧录脚本、调试参数等配置很多都是自动完成的对新手最友好。下载时记得选对应芯片的Device Pack不然打开工程会报“device not found”的错误。另外强调一下在Keil里选择调试器时要选中DAPLink对应的CMSIS-DAP选项然后再设置一下下载目标文件的算法具体步骤我在后面小节说明。5.2 CubeMX配置步骤与常见坑位STM32CubeMX是ST官方提供的图形化初始化代码生成工具它最大的好处是可视化和自动化——你只需要在图形界面里勾选外设和引脚它会自动生成初始化代码解决了大部分寄存器配置的烦恼。配置循迹避障小车的步骤可以概括为六步第一步新建工程在芯片搜索框输入“STM32F103C8”选择LQFP48封装的STM32F103C8Tx确认后进入配置界面。第二步配置RCC。在System Core → RCC页面中把HSE设为“Crystal/Ceramic Resonator”。如果不设置HSE芯片只会用内部RC振荡器跑在8MHz定时器PWM频率和串口波特率都会不准。同时在SYS页面里把Debug设为“Serial Wire”这样才能启用SWD引脚否则芯片一次烧录之后SWD接口被禁用下次下载就要用复位拉低等方式才能连上很麻烦。第三步配置GPIO。按引脚分配表把PA0到PA5设为GPIO_Input模式循迹传感器和超声波EchoPA6和PA7设为TIM3的PWM输出通道PB0、PB1、PB10、PB11设为GPIO_Output模式电机方向控制。这里有一个小技巧在CubeMX的引脚图上直接单击对应的引脚就可以选择功能模式不用像写代码那样一个个找寄存器。第四步配置时钟树。进入Clock Configuration页面把HCLK设为72MHz。系统会自动计算PLL倍频系数从8MHz外部晶振倍频到72MHz。如果你用的是片内RC最大只能到64MHz而且精度不稳定。第五步配置定时器。选择TIM3把Channel1和Channel2设为PWM Generation预分频(Prescaler)设为72-1自动重载值(AutoReload)设为1000-1最终PWM频率72MHz÷72÷10001kHz。这个频率对电机驱动来说足够了。在PWM配置中还可以直接设置占空比初始值建议先设为0防止上电瞬间电机全速转。第六步生成工程。Project Manager页面里选择工具链为MDK-ARM V5生成代码后直接用Keil打开。生成的代码默认使用HAL库结构清晰main函数里的while(1)循环和各个外设的初始化函数都已经帮你写好了。配置过程中最容易犯的错是忘记给GPIO设置上下拉。循迹传感器输出的是推挽信号GPIO设置为Input模式即可不需要上下拉。但超声波Echo引脚建议设为下拉输入防止引脚悬空时出现随机高电平误触发测距。5.3 生成代码后必须做的三件事CubeMX生成的代码只是一个“空壳”离能跑的小车还差三步。第一件事是验证时钟是否真正跑在72MHz。打开Debug配置全速运行代码然后在调试器的Peripheral Registers窗口里查看RCC状态寄存器确认PLL已锁定且系统时钟源为PLL。或者更简单的办法配置一个GPIO输出1Hz方波用示波器测频率。如果没有示波器可以用逻辑分析仪或者干脆偷懒一点——先把LED闪烁程序烧进去如果闪烁频率看起来正常一般说明时钟没问题。第二件事是配置LED心跳。最小系统板上通常都会有一颗PC13引脚的LED我在while(1)循环里让LED以500毫秒周期闪烁这样每次上电之后只需要看一眼LED就能判断程序是否在正常运行排查故障时非常直观。如果没有LED心跳芯片跑飞了、死循环了、时钟没起振你根本无从感知。第三件事是实现超声波测距的测试代码。先用GPIO轮询方式把HC-SR04跑通测出来的距离值通过串口打印到电脑上。串口配置为115200、8N1用USB转TTL模块连接PA9和PA10。这一步至关重要不先把单模块测试通过直接接上小车组装联调出了问题很难定位是传感器故障还是接线问题。我最开始就是偷懒跳过了单模块测试结果整车跑的时候一会儿左偏一会儿右偏花了大半天才发现是超声波模块的供电线接触不良。到这里上篇的内容就讲完了。硬件方案、材料清单、最小系统板原理、引脚规划、控制原理、环境搭建都已经准备就绪。我个人在实际操作中发现做这个项目最忌讳的就是“急着把车跑起来”老老实实把每一个模块单独测试通过再组装联调整体进度反而最快。下篇我会接着讲循迹避障状态机的完整代码实现、中断与PWM调试技巧、整机联调与参数整定以及FreeRTOS移植到STM32F103C8T6的扩展玩法到时候见。
阅读完成 · 觉得有帮助?