简介这份资源面向嵌入式入门与电子设计竞赛选手提供一套基于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.4VVCC接5VSTBY接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/721MHz1us计数一次 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%的调试时间。把避坑章那几条先处理好后面就是调参数的事。希望帮到你。本文还有配套的精品资源点击获取