三路超声波自动跟随小车:STM32测距、差速控制与状态机实战

发布时间:2026/9/8 23:46:09
三路超声波自动跟随小车:STM32测距、差速控制与状态机实战 简介面向STM32嵌入式初学者、智能车竞赛参与者及创客爱好者这套三路超声波自动跟随小车项目提供了完整的参考工程。项目基于STM32微控制器利用三路超声波传感器检测前方障碍物距离由自动跟随算法实时调整电机转速与转向使小车能够自动跟随目标前行适合课程设计、毕业设计及兴趣实践。压缩包内共309个文件大小约8.45MB除了常见的.c/.h源码、.s启动文件和.hex可执行文件还附带.o/.crf编译中间产物、Keil/IAR工程配置.uvproj/.ewp及说明文档等方便直接打开工程重新编译或烧录调试。目前已有2399人学习代码中涉及的GPIO配置、超声波回波计时、PWM电机调速和算法框架能帮助读者理清三路跟随小车的完整开发链路并在理解基础上优化出自己的控制逻辑。1. 项目概述与整体设计思路做跟随小车这个项目很多人的第一反应是上一堆传感器视觉模块、激光雷达、OpenMV样样都想往上堆。但实际做下来你会发现对于一个入门级或者进阶练手的嵌入式项目来说三路超声波方案反而是性价比最高、逻辑最清晰、最容易调通的选择。所谓“三路超声波自动跟随小车”核心就是利用分布在车体前部左、中、右三个方向的超声波传感器实时测量前方目标的距离和大致方位然后通过STM32对两路直流减速电机做差速控制让小车始终与目标保持一个合理距离同时自动调整朝向实现“人走车跟、人停车停”的跟随效果。这个项目非常适合三类人一是刚学完STM32基础外设、想做综合项目来巩固知识的同学二是准备电子设计竞赛或课程设计、需要一个完整可演示作品的学生三是纯粹想给自己做个好玩的小玩具的DIY爱好者。整份工程打包了完整的HAL库工程源码、三路超声波的驱动代码、电机PWM控制代码以及一套简单的跟随状态机逻辑拿到手之后只要按硬件接线图连好线烧录进去就能跑起来。我拿到这份工程之后第一反应是它没有盲目追求“智能”而是把跟随这件事拆成了三个核心问题——测距准不准、判断快不快、转向稳不稳。这三个问题解决好了整个项目的骨架就立住了。三路超声波的分布方式不是随便放的它直接决定了小车对目标方位的分辨能力也决定了跟随过程中会不会出现“跟丢”或者“左右乱摆”的情况。2. 硬件选型与模块连接细节2.1 主控选型为什么是STM32F103C8T6这类芯片工程默认的主控是STM32F103系列具体到板子就是大家最熟悉的C8T6最小系统板。这颗芯片虽然已经堪称“古董”但做这个小车项目完全够用甚至有点杀鸡用牛刀的意思——72MHz主频、3个定时器可用于PWM输出、多个TIM还有输入捕获通道配合HAL库或者标准库写起来都很顺手。三路超声波测距需要的是“产生触发脉冲 测量回波脉宽”前者用普通GPIO翻转即可后者需要用定时器的输入捕获功能来精确测量高电平持续时间。F103的定时器资源足够同时挂三路输入捕获不需要用外部中断硬扛这是选择它的一个关键理由。选型时要特别注意的一个坑不是所有STM32型号的定时器通道都能随意映射到任意引脚。比如TIM2的CH1只能映射到PA0TIM3的CH1只能映射到PA6如果你用的型号是F103C8T6引脚分配务必要去查数据手册里的复用功能表。我见过不少同学在画板的时候随便选引脚结果代码里怎么配置定时器都捕获不到波形折腾半天才发现是引脚复用映射不对。2.2 三路超声波的布局与接线三路超声波采用的模块是HC-SR04这是市面上最常见、最便宜、资料最多的超声波测距模块测距范围大概在2cm到4m精度在±3mm左右对于跟随小车这种场景已经足够了。三路模块的安装位置建议是这样的左侧一路、右侧一路、中间一路左右两路分别朝向斜前方大约与车体中轴线成30度到45度夹角中间一路正对前方。这样布局的好处是当前方目标略微偏离中轴线时左右两路超声波就能通过回波时间差判断出目标在左边还是右边给差速转向提供依据。如果是两路方案目标在正前方小角度偏移时很容易丢失方向信息小车就会表现出“犹豫不决”的摆动。接线方面HC-SR04一共有四个引脚VCC、GND、TRIG、ECHO。VCC接5V注意不是3.3VHC-SR04的工作电压范围是5V如果你用3.3V供电测距精度会明显下降甚至完全无法工作。TRIG是触发引脚接STM32的普通GPIO输出ECHO是回波引脚输出的是一个与距离成正比的高电平脉冲这个引脚的输出电平是5V的不能直接接STM32的GPIO必须加一个分压电阻比如用10K和20K电阻分压到3.3V或者用两个电阻组成的分压网络。这个细节是新手最容易烧引脚的地方千万不能省。对应到工程代码里默认的引脚分配是左路超声波TRIG → PB0ECHO → PA0TIM2_CH1中路超声波TRIG → PB1ECHO → PA1TIM2_CH2右路超声波TRIG → PB10ECHO → PA2TIM2_CH32.3 电机驱动与供电方案驱动两个直流减速电机用的是最常见的L298N模块或者TB6612FNG模块。L298N便宜大碗但压降比较大发热也严重TB6612FNG体积小、效率高现在用的人更多。工程里默认用的是TB6612FNG它需要两路PWM输入PWMA、PWMB和四路方向控制引脚AIN1、AIN2、BIN1、BIN2逻辑电平兼容3.3V可以直接由STM32驱动不需要额外电平转换。电机供电建议用7.4V或者11.1V锂电池组经过稳压模块给STM32供5V或3.3V电机电源和逻辑电源要共地否则PWM信号参考电位不一致电机转起来会乱抖。供电这块有一个特别值得强调的教训不要让电机和MCU共用同一路电源而不做隔离或滤波。直流电机启动瞬间电流很大会导致电源电压跌落轻则STM32复位重启重则Flash程序丢失概率虽小但真遇到过。我之前的做法是电池输出先给一个DC-DC降压模块降到5V5V再分两路一路直接给HC-SR04供电另一路过一个AMS1117-3.3给STM32供电同时在电机电源两端并一个大电容470uF以上和一个小电容104做滤波。这样处理之后电机启动导致的复位问题基本绝迹。3. 超声测距原理与三路数据融合逻辑3.1 测距原理时间差法背后的物理细节HC-SR04的工作原理用一句话说就是发一束超声波数一下它撞到目标弹回来花了多长时间时间乘以声速除以二就是距离。具体到模块内部TRIG引脚收到一个10us以上的高电平脉冲后模块会自动发出8个40kHz的方波脉冲同时把ECHO引脚拉高当模块检测到回波信号时再把ECHO引脚拉低。所以ECHO高电平持续的时间就是超声波从发射到返回的全程时间。距离计算公式就是经典的距离(cm) 高电平时间(us) / 58为什么是58而不是别的数因为声速在15摄氏度空气中大约是340m/s也就是0.034cm/us往返距离要除2所以就是 1 / 0.034 / 2 ≈ 14.7不对这里细致算一下声速340m/s 34000cm/s 0.034cm/us时间us乘以0.034得到的是往返总路程cm再除以2才是单程距离也就是 时间 * 0.034 / 2 时间 / 58.82工程上取整为时间/58。如果要更精确一点可以加温度补偿实际声速V 331.4 0.6 * TT为摄氏度这样算出来的距离在高精度场景下更可靠。在代码里可以做成一个带温度查表的函数不过对于跟随小车来说时间除以58就够用了精度完全在可控范围内。还有一个细节要注意声波在空气中传播是有波束角的HC-SR04的探测波束角大约是15度也就是说它探测到的“目标距离”不一定是正前方的距离而是波束锥形范围内最近反射点的距离。这就是为什么三路超声波要错开角度安装——如果三路全部正对前方左右两路和中间路的读数基本一样就失去方位判断的意义了。3.2 三路数据怎么用距离区间判定与方位判断拿到三路距离数据之后怎么判断“目标在哪里”我的做法是设置几个关键距离阈值跟随距离比如25cm~35cm、停车距离20cm、搜索距离60cm。中间路的距离用于判断目标和车的前后关系左右两路的差值用于判断目标的左右偏移。具体逻辑是这样的中距离在20cm到35cm之间认为目标在合适的跟随区间保持当前速度中距离小于20cm认为目标太近了小车后退或停止中距离大于60cm认为目标丢失进入原地旋转搜索状态左距离明显小于右距离说明目标偏左小车需要左转跟上右距离明显小于左距离说明目标偏右小车需要右转跟上。这里面有个容易忽略的问题超声波传感器的数据不是连续平稳的而是有波动的。比如人在走动时裤腿摆动、手臂晃动都会导致某一路数据瞬间跳变。如果直接拿原始数据做判断小车会频繁误动作。所以必须先做滤波处理我用的方法是滑动平均滤波每个通道缓存最近5次数据取平均同时加一个幅度限制如果某次测量值与上次的差值超过30cm认为是无效跳变直接丢弃。这个处理做完之后小车的动作明显稳定了很多。3.3 状态机设计跟随逻辑代码的骨架整个跟随逻辑不建议用一堆if-else堆叠而是建议拆成状态机来做。状态机的优势是逻辑清晰、容易扩展、调试的时候可以很方便地打印当前状态。工程里我是这样划分状态的状态触发条件动作搜索三路距离全部大于60cm低速原地旋转寻找目标前进中距离在35~60cm区间前进速度与距离成反比跟随中距离在20~35cm区间根据左右差值微调方向保持匀速后退中距离小于20cm后退直到距离回到安全区间避障任意一路距离小于15cm且不是中距离主导向相反方向转向这里有一个很关键的经验跟随状态和前进状态不要用同一个速度。在35~60cm区间如果全速前进当目标停下来的时候小车会冲过头直接进入后退状态体感上就是“点头”式的顿挫。正确做法是速度用比例控制距离越远速度越快距离越接近目标速度越慢这样接近过程是平滑减速的。比例参数我调下来比较顺手的是目标距离30cm速度系数Kp取值在1.5~2.0左右具体需要结合电机转速和车轮直径实测调整。4. 工程代码解析与关键功能实现4.1 定时器输入捕获与超声波驱动工程里超声波驱动的核心是定时器输入捕获而不是单纯靠GPIO中断死等。原因很简单输入捕获是硬件计时精度高不占用CPU。以中路超声波为例代码逻辑是这样的// 触发一次测距 void Ultrasonic_Trigger(void) { HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_SET); delay_us(15); HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET); }触发之后ECHO引脚会在收到回波时产生一个高电平脉冲我们开启TIM2的输入捕获功能捕获这个高电平的上升沿和下降沿两次捕获的时间差就是高电平持续时间。用HAL库实现的话需要开启定时器的两个捕获通道一个捕获上升沿、一个捕获下降沿然后在回调函数里记录计数器值。void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM2) { if (htim-Channel HAL_TIM_ACTIVE_CHANNEL_1) // 中路ECHO - PA0 { if (capture_up[0] 0) { capture_up[0] HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING); } else { capture_down[0] HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 防止计数器溢出导致数据异常 width_us[0] (capture_down[0] capture_up[0]) ? (capture_down[0] - capture_up[0]) : (0xFFFF - capture_up[0] capture_down[0]); distance_cm[0] width_us[0] / 58.0; __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING); capture_up[0] 0; } } // 左右两路通道代码类似CH2/CH3对应处理 } }这里有个新手经常踩的坑定时器的预分频和计数周期配置不对导致捕获值溢出。我的做法是把TIM2配置成1MHz计数频率72MHz主频分频72这样计数器每1us加116位计数器最大计数65535也就是最大可以测到65ms的高电平对应距离大概是 65000 / 58 ≈ 1120cm远超HC-SR04的量程所以不会出现溢出问题。如果你的配置是分频没改、计数器跑在72MHz那1us就要计数72次65535个计数只对应910us大概只能测15cm超声波模块基本就是个废的。4.2 电机PWM控制与差速转向差速转向的实现是跟随逻辑的另一个核心。两个电机分别由定时器的两路PWM控制速度方向由GPIO控制。我用的PWM频率是10kHz——这个频率下电机的噪音比较小驱动芯片的开关损耗也低。代码里PWM初始化和调速函数大概是这个思路void Motor_SetSpeed(int16_t speed_left, int16_t speed_right) { // speed范围为-100~100正数为前进负数为后退 if (speed_left 0) { AIN1_reset(); AIN2_set(); // 左电机前进 __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, speed_left * 100); // 占空比映射 } else { AIN1_set(); AIN2_reset(); // 左电机后退 __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, (-speed_left) * 100); } // 右电机逻辑相同用TIM_CHANNEL_2 }在跟随状态里左右轮速度不是固定的而是根据左右两路超声波距离差来动态调整。一种简洁有效的方法是先根据中距离算出一个基础速度base_speed然后根据左右距离差算出转向补偿量delta_speed左轮速度 base_speed delta_speed右轮速度 base_speed - delta_speed。当目标偏左时左距离小于右距离delta_speed为负左轮减速、右轮加速小车就向左转弯跟上。这个原理和坦克差速转向是一样的核心在于delta_speed的控制幅度——调得太大车子会蛇形走位调得太小跟不上目标转向的速度。我实测下来delta_speed的最大值限制在基础速度的40%左右比较合适太大容易原地打转。4.3 OLED显示与调试信息的处理工程里还集成了一块OLED显示屏I2C接口用于实时显示三路距离值和当前状态。这个屏幕对调试帮助非常大因为超声波数据在串口里看也可以但不如直接挂在车上看直观。我把显示刷新频率控制在10Hz左右避免I2C通信占用太多CPU时间影响测距触发时序。显示内容分三行第一行显示“L: xx cm”第二行显示“C: xx cm”第三行显示“R: xx cm”和当前状态码0搜索、1前进、2跟随、3后退、4避障。这样小车跑起来你一眼就能看出当前是哪个状态、哪路数据在起作用排查问题的时候效率极高。我在实际调试中遇到过“小车一遇到墙角就疯狂转向”的问题就是靠看OLED上的数据才定位到原因——原来是左右两路探头安装角度太小导致墙角反射同时落在左右两个探头的波束范围内距离差信号不稳定。把左右探头往外掰了几度之后问题立刻消失。5. 常见问题与排查技巧实录5.1 烧录报错与调试器连接问题这份工程烧录的时候最常遇到的一个报错是Error: No STM32 target found! If your product embeds debug authentication, please...这个报错的意思就是调试器没有找到STM32芯片通常原因有三个接线没接对、板子供电不足、芯片被锁死。接线方面要核对SWD四根线——SWDIO、SWCLK、GND、3V3是否一一对应这四根线接错任何一根都会导致报错。供电不足的情况常见于用USB转TTL模块给板子供电电流不够时芯片可以运行但调试接口不稳定解决办法是外接独立的3.3V电源或者用ST-Link自带的5V输出。芯片锁死多半是因为之前的程序把SWD引脚复用成了普通GPIO导致调试接口被禁用解决办法是用ST-Link Utility连上后在“Option Bytes”里把读保护级别改为Level 0恢复出厂状态后再重新烧录。关于最后一种情况工程代码里特意在初始化阶段保留了SWD引脚的复用功能所以正常使用中基本不会触发锁死但如果你自己改过引脚配置就要特别注意这个坑。5.2 测距数据跳变与滤波策略调整如果你发现OLED上的距离数据不停地乱跳尤其是静止目标测出来的距离忽远忽近那大概率不是代码问题而是超声波模块本身受到干扰。常见的干扰源有三个一是电机产生的电磁干扰尤其是电刷电机在转动时会产生大量火花噪声可能会通过电源线传导到超声波模块上二是多个超声波模块同时工作时的相互串扰A模块发出的超声波被B模块接收到产生虚假回波三是环境中的高频噪声比如电脑风扇、空调等设备发出的超声频段噪声。针对第一种干扰除了前面说的电源滤波之外还建议把超声波模块的VCC和GND直接用双绞线连接到电源输出端不要和电机线并行走线。针对第二种串扰可以采用“轮询触发”的方式也就是三路超声波不要同时触发而是按照时间片依次触发每路测完一次之后再触发下一路中间间隔至少30ms。这份工程里用的就是这种轮询方案每一轮三个方向依次采样循环周期大概是90ms既避免了串扰也不会让数据太滞后。针对第三种环境噪声只能靠滤波算法来缓解如果目标距离在30cm以内可以适当收紧滑动平均窗口的剔除阈值比如距离跳变超过20cm就按无效处理。5.3 跟随时“画龙”现象的调参方法很多人在做这类型小车的时候都会遇到一个现象小车在跟随过程中左右摇摆像画龙一样走S形路线。这个问题的根源在于转向补偿量过大或者响应过快导致系统产生了振荡。用控制理论的话说就是比例增益太大系统阻尼不足。解决办法有两条路一是把delta_speed的最大幅度调小让转向动作更柔和二是在转向补偿上增加一个死区——左右距离差在5cm以内时不调整方向超过5cm才开始转向。死区的加入非常有效因为超声波本身有测量误差5cm以内的差值分布我们可以归为噪声不响应反而能让小车走得更直。另一个容易忽视的调参方向是执行频率。如果控制的是10Hz采样率但转向调整也是10Hz执行一次那么在两次控制之间小车已经往前走了好几厘米转向修正总会慢半拍。我最终把超声波采样频率控制在20Hz左右控制周期50ms在50ms内完成“数据滤波 状态判断 速度更新”整套逻辑实测下来跟随时几乎没有滞后感。当然这个频率还取决于你单片机主频和代码效率F103跑20Hz循环是没有任何压力的即使开三个定时器输入捕获也占用不了多少CPU。5.4 关于扩展从跟随到避障与通讯做完基础跟随之后很多人会想往工程里加功能。我个人的经验是按这个优先级扩展先加红外避障模块在现有状态机里增加一个避障状态即可再加蓝牙遥控模式用HC-05串口透传模块让程序在“遥控”和“跟随”两个模式间切换最后再考虑和手机APP或上位机做可视化通讯。蓝牙接入比较简单核心就是USART串口中断简单的指令解析协议比如发送“F”表示前进、“B”表示后退、“S”表示停止跟随状态下收到任何指令就自动退出跟随切到手动模式。如果想把数据发到上位机做实时曲线监控可以用USARTDMA的方式以固定帧格式发送三路距离值和当前状态上位机用串口助手或者Python的pyserial库读取并绘制曲线。这样调试跟随算法的参数会直观很多比如调整比例系数时直接观察距离曲线的收敛情况比靠肉眼盯着小车走位要靠谱得多。写在最后的一点经验这个项目我前前后后做了三个版本第一个版本用两路超声波跟随时频繁跟丢第二个版本加了中间探头但三路探头全部正对前方左右方向判断依然不灵敏直到第三个版本把左右两路探头外展角度调整好、加上滑动平均滤波和死区控制之后小车才算真正“稳”下来。回头总结整个开发过程最深的体会是嵌入式小车项目里硬件安装方式和代码逻辑同等重要甚至更重要。超声波探头的安装角度差几度软件上写多少行滤波代码都救不回来供电布线乱一点再好的控制算法也跑不出一个稳定的数据源。所以做这类项目一定要先把硬件底子打好再在状态机和调参上下功夫。如果你拿到这份工程之后自己动手搭了一套遇到某个环节卡住了不妨按这篇文章的顺序逐项排查——接线、供电、测距、滤波、调参大部分问题都逃不出这五个环节。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询