☰
STM32超声波灭火小车实战:从选型接线到状态机避坑全解析
2026/10/14 18:06:26 网站建设 项目流程

简介:这份资源面向嵌入式入门与电子设计竞赛选手,提供一套基于STM32F103C8T6的超声波智能灭火小车完整方案,可解决自主巡逻、避障与火源定位的实战需求。包内包含程序源码、原理图与PCB文件、参考论文及程序流程图,覆盖从硬件电路到软件逻辑的完整链路。压缩包约47.02MB,文件以源码、原理图、PCB工程和文档为主,分别对应程序烧录、电路复现与设计说明。已有1579人学习下载,说明方案具备一定参考价值。读者可据此掌握L293D电机驱动、超声波测距避障与火焰传感器检测火源的协同控制思路,理解寻迹小车在仓库安全巡逻场景下的任务调度与调试方法,并借助参考论文与流程图快速梳理系统架构,适合课程设计、毕业设计或竞赛备赛时对照复现与二次开发。

1. 从一堆散件到能跑能喷:STM32超声波灭火小车到底在做什么

很多人第一次看到“基于STM32超声波智能灭火小车”这个标题,脑子里浮现的是玩具遥控车加个喷水壶。但真正动手做过的人知道,这东西是一个把感知、决策、执行塞进一块MCU的完整闭环系统。它要解决的核心问题是:在一个有障碍物和火源的场地里,小车得自己找到火、靠近到有效距离、停下来把火灭掉,全程不能靠人遥控。适合谁做?电子类专业做课程设计的学生、想入门嵌入式控制闭环的开发者、以及需要一个小型移动机器人平台验证算法的人。我见过太多人卡在“超声波测距跳变导致小车原地抽搐”或者“灭火风扇一启动STM32就复位”这种血泪坑上。这篇笔记就按我实际搭车的顺序,把选型、接线、代码、参数和翻车点一次讲透。

2. 硬件选型与接线:为什么STM32F103C8T6是甜点,不是随便挑的

2.1 主控、驱动、传感器的最小可行清单

做这个车,主控选STM32F103C8T6(俗称蓝板)是常见做法。原因很直接:72MHz主频跑超声波时序和电机PWM绰绰有余,片上定时器够多,价格低到烧了不心疼。电机驱动我用TB6612FNG,比L298N效率高、发热小,关键是支持PWM频率到100kHz,不会像L298N那样在低速时发出刺耳啸叫。超声波模块用HC-SR04,便宜、资料多,但它的盲区和温漂是后面避坑章要重点说的。火焰检测用3路红外火焰传感器,分别朝左前、正前、右前安装,输出数字量给STM32的GPIO。灭火执行器用微型直流风扇加雾化喷头,或者简单点用5V小水泵,通过继电器或MOS管控制。

电源部分必须单独说:电机和风扇走一路7.4V锂电池,STM32和传感器走另一路5V稳压。我一般用两块电池或者一块电池加两路降压模块,共地但不共电源轨。这个细节决定了你的小车会不会在风扇启动瞬间“抽风”。

2.2 接线表与引脚分配

下面这张表是我在多个版本上验证过的引脚分配,避开了JTAG占用引脚和ADC冲突。

功能模块引脚外设资源备注
HC-SR04 TrigPA1GPIO输出推挽输出,初始低电平
HC-SR04 EchoPA0TIM2_CH1输入捕获模式
左电机PWMPA8TIM1_CH1频率10kHz
左电机方向PB0GPIO输出与PWM配合控正反转
右电机PWMPA9TIM1_CH2频率10kHz
右电机方向PB1GPIO输出同上
火焰传感器左PB5GPIO输入上拉输入,低电平有效
火焰传感器中PB6GPIO输入上拉输入
火焰传感器右PB7GPIO输入上拉输入
风扇/水泵控制PB8GPIO输出经MOS管驱动
调试串口TXPA9USART1_TX与右电机PWM复用需注意

注意PA9同时被右电机PWM和串口TX占用,实际接线时二选一。我一般把串口调试放在开发阶段用,最终固件里关掉串口输出,PA9留给电机。

2.3 电机驱动与电源的硬约束

TB6612的VM接7.4V,VCC接5V,STBY接5V使能。每路电机输出电流峰值1.2A,堵转2A。如果你选的电机堵转超过这个值,驱动芯片会烫到能煎鸡蛋。我习惯在电机两端各并一个0.1uF瓷片电容,减少换向火花对超声波和火焰传感器的干扰。电源线上再并一个470uF电解电容在驱动板输入端,风扇启动瞬间的压降能压到0.3V以内,STM32就不会复位了。

3. 超声波测距与火焰定位的代码实现:从寄存器到能用的距离值

3.1 用输入捕获测超声波回波脉宽

HC-SR04的时序是:Trig给10us高电平,模块发8个40kHz方波,Echo拉高,高电平持续时间就是声波往返时间。用TIM2输入捕获测这个高电平宽度,比用外部中断加延时准得多。

// TIM2输入捕获初始化,PA0对应TIM2_CH1 void Ultrasonic_Init(void) { GPIO_InitTypeDef gpio; TIM_TimeBaseInitTypeDef tim; TIM_ICInitTypeDef ic; NVIC_InitTypeDef nvic; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // PA0 浮空输入,接Echo gpio.GPIO_Pin = GPIO_Pin_0; gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &gpio); // PA1 推挽输出,接Trig gpio.GPIO_Pin = GPIO_Pin_1; gpio.GPIO_Mode = GPIO_Mode_Out_PP; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio); GPIO_ResetBits(GPIOA, GPIO_Pin_1); // TIM2 时基:72MHz/72=1MHz,1us计数一次 tim.TIM_Prescaler = 72 - 1; tim.TIM_Period = 0xFFFF; tim.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &tim); // 输入捕获:上升沿触发,捕获后立即改为下降沿 ic.TIM_Channel = TIM_Channel_1; ic.TIM_ICPolarity = TIM_ICPolarity_Rising; ic.TIM_ICSelection = TIM_ICSelection_DirectTI; ic.TIM_ICPrescaler = TIM_ICPSC_DIV1; ic.TIM_ICFilter = 0x0F; // 滤波,抑制毛刺 TIM_ICInit(TIM2, &ic); TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE); nvic.NVIC_IRQChannel = TIM2_IRQn; nvic.NVIC_IRQChannelPreemptionPriority = 1; nvic.NVIC_IRQChannelSubPriority = 0; nvic.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvic); TIM_Cmd(TIM2, ENABLE); }

这段代码的关键参数是预分频72-1,让计数器每微秒加一。捕获滤波设为0x0F,能滤掉大部分电机干扰引起的毛刺。中断里第一次捕获上升沿记下计数器值,同时把极性改成下降沿;第二次捕获下降沿算出差值,乘以声速0.034cm/us再除以2,就是距离。

3.2 火焰传感器读值与方向判断

三路火焰传感器输出数字量,低电平表示检测到火焰。但红外火焰传感器有个特性:它检测的是火焰中特定波长的红外线,日光灯和太阳光里也有,所以室内调试时经常误触发。我一般加一个软件消抖:连续读5次,至少4次为低才认为有火焰。

// 读取三路火焰传感器,返回方向:0无火,1左,2前,3右 uint8_t Flame_GetDirection(void) { uint8_t cnt_left = 0, cnt_mid = 0, cnt_right = 0; for (uint8_t i = 0; i < 5; i++) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_5) == 0) cnt_left++; if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6) == 0) cnt_mid++; if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7) == 0) cnt_right++; Delay_us(200); } uint8_t left = (cnt_left >= 4) ? 1 : 0; uint8_t mid = (cnt_mid >= 4) ? 1 : 0; uint8_t right = (cnt_right >= 4) ? 1 : 0; if (mid) return 2; // 正前方优先 if (left) return 1; if (right) return 3; return 0; }

消抖次数和阈值可以调。如果场地火焰是蜡烛,传感器距离30cm左右能稳定触发;如果是打火机,距离要拉到10cm以内。这个参数没有标准值,必须按你的火源实测。

3.3 电机PWM与运动控制封装

电机控制封装成四个动作:前进、后退、左转、右转、停止。PWM频率10kHz,占空比用TIM1的CCR寄存器设置。我一般把速度分成三档:慢速30%、中速55%、快速80%。靠近火源时用慢速,搜索时用中速。

// 设置左右电机速度和方向 // left_speed, right_speed: -100 ~ 100,负数为反转 void Motor_Set(int8_t left_speed, int8_t right_speed) { // 左电机方向 if (left_speed >= 0) { GPIO_SetBits(GPIOB, GPIO_Pin_0); TIM_SetCompare1(TIM1, left_speed * 100); // 占空比 = speed% } else { GPIO_ResetBits(GPIOB, GPIO_Pin_0); TIM_SetCompare1(TIM1, (-left_speed) * 100); } // 右电机方向 if (right_speed >= 0) { GPIO_SetBits(GPIOB, GPIO_Pin_1); TIM_SetCompare2(TIM1, right_speed * 100); } else { GPIO_ResetBits(GPIOB, GPIO_Pin_1); TIM_SetCompare2(TIM1, (-right_speed) * 100); } }

这里TIM1的ARR设为1000-1,所以CCR值100对应10%占空比。注意左右电机如果装反了,前进会变成原地转圈,调试时先单独测试每个轮子的转向。

4. 避坑与排查:超声波跳变、电源复位、火焰误判这三座大山

4.1 超声波距离值在20cm内疯狂跳变

现象:小车靠近墙壁或火源到20cm以内时,串口打印的距离值在5cm到80cm之间乱跳,小车动作抽搐。

原因:HC-SR04有最小盲区,规格书标2cm,实际在15cm以内回波和发射波混在一起,模块自己都分不清。另外电机PWM的10kHz方波和超声波40kHz虽然不同频,但驱动板上的开关噪声会耦合到Echo线上。

解决:软件上做三件事。第一,连续测5次,去掉一个最大值和一个最小值,剩下3次取平均。第二,如果本次距离和上次距离差值超过30cm,直接丢弃本次值,沿用上次。第三,靠近到15cm以内时不再依赖超声波,改用火焰传感器的触发信号做最终停车判断。硬件上在Echo线对地并一个100pF电容,能滤掉大部分高频毛刺。

4.2 风扇一启动STM32就复位或程序跑飞

现象:火焰传感器触发后,风扇转起来的一瞬间,小车要么复位重启,要么电机乱转,串口输出乱码。

原因:风扇或水泵是感性负载,启动电流是额定电流的3到5倍。如果和STM32共用一路5V,瞬间把电压拉到3V以下,STM32的BOR复位电路就动作了。另外电机换向火花通过电源线传导,也会干扰MCU。

解决:风扇和电机必须走独立电源,或者至少独立降压模块。在风扇电源输入端并一个470uF电解电容加一个0.1uF瓷片电容。MOS管驱动风扇时,栅极加一个10k下拉电阻,防止悬空误导通。如果还复位,在STM32的VDD和VSS之间并一个100uF钽电容,靠近芯片引脚放。

4.3 火焰传感器在日光下一直输出低电平

现象:室内调试正常,拿到窗边或灯光下,火焰传感器没对着火也输出低电平,小车对着白墙喷水。

原因:红外火焰传感器检测的是180nm到260nm波段的紫外线和部分红外线,但廉价模块用的接收管对可见光也有响应。日光灯里的紫外成分和太阳光里的红外成分都会触发。

解决:第一,给传感器加黑色热缩管遮光罩,只留正前方一个小孔。第二,软件上提高消抖阈值,从5次中4次改成10次中8次。第三,如果场地允许,用蜡烛或打火机做火源,不要用强光手电模拟。第四,三路传感器做投票:只有至少两路同时触发才认为有火,单路触发只做方向参考不做停车依据。

4.4 小车原地转圈找不到火源

现象:搜索模式下小车一直转圈,偶尔直走一下又转回去,永远到不了火源附近。

原因:火焰传感器的检测角度很窄,通常只有30度左右。三路传感器覆盖范围不到120度,小车转快了会直接扫过火源而没触发。另外超声波避障逻辑如果优先级高于火焰搜索,小车会一直躲障碍物而忽略火源。

解决:搜索时电机速度降到慢速档,转圈速度控制在每秒60度以内,给传感器足够的响应时间。状态机里火焰信号优先级高于超声波避障,但火焰信号触发后要先停车再判断方向。如果火源在正后方,小车需要先转180度,这个过程中火焰信号会丢失,所以转的时候要持续采样,一旦丢失就反向转回来。

4.5 灭火风扇吹不灭蜡烛

现象:小车找到火源,停下来,风扇也转了,但蜡烛火苗只是歪一下又直起来。

原因:风扇风量不够,或者距离太远。蜡烛火苗需要风速达到一定阈值才能吹灭,普通5V微型风扇在30cm外基本没效果。

解决:第一,把停车距离从30cm改到10cm到15cm,用超声波加火焰双重判断。第二,风扇换成涵道风扇或者用雾化喷头喷水雾,灭火效率高一个量级。第三,如果用水泵,喷头要对准火源根部而不是火苗上部。第四,程序里加一个“灭火确认”步骤:风扇启动后持续2秒,如果火焰传感器仍然触发,小车后退5cm再前进,重新对准。

5. 状态机与调试技巧:让小车从能跑变成可靠

5.1 用状态机替代if-else堆叠

新手最容易写出一堆if-else嵌套,最后自己都理不清。我一般用状态机:搜索、对准、靠近、灭火、确认、退出六个状态。每个状态只做一件事,状态切换条件写清楚。

typedef enum { STATE_SEARCH, // 原地慢转搜索火源 STATE_ALIGN, // 微调方向对准火源 STATE_APPROACH, // 前进靠近 STATE_EXTINGUISH, // 停车灭火 STATE_CONFIRM, // 确认火焰是否熄灭 STATE_BACKOFF // 后退退出 } CarState_t; CarState_t state = STATE_SEARCH; void Car_Update(void) { uint8_t flame_dir = Flame_GetDirection(); uint16_t dist = Ultrasonic_GetDistance(); switch (state) { case STATE_SEARCH: Motor_Set(0, 40); // 原地慢转 if (flame_dir != 0) state = STATE_ALIGN; break; case STATE_ALIGN: if (flame_dir == 2) { // 正前方 Motor_Set(0, 0); state = STATE_APPROACH; } else if (flame_dir == 1) { // 左边 Motor_Set(-30, 30); } else if (flame_dir == 3) { // 右边 Motor_Set(30, -30); } else { state = STATE_SEARCH; // 火源丢失,回去搜 } break; case STATE_APPROACH: if (dist < 15) { Motor_Set(0, 0); state = STATE_EXTINGUISH; } else { Motor_Set(50, 50); } if (flame_dir == 0) state = STATE_SEARCH; break; case STATE_EXTINGUISH: GPIO_SetBits(GPIOB, GPIO_Pin_8); // 开风扇 Delay_ms(2000); state = STATE_CONFIRM; break; case STATE_CONFIRM: if (Flame_GetDirection() == 0) { GPIO_ResetBits(GPIOB, GPIO_Pin_8); // 关风扇 state = STATE_BACKOFF; } else { Motor_Set(-30, -30); // 后退一点再试 Delay_ms(300); state = STATE_EXTINGUISH; } break; case STATE_BACKOFF: Motor_Set(-40, -40); Delay_ms(500); Motor_Set(0, 0); state = STATE_SEARCH; break; } }

这个状态机每10ms调用一次,所有延时用非阻塞方式或者短延时。实际跑的时候,搜索状态转圈速度要慢,对准状态用差速转向,靠近状态用超声波做距离闭环。

5.2 串口调试与参数在线调整

调试阶段一定要留串口输出,把距离、火焰方向、当前状态打印出来。我习惯定义一个调试宏,最终固件里关掉。

#define DEBUG_ENABLE 1 #if DEBUG_ENABLE #define DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__) #else #define DEBUG_PRINT(fmt, ...) #endif

串口波特率115200,每100ms打印一次。如果发现状态切换太频繁,说明传感器抖动,回去加消抖。如果发现距离值更新太慢,检查超声波触发间隔,HC-SR04两次测量之间至少间隔60ms,否则回波会串扰。

5.3 场地标定与参数固化

换一个场地,火焰传感器的阈值、超声波停车距离、电机速度都要重新标。我一般把关键参数放在一个结构体里,调试时通过串口命令修改,确认后写进Flash。

参数典型值调整方向
火焰消抖次数5次中4次误触发多就加大
超声波停车距离15cm风扇弱就减小
搜索转速40%占空比丢火就降低
靠近速度50%占空比撞墙就降低
灭火持续时间2000ms灭不灭就加长
后退距离500ms复燃就加长

这些值没有万能解,必须按你的车重、电机、火源、场地摩擦系数实测。我一般花半天时间专门调参数,比改代码有用得多。

5.4 一个提高可靠性的小技巧:双阈值火焰判断

单阈值判断火焰容易在临界距离抖动。我改成双阈值:距离火源远时用低灵敏度阈值(10次中6次),靠近后用高灵敏度阈值(5次中4次)。这样搜索时不会误触发,靠近后又能稳定确认。实现上就是根据超声波距离动态改消抖阈值,代码里加一个判断就行。

最后一章说个我自己的习惯:每次改完代码,先不接电机,只接超声波和火焰传感器,用串口看数据。数据稳了再接电机,电机稳了再装风扇。分步验证比一次全接上然后抓瞎强十倍。这个车看着简单,但电源、干扰、传感器特性这三个东西能吃掉你80%的调试时间。把避坑章那几条先处理好,后面就是调参数的事。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询