STM32循迹避障小车实战(上):从架构规划到硬件组装调试

发布时间:2026/10/5 3:57:40
STM32循迹避障小车实战(上):从架构规划到硬件组装调试 做循迹避障小车这件事我从大一折腾到现在帮朋友做过不止五六台踩过的坑比走过的路都多。最近总有人在群里问基于STM32F103C8T6的循迹避障小车怎么做很多人一上来就急着写代码结果没两天就跑来问我“车为什么不动”“为什么不循迹”。其实绝大多数问题都不是代码问题而是动手之前没说清楚的规划、选型、接线问题。所以这次我打算把这个项目从头到尾拆成一个系列写出来本篇是上篇覆盖整个项目的第1、2、3点功能边界与总体架构怎么定、核心硬件选型的取舍逻辑、开发环境搭建与硬件组装调试。把这三个点吃透后面写代码、调车会顺利非常多。1. 项目总体架构先明确功能边界再谈元器件1.1 循迹和避障的本质需求循迹小车要解决的根本问题只有一个车怎么沿着地面上的黑线走。实际工程里用的传感器是TCRT5000这类红外反射式光电传感器模块上的发射管发出红外光接收管检测反射回来的光强黑色吸光、白色反光强所以传感器遇到黑线和白线时输出电平会不一样。STM32F103C8T6读这几个IO口的高低电平就知道车是偏左了还是偏右了然后控制左右两个电机做差速把车拉回线上。这个过程看着简单但真要把车调得又稳又不蛇形里面有不少细节。避障要解决的问题更直接行驶路径上有障碍物怎么办。主流方案是HC-SR04超声波模块通过TRIG引脚给一个10微秒以上的高电平触发信号模块内部发出8个40kHz的超声波脉冲碰到障碍物反射回来ECHO引脚会输出一个高电平高电平持续时间就是声波往返的时间。用这个时间乘以声速再除以2就能算出距离。STM32读出距离后小于设定阈值就停车、转弯或者绕行。所以这两个功能的本质都不复杂读电平、算时间、算距离然后控制电机。真正的难点在于两者同时工作时怎么定优先级转弯逻辑怎么处理以及硬件层面能不能稳定供电、稳定采数。不少新手把循迹和避障写成了两个互不相关的代码块结果车到了路口又避障又循迹左右打架这就是架构没想清楚。1.2 为什么主控选STM32F103C8T6而不是51或Arduino很多新手会问做个循迹避障小车用51单片机或者Arduino Nano不也行吗确实行同类方案网上遍地都是但我个人推荐STM32F103C8T6理由非常实际。先看性能。STM32F103C8T6是Cortex-M3内核主频72MHz内置64KB Flash、20KB SRAMLQFP48封装下有37个可用GPIO。对循迹避障这个任务来说这个性能富余得有点夸张但富余不是浪费——它意味着你还可以同时驱动OLED显示、蓝牙遥控、编码器测速、甚至跑FreeRTOS做多任务调度而不会捉襟见肘。51单片机的8位性能虽然也能完成基础功能但一旦想加功能往往要推倒重来。再看外设配置。F103C8T6带了3个USART、2个SPI、2个I2C、1个12位ADC、4个定时器其中TIM1还是高级定时器另外还有USB 2.0全速设备接口。电机PWM、超声波计时、串口调试、传感器采集这些外设正好全覆盖不需要额外扩展芯片。对做小车来说这颗芯片几乎是为你量身定做的。最后是生态和成本。目前某宝上一块STM32F103C8T6最小系统板的价格通常在10元上下资料极其丰富Keil、IAR、STM32CubeMX、标准外设库、HAL库都是成熟方案。更重要的是以后你做更复杂的四轴、平衡小车、机械臂这块芯片和这套工具链照样能复用学习周期不会白费。我做了张对比表方便你判断维度51单片机Arduino NanoSTM32F103C8T6主频12MHz左右16MHz72MHz位宽8位AVR 8位Cortex-M3 32位定时器资源2~3个3个4个含高级定时器通信接口UART为主UART/I2C/SPIUSART×3/SPI×2/I2C×2/USB开发方式Keil C51寄存器为主Arduino IDE库函数Keil/IAR 标准库/HAL可扩展性较弱一般很强可上FreeRTOS、LVGL等1.3 预留可扩展能力别把引脚一次性用光这是我吃过亏以后养成的习惯画引脚规划表的时候先留出几个“机动引脚”。这个小车最基础的功能需要3个循迹传感器输入、2路超声波引脚、2路PWM、4路电机方向引脚再加1路串口调试总共大约12个GPIO。F103C8T6有37个可用IO剩得还很多。建议至少留出I2C引脚和USART2引脚给后续的OLED显示屏、蓝牙模块、MPU6050这类扩展模块用。如果你想加编码器测速做闭环定时器2/3/4的正交解码功能也要提前规划好。宁可现在空着也不要以后飞线飞得怀疑人生。我把自己的引脚规划表直接贴出来功能模块引脚备注左循迹传感器PB0输入中循迹传感器PB1输入右循迹传感器PB2输入注意PB2是BOOT1引脚超声波TRIGPB3输出超声波ECHOPB4输入带FT容忍标志左电机PWMPA0TIM2_CH1右电机PWMPA1TIM2_CH2左电机方向1PA2TB6612 AIN1左电机方向2PA3TB6612 AIN2右电机方向1PA4TB6612 BIN1右电机方向2PA5TB6612 BIN2串口调试TXPA9USART1_TX串口调试RXPA10USART1_RX预留PA6/PA7/PB5/PB6/PB7/PB10/PB11OLED/蓝牙/编码器这里涉及一个很多新手会忽略的坑PB2、PB3、PB4在F103上的默认复用功能分别是BOOT1、JTDO、NJTRST。如果你买的板子把BOOT1引到了某个跳线帽或者JTAG接口占用了PB3/PB4这些引脚就不能随便当普通IO用。遇到这种情况要么换别的引脚要么在代码里禁用JTAG、只开SWD。我后面讲CubeMX配置时会专门说这个。2. 硬件选型逐个说主控板、电机驱动、传感器、电池2.1 最小系统板怎么选国产替代能不能直接用刚才已经定了主控选STM32F103C8T6但买的时候你会发现市面上除了ST原厂还有GD32、APM32、MM32等一大堆国产替代型号外形一样、引脚兼容价格更便宜。这就是大家常说的“国产替代”话题。坦白讲对于这款小车项目国产替代完全能用很多博主用GD32F103C8T6直接烧录ST的工程也跑得很稳。但我个人建议如果你是学习、调试、查资料优先选ST原厂芯片的最小系统板别为省几块钱给自己添堵。原因有三个第一原厂数据手册、勘误表、应用笔记最全遇到问题查起来方便第二CubeMX生成的HAL库和底层配置默认按ST芯片来GD32虽然大体兼容但在RCC时钟树、复位时序、Flash等待周期这些细节上还是有差异新手遇到不明问题很难判断是代码问题还是芯片兼容问题第三用ST-Link或DAPLink调试时原厂芯片的识别和调试体验更顺。等项目吃透了再用国产替代做产品降本那时候你已经有足够的能力去排障。购买时还要注意两点。一是看板子上的晶振正规的最小系统板一般带8MHz外部晶振但也有些超简版只靠内部RC振荡器这种板子用CubeMX配HSE会直接死机必须改用HSI输入。二是看有没有板载USB转串口芯片常见的是CH340或CP2102有的话调试非常方便下载程序时也不需要额外接USB转TTL模块。2.2 电机驱动L298N和TB6612FNG的取舍电机驱动模块是动力核心。市面上最常见的两种方案是L298N和TB6612FNG很多新手在这里纠结半天其实两张表就能看明白项目L298N模块TB6612FNG模块电机电压范围4.5~36V直接用电池电压VM电机电源最高约15V典型5~12V逻辑供电板载5V稳压器可给单片机供电需要独立VCC逻辑电源2.7~5.5V最大连续电流约2A实际超过1A发热明显约1.2A导通压降约2V大电流下发热严重MOSFET压降低发热小控制方式IN1/IN2/ENA加PWM调速AIN1/AIN2/PWMA加PWM调速模块体积大小适合场景大电机、高压电池、实验教学智能车、小型TT马达、低压电池如果你用的是常见的TT马达和减速电机工作电压3~6V电流几百毫安那TB6612FNG是更合适的选择。压降低意味着电池能量更多地转化为驱动力模块体积小也方便安装。L298N的优势是输入电压范围宽、板载5V输出可以给逻辑电路供电但那个2V左右的压降在6V电池供电系统里太伤了电机实际工作电压只剩下4V爬坡和加速都很吃力。我最终选的是TB6612FNG。接线时特别强调一点TB6612上的VCC是逻辑电源接3.3V或5V都行VM才是电机电源接电池正极。两个电源都要和STM32的GND共地否则PWM波形参考电位不一致电机会乱转。2.3 循迹传感器与超声波模块的选型细节循迹传感器我推荐三路TCRT5000模块可以是三个独立小模块也可以是一体的三路模块。为什么是三个不是两个两个探头只能判断黑白跳变车在线上小幅蛇形时反馈不够敏感三个探头左、中、右可以明确判断“中探头在线上”“左探头压线”“右探头压线”三种状态让控制程序有更细的决策空间。五路当然更精细但入门阶段三路足够把逻辑跑通。TCRT5000模块上有一个灵敏度调节电位器用螺丝刀旋转可以改变比较器阈值。调灵敏度是个手感活这里给一个通用调法把探头放在白色地面上观察模块指示灯状态再放到黑线上确认指示灯状态发生明显变化调整电位器让黑白两种地面下输出电平有清晰跳变。模块离地高度控制在1到1.5厘米太高红外光散射严重容易误判。超声波模块几乎只有HC-SR04一个性价比之选几块钱一个测距范围2到450厘米误差在厘米级对小车避障完全够用。要注意它的工作电压标称5V但不少模块3.3V也能跑。ECHO返回值是5V电平接到STM32扩展引脚之前最好确认目标引脚是不是5V容忍引脚数据手册里标FT或者加个分压电阻。我在引脚规划里把ECHO放在PB4查手册确认它带FT标志才敢直接接。2.4 供电与电平新手最容易翻车的两个环节供电设计是这个项目里最容易被低估的部分。很多小车用4节干电池6V给电机和单片机一起供电结果一上电单片机就重启或者电机一转屏幕就花。原因不难理解电机启动瞬间的电流可能是正常运转的好几倍干电池内阻大负载一拉端电压瞬时跌到4V甚至更低。如果单片机用板载AMS1117-3.3从5V或6V稳压到3.3V输入电压一旦低于稳压器最低压差3.3V输出就跟着掉单片机自然复位。我的建议是动力电源与控制电源分开。用两节18650锂电池串联约7.4V专门给电机供电接到TB6612的VM控制部分单独用5V/3.3V的降压模块或者从带多路输出的电源板取电。如果非要用单电池供电至少选择容量大、内阻低的锂电池并且确保稳压方案有余量。电平匹配同样关键。STM32F103C8T6的IO是3.3V逻辑但很多传感器模块和电机驱动模块的逻辑供电可以接3.3V也可以接5V。最稳妥的做法是把所有模块的逻辑电平统一在3.3V传感器模块如果标称5V先看它有没有板载稳压和电平转换没有就要自己做分压或者换3.3V兼容版本。TB6612的VCC接3.3V时AIN/BIN和PWM输入也兼容单片机的3.3V信号不需要额外转换这也是我选TB6612的一个隐藏加分项。3. 开发环境搭建与工程初始化Keil加CubeMX一次配好3.1 Keil MDK与IAR选哪个下载调试器怎么配开发STM32的主流工具链有两套Keil MDK和IAR Embedded Workbench for ARM。两者都能编译、下载、调试选择更多是习惯问题。Keil在中文资料和教程数量上占绝对优势遇到问题搜解决方案基本都能搜到IAR的编译器优化更好、工程管理更规范但中文资料相对少。对做小车来说选Keil MDK就够用加上搜资料方便这个隐性优势新手不容易卡壳。下载调试器方面最便宜实用的是ST-Link V2或者DAPLink/CMSIS-DAP。前者是ST官方调试器稳定、兼容性好后者是开源方案同样支持Keil的CMSIS-DAP接口很多国产开发板直接板载了。我平时用DAPLink比较多体积小、速度够用部分版本还能顺便当USB转串口用。接线很简单SWD三根线SWDIO、SWCLK、GND再加一根VCC用于电平参考。某些DAPLink不接VCC也能工作但建议接上目标板电压才能被正确识别。3.2 用CubeMX完成最小系统配置时钟、GPIO、定时器、串口现在开发STM32F1的主流方式是先用STM32CubeMX图形化配置生成初始化代码再在生成的工程里填业务逻辑。这样做的好处是引脚冲突、时钟树错误这类低级问题CubeMX会直接帮你拦住。打开CubeMX芯片型号选STM32F103C8Tx然后按下面几步配置SYS选项里把Debug选成Serial Wire只保留SWD下载这样可以释放PB3、PB4这两个JTAG相关引脚你才能拿它们当普通IO用。RCC选项里把HSE选成Crystal/Ceramic Resonator表示使用外部8MHz晶振。时钟树里让HSE经过PLL倍频到72MHz。默认配置通常是HSE 8MHz作为PLL源倍频系数9主频72MHz。AHB、APB1、APB2的分频按默认即可APB1通常是36MHzAPB2是72MHz这个会直接影响后面定时器的时钟频率。GPIO配置3个循迹传感器引脚设成GPIO_Input超声波TRIG设成GPIO_OutputECHO设成GPIO_Input。如果后续想用外部中断做超声波也可以配成EXTI但入门用轮询就够了。定时器用TIM2输出两路PWMCH1和CH2频率建议10到20kHz。计算公式是TIM2时钟频率除以预分频PSC1再除以自动重载ARR1。比如PSC71、ARR99那么72MHz除以72再除以100等于10kHz。PWM初始占空比设0防止上电电机乱转。串口使能USART1波特率1152008位数据无校验1位停止位用于打印调试信息。这一步极其重要没有串口调试信息你后面排查问题会非常痛苦。代码生成时在Project Manager里填工程名和路径Toolchain选MDK-ARM固件包选F1系列HAL库版本。生成后用Keil打开编译一次确保零错误最小系统的固件框架就算搭好了。你在CubeMX里做的所有配置都会生成对应的初始化代码业务逻辑只需要往main.c的while(1)里填。3.3 工程目录划分与分层思路后面写代码不头疼CubeMX生成的工程默认把所有HAL库文件堆在Drivers目录里用户的代码写在main.c。如果逻辑简单直接往while(1)里塞代码也不是不行但后面想加OLED、蓝牙、FreeRTOS的时候这种写法会越来越乱改一个功能牵一发动全身。我的习惯是在工程里新增两个目录BSP板级支持包和App应用逻辑。BSP下面放bsp_motor.c、bsp_tracking.c、bsp_ultrasonic.c、bsp_uart.c每个文件对应一个硬件外设导出几个“动作类”函数比如Motor_SetSpeed(int left, int right)、Tracking_GetState(void)、Ultrasonic_GetDistance(void)。App下面放app_control.c负责把所有传感器信息汇总经过决策逻辑后调用BSP的函数。好处很直接硬件操作被封装在BSP层控制逻辑只依赖函数接口。以后把三路循迹换成五路只需要改bsp_tracking.cApp层完全不用动。这种分层思想在FreeRTOS多任务场景下尤其重要——一个任务管传感器采样一个任务管控制决策一个任务管数据显示互不干扰。虽然上篇还不需要写FreeRTOS但目录结构从一开始就按这个思路搭后面移植会非常顺。4. 硬件组装与接线实操从零件到能跑的车4.1 车体组装和电机安装别小看这一步的效率影响市面上的小车底盘主要有亚克力板、塑料件和3D打印件三种。亚克力底盘价格便宜、强度够用缺点是螺丝孔位要自己对照图纸装需要一点耐心。组装时先装电机再装轮子注意TT马达常见型号是JGA25-370有两根线接线颜色不统一常见是红色正极、白色或黑色负极。保险起见上电测试之前用万用表或者直接低压电池测一下转向避免两个电机方向装反——这是“车不走直线”最常见也最低级的原因。TT马达的减速比常见1:48转速适中做室内循迹的行驶速度正合适。安装电机时要注意齿轮箱和底盘固定孔是否对齐螺丝不要一开始就拧死等两个电机都装上确认轮子转动顺畅后再拧紧。轮子按压到输出轴上时要压到位否则行驶途中会掉轮子。这些机械细节虽然不涉及代码但直接影响后面的整车稳定性。4.2 完整接线表对照着接不用猜下面的接线表是我在实际项目中用的一组分配和前面的规划表格完全一致你直接用就行模块模块端接到STM32最小系统板说明左循迹模块VCC3.3V模块供电左循迹模块GNDGND共地左循迹模块OUTPB0数字量输出中循迹模块OUTPB1数字量输出右循迹模块OUTPB2数字量输出超声波模块VCC5VHC-SR04标称5V超声波模块GNDGND共地超声波模块TRIGPB3触发信号超声波模块ECHOPB4回波信号FT容忍引脚TB6612VCC3.3V逻辑电源TB6612GNDGND共地TB6612VM电池正极7.4V电机电源TB6612PWMAPA0左电机PWMTB6612AIN1PA2左电机方向1TB6612AIN2PA3左电机方向2TB6612PWMBPA1右电机PWMTB6612BIN1PA4右电机方向1TB6612BIN2PA5右电机方向2TB6612AO1/AO2左电机两根线左电机TB6612BO1/BO2右电机两根线右电机电池GND所有GND汇聚必须共地这张表的核心原则其实就一句话所有模块的GND必须连到一起否则信号就是悬空的单片机读到的电平毫无意义。我在群里帮人排查过太多次“传感器数据乱跳”“电机不转”的问题最后发现就是某个模块的GND没接。接线这一步宁可多花十分钟检查也不要等上电出问题再猜。4.3 传感器安装位置与机械布局机械布局直接决定传感器读数的稳定性。循迹探头安装位置的核心要求是三个探头横向排成一条直线间距要匹配赛道黑线宽度。普通电工胶带做黑线大约1.8到2厘米宽三路探头的左右间距建议控制在1厘米以内否则可能出现中间探头刚离开黑线、车已经偏了一大截的情况。具体操作是把车放上黑线让中间探头正对黑线观察左右探头是否刚好压在黑白交界附近再微调位置和灵敏度。超声波模块安装在车头正前方探头平面和车轴垂直高度要高于地面常见障碍物。注意探头不能被遮挡装太高又测不到低矮障碍物。如果用圆形亚克力底盘通常用双面胶或热熔胶固定在车头如果你打算加OLED屏幕屏幕尽量放后部或中央避免挡超声波。电池盒的位置我建议放在底盘中央偏后。原因很简单重心接近几何中心时车加减速比较平稳电池压在后轮附近能增加驱动轮抓地力减少急转弯时打滑。这些细节都是调车时一点一点试出来的写代码之前先把车做到“机械上对称”后面调逻辑会省很多事。4.4 上电前的最后检查清单与第一次点亮接完线别急着插电按下面这几步过一遍清单用万用表通断档测电池输出正负极确认没有短路。这一步虽然基础但真能挡住接反烧板子的事故。先只通3.3V控制电源检查最小系统板电源指示灯亮测量3.3V输出是否正常。用DAPLink或ST-Link连接SWD接口打开刚才CubeMX生成的工程编译下载一个最简单的GPIO翻转程序到板子里确认单片机本身工作正常。没有LED就用串口打印“hello”。再通电机电源依次测试左右电机正反转。写个小测试函数分别设置AIN1/AIN2、BIN1/BIN2的电平组合确认电机的实际转向和程序里的控制字一致。用手挡住循迹探头看模块指示灯是否变化用尺子放在超声波前面读串口打印的距离值确认数据合理。全部通过后再把车放到地面先手动扶住给一个固定PWM确认直行不跑偏。第6步很重要如果固定PWM下两个轮子转速不一致先别急着调程序检查电机齿轮箱是否卡涩、轮胎是否气压一致、底盘有没有某侧摩擦。机械层面的不平衡软件上很难完全补偿。第一次点亮成功后本篇要做的准备工作就全部完成了。很多教程喜欢直接跳到写代码环节但根据我的经验前面这套规划和接线如果做得扎实后面写循迹避障逻辑的时间能省下一大半反过来前面贪快随便接后面每写一个功能都要赔上几次排查的时间。上篇的三件事到这里就全做完了。回头看会发现项目规划确定边界、硬件选型明确取舍、环境搭建和组装保证基础能用——这三步没有一步在写代码但它们决定了后面写代码时到底是在享受还是在补坑。我帮群友排查过太多次“车不动”的问题最后基本都落在接线、供电和引脚占用上很少真的死在算法上。所以如果你正在做同款小车我真心建议别跳步把上篇这三步走扎实。接下来我会在下篇专门讲电机驱动、循迹与避障的决策逻辑以及整车联调时会遇到的典型问题到时候咱们接着聊。