基于STM32的巡线小车设计:从硬件搭建到PID控制算法 📅 发布时间:2026/9/9 15:59:41 👁 浏览次数: 简介基于STM32的巡线小车设计是一份完整的嵌入式工程资源采用STM32F103VET6芯片面向嵌入式初学者、智能车竞赛与电子课程设计人群帮助读者掌握从传感器采集到电机驱动的完整开发链路。开发者可从中学习红外对管路面检测原理、PID巡线算法、PWM直流电机调速以及STM32标准外设库、中断和定时器的实际用法。压缩包共72个文件大小314KB文件以28个H头文件、27个C源文件和8个汇编启动文件为主体另含Keil MDK工程配置、hex烧写文件、JLink下载配置、readme说明文档与清理脚本目录分为User、Libraries、CMSIS、Doc、Project等模块便于查看主程序、外设驱动和文档说明。目前已有1756人学习工程内主程序、中断处理函数、系统时钟配置以及下载调试相关文件齐备可直接打开工程编译烧录也可在现有框架上扩展蓝牙遥控、OLED显示、速度闭环等功能。整体轻量规范对于希望完成一个真实智能车项目并熟悉STM32开发流程的读者具有很强的参考和复用价值。 搞嵌入式的人几乎都绕不过一个经典项目——巡线小车。不管你是正在准备毕业设计的在校生还是刚入门STM32想找点东西练手的开发者基于STM32的巡线小车基本算是一道必做题。它不像点灯、串口打印那样只涉及单片机的一个外设而是把GPIO、定时器、PWM、ADC、中断、控制算法全部串在了一起做完这一个项目你对整个STM32的开发框架和嵌入式系统设计的理解会一下子立体起来。我手里这套“基于STM32的巡线小车设计”正是围绕这个经典场景展开的完整方案今天我就把这套项目的设计思路、硬件选型、核心代码和调试过程中踩过的坑都整理出来。不管你是零基础小白还是已经写过不少代码的老手这篇文章都能给你一个可以直接复现的参考模板而且我会把很多文档里不会细说的“为什么”一并讲清楚。1. 项目整体设计与方案选型拆解巡线小车听起来简单但牵涉到的决策点其实不少。主控选谁、用什么传感器、电机驱动用哪种方案这三个问题没想清楚就直接开干后面基本要返工。我先说说我这套设计的核心思路。1.1 为什么用STM32做主控而不是51或Arduino很多新手上来会问巡线小车用51单片机或者Arduino不也能做吗确实能做但差别在于你能从项目里带走多少东西。51单片机资源太紧张一个PWM模块都要靠定时器模拟传感器多几路就捉襟见肘Arduino虽然上手快但封装层太厚底层寄存器怎么工作、中断怎么响应、定时器怎么配置你基本接触不到。STM32F103C8T6是这套设计的核心选择原因很直接72MHz主频加上丰富的外设资源让巡线算法的实现空间大了很多。你可以同时挂多路传感器、驱动两路直流电机、跑PID控制还能留出串口做调试输出。更关键的是STM32的开发流程涵盖了大量工程实际中用得到的东西——时钟树配置、中断优先级管理、PWM输出、ADC采样、定时器编码器模式这些能力是往更深层嵌入式开发走的基本功。1.2 巡线方案对比与传感器选型巡线的核心是“怎么感知黑线”。目前主流方案大概有三种红外反射式传感器阵列、灰度传感器阵列、摄像头视觉巡线。方案成本抗干扰能力开发难度适用场景红外反射式TCRT5000低中易受环境光影响低入门首选室内固定光线灰度传感器阵列中较好可检测灰度差中需要区分颜色深浅的赛道摄像头视觉巡线高强高竞赛级、高速巡线我用的方案是5路TCRT5000红外反射式传感器阵列。为什么选5路不是3路很简单传感器路数越多你对黑线位置的判断就越精细。3路只能判断“偏左、居左中、中间、偏右、右中”实际只有粗粒度而5路可以计算出一个连续的偏差值配合PD控制算法能让小车走得更顺滑——尤其在大弯道和S弯上这个差异非常明显。另外TCRT5000自带发射和接收管发射红外光被黑线吸收、被白纸反射接收管接收到不同光强后输出不同的电压通过比较器或ADC量化后就能得到数字信号。1.3 电机驱动方案L298N还是TB6612FNG电机驱动也是这个项目的一个关键选型点。市面上常见的是L298N和TB6612FNG。我这里直接推荐TB6612FNG虽然它比L298N贵一点但体积小、效率高、发热低而且内部集成续流二极管不需要额外搭保护电路。L298N是很多教程里的老面孔饱和压降大两路输出跑一会儿芯片就烫得厉害而且它那个大块头装在巡线小车这种紧凑车架上非常占空间。我这套设计里用的是TB6612FNG驱动两颗普通N20微型减速电机带霍尔编码器的版本工作电压3.3V到5V刚好和STM32的电压域匹配。如果你想给电机单独供更高的电压TB6612也能承受只要逻辑电平部分和功率部分分开供电就行。2. 硬件搭建与关键接线要点选型定了之后硬件搭建才是最考验耐心的环节。很多新手在焊接和接线上翻车接线错了轻则没反应重则烧板子。这里我把整个硬件系统的搭建过程完整过一遍。2.1 核心元器件清单与供电规划除了主控、传感器和驱动模块整个小车还需要车架、电池、降压模块、开关等配套。我列一下我实测跑通的完整清单主控板STM32F103C8T6最小系统板蓝色Pill板传感器5路TCRT5000红外循迹模块数字量输出版本电机驱动TB6612FNG模块电机N20微型减速电机1:30减速比带霍尔编码器车架3D打印或者淘宝常见的亚克力智能小车底盘电池2节18650锂电池串联7.4V或者1节3.7V锂电池降压AMS1117-5.0和AMS1117-3.3模块各一个或者直接用带5V/3.3V输出的电源模块供电这里必须注意一个坑STM32和传感器对电压很敏感坚决不能直接用电池电压供电。我的做法是7.4V电池先经过降压模块得到5V给电机驱动板和传感器供电然后再从5V降压到3.3V给STM32。如果电池电压是3.7V单节那驱动板逻辑部分可以直接接3.3V但功率部分要看电机额定电压是多少别直接怼上去。2.2 GPIO分配与接线对照表接线是整个硬件环节最需要细心的地方。我用的GPIO分配方案如下这套分配经过实际验证没有冲突模块信号STM32引脚传感器模块IN1最左PA0传感器模块IN2中左PA1传感器模块IN3中间PA2传感器模块IN4中右PA3传感器模块IN5最右PA4TB6612PWMA左电机调速PA8TIM1_CH1TB6612AIN1PB12TB6612AIN2PB13TB6612PWMB右电机调速PA9TIM1_CH2TB6612BIN1PB14TB6612BIN2PB15ST-LinkSWDIO / SWCLKPA13 / PA14传感器模块的VCC接5VGND接GNDOUT引脚直接接STM32的GPIO。注意一点TCRT5000模块上通常有一个电位器可以调整阈值灵敏度这个电位器在调试阶段要反复调不是装上去就能直接用。还有一个细节TB6612的STBY引脚必须接高电平否则电机驱动输出会被禁用小车怎么都不走。这是新手最常见的“没反应”原因之一后面排查部分我会再强调。2.3 底盘机械结构注意事项机械结构看起来不重要但对巡线效果影响很大。传感器支架的高度和角度决定了红外光的反射路径是否稳定。我实测下来的经验是传感器离地面高度控制在1cm到1.5cm之间最合适太近会因为地面不平整产生抖动误判太远红外光发散严重黑线和白纸的反射差异就不明显了。另外传感器应该沿着车头中轴线等距排开最左和最右的传感器间距要略大于黑线宽度这样小车在正常偏差范围内不会跑出传感器的检测区域。电池装在后轴上方比较合理这样重心偏后车头传感器部分不会因前倾而离地过近或过远。3. 核心算法与代码实现硬件搭建完成之后真正的灵魂在嵌入式软件层面。这个项目的代码核心不是驱动怎么点灯而是三个递进的逻辑读取传感器状态、计算偏差、控制电机修正方向。下面我把每一部分都拆开讲。3.1 传感器状态读取与数据归一化我用的是数字量输出的TCRT5000模块每个传感器输出0或1检测到黑线为0低电平没有检测到在白纸上为1高电平。这个逻辑因模块电路而异有的模块是反的具体看你买的是高有效还是低有效版本代码里就需要适当取反。uint8_t read_sensors(void) { uint8_t val 0; val | (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) 0); // S1 最左 val | (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) 1); // S2 中左 val | (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) 2); // S3 中间 val | (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) 3); // S4 中右 val | (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) 4); // S5 最右 return val; }这段代码把5个传感器读到的状态拼成了一个5位数据每一位代表一个传感器的状态。这样一个字节变量就能表示当前小车在黑线上的相对位置后续逻辑处理起来非常高效。3.2 中线偏移量的加权平均计算有了5路传感器的状态接下来要解决的核心问题是“小车现在偏离黑线多少”这需要一个量化指标。我采用的是加权平均法这个思路和统计学里的加权均值是一个道理。每个传感器给它一个位置权重从左到右分别是-2、-1、0、1、2。加权平均后得到一个浮点型的位置值范围从-2到20表示小车居中负数表示偏左正数表示偏右。float get_offset(uint8_t sensor_val) { int8_t weight[5] {-2, -1, 0, 1, 2}; int sum 0; int count 0; for (int i 0; i 5; i) { if (sensor_val (0x01 i)) { sum weight[i]; count; } } if (count 0) { // 丢线情况返回一个特殊值由上层处理 return -10.0f; } return (float)sum / (float)count; }这里有个关键细节如果所有传感器都读不到黑线count为0此时不能直接除0我返回一个特殊值-10。上层逻辑看到-10时要判断这是左侧丢线还是右侧丢线——如果上一次的偏移量为负说明车已经冲出左侧此时应该加大右转反之亦然。如果不加这个保护小车会在丢线瞬间直接失控乱跑。3.3 PD控制算法与控制律设计计算出偏移量之后最理想的转向逻辑是“偏移越大修正越猛”。单纯用比例控制P控制也能跑但是容易产生振荡——小车会左右来回甩头尤其在入弯出弯的时候特别明显。解决办法就是加上微分项D控制参考偏移量的变化率来提前抑制这种振荡。PD控制的公式output Kp * offset Kd * (offset - last_offset)其中offset是本次偏差last_offset是上一次偏差它们的差就是偏差的变化率。这个变化率能预判小车正在快速偏向哪一侧从而提前反向修正。最后把output叠加到左右电机的PWM值上void control_motor(float offset, float *left_pwm, float *right_pwm) { float base_speed 180.0f; // 基础PWM值范围0~255 static float last_offset 0.0f; float derivative offset - last_offset; float correction Kp * offset Kd * derivative; *left_pwm base_speed - correction; *right_pwm base_speed correction; last_offset offset; }这个逻辑的字面意思就是当小车偏左时offset为负correction为负左轮PWM增大、右轮PWM减小小车向右修正。Kp和Kd是两个需要调试的参数没有万能值和车重、电机响应速度、传感器间距都有关系。我实测小车在Kp25、Kd8左右表现最稳但你的车架不同、电机不同这个参数可能要上下浮动不少。3.4 PID参数调试的实用方法参数调试是整个项目里最玄学也最容易劝退人的环节。很多人把Kp调大一点车就开始疯狂抖调小一点车又过弯跟不上。我的建议是“先P后D逐步逼近”。先把Kd设为0只调Kp。从很小的值比如5开始逐渐增大直到小车在直线段能稳定走直但不要追求过弯性能。然后加入Kd从1开始往上加直到过弯时车身的摆动明显减小。这个过程一定是在实际赛道上反复跑的我一般会在电脑上打开串口监视器把offset的值实时打印出来通过观察波形来确定车是在振荡还是跟踪不上。串口数据有时候比你的眼睛更可信。4. 实测调试实录与常见问题排查无论是新做的小车还是别人给的代码调试阶段总是最痛苦的。这一节我把自己项目中真实遇到的、以及周围同学问得最多的问题汇总出来每个问题都附上排查思路和解决方法可以直接对照排查。4.1 程序下载不进去error: no stm32 target found这个问题的报错特征是在Keil或STM32CubeProgrammer下载时弹出错误提示内容包含no stm32 target found。这几乎都是连接层面的问题我从自己多次踩坑中总结出三个主要方向接线问题ST-Link的SWDIO、SWCLK、GND、3.3V四根线有没有和板子对应接好。SWDIO对应PA13SWCLK对应PA14GND要共地3.3V要接到板子电源引脚。板子供电不足ST-Link的3.3V通常只有几十mA的驱动能力如果板子还有其他外设都在耗电可能导致芯片上电异常。此时可以改用外部供电但必须保证共地。BOOT0引脚状态如果BOOT0被拉高芯片会进入ISP引导模式此时SWD调试口可能无法正常连接。正常情况下BOOT0应该始终接地。我遇到最邪门的一次是杜邦线老化导致的接触不良换了一组线就好了。所以排查顺序建议是先换线再查供电最后查BOOT0。4.2 传感器阈值调不好黑线检测不稳定TCRT5000模块上的电位器是一个单圈可调电阻用来调节比较器的阈值。这个调节很敏感转一点点差异就很大。我的做法是把小车放在白纸上用螺丝刀慢慢拧电位器直到指示灯刚刚亮起或熄灭取决于模块是高有效还是低有效然后再把小车移到黑线上确认检测状态翻转。如果反复调整都找不到一个能稳定区分黑白的位置大概率是传感器离地高度不对回到2.3节说的重新调整支架高度。另外要检查环境光干扰。如果小车在窗边跑阳光直射导致红外接收管饱和所有传感器都输出白色状态小车必跑飞。解决办法是给传感器加一个遮光罩或者在室内拉上窗帘调试。4.3 小车高速跑飞或者过弯出线这个问题通常有两个直接原因基础PWM值太大或者PD参数没调好。先说基础速度如果直接把base_speed设为255基本等于全速狂奔传感器读取周期跟不上控制周期也跟不上小车肯定飞。我建议基础速度从100左右开始跑顺了再慢慢往上加每加10就跑一圈看效果。第二个原因是PD参数设置不当。如果Kp过小小车在偏出赛道之前修正力不够如果Kd过大会对偏差变化反应过度在弯道入口出现明显的顿挫感反而更容易出线。我自己的调试习惯是先在直线赛段上把Kp调到既不左右摆动又能快速回中的值然后再到弯道上调Kd。4.4 常见问题速查表现象可能原因排查与解决下载时报 no target found接线错误/供电不足/BOOT0为高换线、外接电源、BOOT0接地电机完全不动STBY未接高 / TB6612供电缺失 / PWM引脚配置错检查STBY引脚、VCC和GND电机只朝一个方向转AIN1/AIN2时序不对检查转向引脚电平组合小车左右疯狂摆头Kp过大或Kd为0增大Kd降低Kp小车直线总是偏向一边两侧电机转速不一致 / 车轮打滑在代码中做差速补偿传感器读不到黑线电位器没调好 / 高度不合适重新调阈值调整传感器高度电池电压掉得快电机堵转 / 电池容量小检查机械阻力换高容量电池4.5 一个容易被忽略的干扰问题电机对传感器的影响调试到后期你会发现一个很诡异的现象小车静止在轨道上时传感器读数一切正常一旦启动电机传感器数据开始乱跳。这是典型的电磁干扰问题——直流电机换向时产生的火花放电会辐射电磁噪声干扰传感器信号线。解决思路有三个按优先级排序一是传感器和电机驱动板的电源分开走线不要共用一根细杜邦线二是所有信号线尽量短并且避开电机电源线三是在电机两端并联一个104陶瓷电容或者在驱动板电源入口加一个大一点的电解电容做滤波。这个问题在仿真和纯代码层面看不出来只有实际跑起来才会遇到但处理不好会浪费大量排查时间。5. 项目扩展方向与经验沉淀做完一台能稳定跑完全程的巡线小车并不是终点。这个项目真正的价值在于它是一个完整的嵌入式系统案例往任何一个方向深挖都能长出新的项目。如果想让小车跑得更聪明可以从两个方向入手一是速度闭环控制N20电机自带的霍尔编码器此时派上用场通过定时器的编码器模式读取电机转速再加一个内环PID控制器这样不仅能精确控速还能让转向更平滑二是加入无线遥控和上位机通过ESP8266模块或者蓝牙模块把小车状态实时发送到手机或电脑上实现远程调参、赛道数据回传。如果想让小车识别更复杂的赛道元素可以把传感器增加到8路甚至更多或者直接换成摄像头方案。STM32F103的内存跑视觉算法比较吃力但可以搭配K210这类边缘AI芯片做视觉识别两个芯片通过串口或SPI通信STM32负责运动控制K210负责视觉处理。这个架构也是智能车竞赛里比较成熟的协作方案跟热词里提到的K210和STM32通讯场景完全对应上。我个人最大的体会是巡线小车这个项目几乎覆盖了嵌入式开发从硬件到软件到算法的全链路硬件连接让你理解电源和信号完整性驱动开发让你真正用懂定时器和PWM外设控制算法让你体会理论公式落到实际机械系统上的效果。做完这台小车你再去学RTOS、学习更复杂的机器人项目都会觉得底子厚实很多。最后再分享一个小技巧调试的时候一定不要省串口打印。把偏移量、PWM输出值、目标速度这些关键变量通过串口定时输出到串口助手用实时曲线工具或者简单的文本观察能让你比单靠眼睛看小车跑更快地定位问题。嵌入式调试的底层逻辑永远是先让系统自己把话说清楚再决定怎么改。本文还有配套的精品资源点击获取