资讯动态

STM32超声波灭火小车实战:L293D驱动、避障与火焰寻火状态机

发布时间:2026/10/14 18:09:32 来源:尧图企业网站定制
简介这份资源面向嵌入式入门与电子设计竞赛选手提供一套基于STM32F103C8T6的超声波智能灭火小车完整方案可用于仓库自主安全巡逻与火源检测灭火等场景。包内包含程序源码、原理图与PCB、参考论文及程序流程图压缩包约47.02MB文件以工程源码、电路设计文件与文档资料为主分别对应代码实现、硬件电路搭建与设计思路说明。目前已有1579人学习下载说明该方案在同类项目中具备一定参考价值。读者可借助源码理解超声波避障与火焰传感器检测的联动逻辑结合L293D电机驱动模块完成寻迹与自主避障运动调试并通过原理图与PCB文件核对硬件连接参考论文与流程图则有助于梳理整体控制流程与答辩写作思路适合作为课程设计、竞赛作品或自主巡逻灭火小车的开发起点。1. 从一堆散件到能跑能喷STM32 超声波灭火小车到底能解决什么如果你手头正躺着一套 STM32 智能车散件或者刚拿到一份名为“基于 STM32 超声波智能灭火小车”的源码包却不确定它能不能跑、该怎么调、值不值得花时间复现那这篇笔记就是写给你的。这个资源本质上是一个以 STM32 为主控、L293D 为电机驱动、超声波模块负责测距避障、火焰传感器负责寻火的完整嵌入式小车方案。它解决的不是“让轮子转起来”这种入门问题而是把寻迹、避障、灭火三个任务串成一条状态机让小车在模拟房间环境里自主找到火源并执行灭火动作。适合已经点过 LED、调过串口想找一个能覆盖多传感器融合与电机控制的综合项目练手的嵌入式初学者也适合需要快速验证 L293D 驱动逻辑和超声波避障策略的熟手做二次开发。我拆过不少类似包这个组合的典型痛点是超声波测距抖动导致原地抽搐、火焰传感器误触发、L293D 发热降频下面会逐个拆开讲。2. 硬件链路与 L293D 驱动为什么选它怎么接线不翻车2.1 主控与驱动选型的现实理由STM32 在这个项目里不是性能过剩而是刚需。超声波模块需要输入捕获来测回波高电平时间火焰传感器需要 ADC 采样或者数字量中断电机需要 PWM 调速寻迹需要多路 GPIO 或 ADC。如果用 51 单片机定时器资源和中断优先级会非常紧张稍微加一点滤波逻辑就捉襟见肘。STM32F103 系列常见做法是 TIM 输入捕获测超声波、TIM PWM 驱动 L293D 使能脚、ADC 扫描火焰传感器模拟输出这套外设分配在 F103C8T6 上刚好够用。L293D 是双 H 桥驱动单片能带两路直流电机峰值电流 1.2A持续 600mA 左右。灭火小车常用黄色 TT 马达堵转电流能到 1A 以上所以 L293D 属于“刚好够用但余量不大”的选择。选它的理由很直接接线简单、逻辑电平兼容 3.3V、自带续流二极管不用额外加保护。但代价是压降大实测输出到电机大约只有输入电压的 70% 到 80%用 4 节 AA 电池供电时电机端可能只剩 3.5V 左右转速明显偏软。常见做法是电源用 2 节 18650 串联7.4V或者 6 节 AA9V再经过 L293D 降到电机能接受的区间。提示L293D 的 VCC1 是逻辑供电5VVCC2 是电机供电7.4V 到 9V两个脚都要接只接一个电机不转或者芯片发烫。2.2 接线表与关键参数下面这张表是我实际跑通后整理的引脚分配不同板子可以改但外设冲突要提前避开。功能STM32 引脚外设资源备注超声波 TrigPA0GPIO 输出10us 高电平触发超声波 EchoPA1TIM2_CH2 输入捕获测高电平时间左电机方向PB0GPIOL293D IN1左电机方向PB1GPIOL293D IN2右电机方向PB2GPIOL293D IN3右电机方向PB3GPIOL293D IN4电机使能PA8TIM1_CH1 PWM左路调速电机使能PA9TIM1_CH2 PWM右路调速火焰传感器PA4ADC1_IN4模拟量读火焰强度寻迹左PB5GPIO 输入红外对管寻迹右PB6GPIO 输入红外对管PWM 频率建议设在 8kHz 到 16kHz 之间。太低电机会有啸叫太高 L293D 开关损耗变大发热明显。我一般用 TIM1 的 72MHz 主频预分频 8-1自动重装 1000-1得到 9kHz 左右的 PWM占空比 0 到 1000 对应 0% 到 100%。2.3 电机控制代码与逻辑说明// L293D 双电机方向控制左右各两路 GPIO void Motor_SetDirection(uint8_t motor, uint8_t dir) { if (motor MOTOR_LEFT) { switch (dir) { case DIR_FORWARD: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); break; case DIR_BACKWARD: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); break; default: // 刹车 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); break; } } else { // 右电机同理操作 PB2/PB3 } } // 设置 PWM 占空比arr 为自动重装值 void Motor_SetSpeed(uint8_t motor, uint16_t duty) { if (duty 1000) duty 1000; // 限幅防止越界 if (motor MOTOR_LEFT) { __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, duty); } else { __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_2, duty); } }方向控制就是 H 桥的两个输入脚一高一低电机正转同时低电平就是刹车。PWM 占空比直接决定平均电压从而决定转速。这里没有做死区补偿因为 L293D 内部有逻辑方向切换时最好先刹车再换向否则瞬间反向电流可能触发芯片过热保护。我一般会在换向前加 20ms 的刹车延时这个习惯救过好几片 L293D。3. 超声波避障与火焰寻火状态机怎么串阈值怎么定3.1 超声波测距的输入捕获实现超声波模块 HC-SR04 的时序很简单Trig 给至少 10us 高电平模块发 8 个 40kHz 脉冲Echo 拉高高电平持续时间就是声波往返时间。距离 高电平时间 × 声速 / 2。声速取 340m/s换算成厘米就是 0.034 cm/us所以距离 时间 × 0.017。用输入捕获的好处是不用阻塞延时主循环可以继续跑状态机。配置 TIM2_CH2 为上升沿捕获第一次捕获记录 CCR 值同时把极性改成下降沿第二次捕获再读 CCR差值就是高电平时间。注意定时器溢出72MHz 下预分频 72-1 得到 1MHz 计数溢出周期 65536us对应距离约 11 米足够用。// 输入捕获回调处理 Echo 上升沿和下降沿 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim-Channel HAL_TIM_ACTIVE_CHANNEL_2) { if (capture_state WAIT_RISING) { capture_rising HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2); __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_FALLING); capture_state WAIT_FALLING; } else { capture_falling HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2); if (capture_falling capture_rising) { pulse_width capture_falling - capture_rising; } else { pulse_width 0xFFFF - capture_rising capture_falling; // 处理溢出 } distance_cm pulse_width * 0.017f; __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_RISING); capture_state WAIT_RISING; } } }这段代码的关键是极性切换和溢出处理。如果只捕获上升沿不切换极性永远读不到下降沿。溢出处理容易被忽略当距离超过 3.9 米时计数值会翻转不处理就会得到错误的小距离小车会莫名其妙往障碍物上撞。3.2 火焰传感器阈值与灭火动作火焰传感器常见的是三脚模块VCC、GND、DO数字输出和 AO模拟输出。DO 可以通过电位器调阈值但一致性差我一般用 AO 接 ADC在代码里做滑动平均后再判断。火焰越近AO 电压越高或者越低取决于模块具体方向用串口打印几次就清楚了。灭火动作通常用一个小风扇或者水泵。风扇用 MOS 管驱动水泵用继电器。代码里就是检测到火焰强度超过阈值后停车、开启灭火执行器、延时 2 到 3 秒、关闭、后退一点再重新寻火。这里有个坑风扇启动瞬间电流大如果和 L293D 共用电源电压会被拉低导致电机复位或者单片机掉电。常见做法是灭火执行器单独走一路电源或者加一个大电容缓冲。3.3 状态机设计寻迹、避障、灭火怎么切换状态机是整个小车的骨架。我一般分四个状态IDLE待机、TRACKING寻迹前进、AVOIDING避障、EXTINGUISHING灭火。优先级是灭火 避障 寻迹。也就是说任何状态下检测到火焰强度超阈值立刻切到灭火没有火焰时如果超声波距离小于 20cm切避障否则按寻迹逻辑走。typedef enum { IDLE, TRACKING, AVOIDING, EXTINGUISHING } CarState; CarState state IDLE; void Car_Update(void) { float flame Flame_ReadAverage(); float dist Ultrasonic_GetDistance(); if (flame FLAME_THRESHOLD) { state EXTINGUISHING; } else if (dist 20.0f dist 2.0f) { // 2cm 以下视为无效 state AVOIDING; } else { state TRACKING; } switch (state) { case TRACKING: Track_FollowLine(); break; case AVOIDING: Motor_SetSpeed(MOTOR_LEFT, 0); Motor_SetSpeed(MOTOR_RIGHT, 0); Motor_SetDirection(MOTOR_LEFT, DIR_BACKWARD); Motor_SetDirection(MOTOR_RIGHT, DIR_BACKWARD); HAL_Delay(300); Motor_SetDirection(MOTOR_LEFT, DIR_FORWARD); Motor_SetDirection(MOTOR_RIGHT, DIR_BACKWARD); // 右转 HAL_Delay(400); break; case EXTINGUISHING: Motor_SetSpeed(MOTOR_LEFT, 0); Motor_SetSpeed(MOTOR_RIGHT, 0); Fan_On(); HAL_Delay(2500); Fan_Off(); break; default: break; } }避障逻辑我用了最简单的后退加转向没有做超声波扫描阵列。单超声波模块只能测正前方侧方障碍物看不到所以避障动作要保守一点转向角度大一些。如果场地狭窄可以加一个舵机让超声波左右扫但那就增加了一个 TIM 或者 GPIO 资源代码复杂度也上去了。4. 避坑与排查超声波抖动、L293D 发热、火焰误触发4.1 超声波读数跳变导致小车原地抽搐现象串口打印距离值在 15cm 和 200cm 之间反复跳小车在障碍物前反复前进后退像抽搐一样。原因HC-SR04 的回波信号容易受电源纹波和周围超声波噪声干扰单次测量不可信。另外如果 Trig 触发间隔太短模块还没发完上一次的回波就触发下一次也会读到错误值。解决连续测 5 次去掉一个最大值和一个最小值剩下 3 次取平均。触发间隔至少 60ms给模块足够的余量。代码里加一个距离变化率限制比如两次测量差值超过 50cm 就丢弃本次结果用上一次的值。4.2 L293D 发烫甚至保护现象跑几分钟后小车速度明显下降L293D 表面烫手严重时电机停转过一会儿又恢复。原因L293D 内部压降大持续电流接近 600mA 时功耗超过 1W不加散热片温度会飙到 100 度以上触发过热保护。另外如果 PWM 频率太低开关损耗也会增加发热。解决加一片小的铝散热片成本几毛钱。PWM 频率提到 9kHz 以上。如果电机堵转频繁换 TB6612 或者 DRV8833 这类 MOS 管驱动压降小、发热低但接线逻辑要改不能直接替换。4.3 火焰传感器被环境光误触发现象没有火源小车偶尔自己进入灭火状态风扇乱转。原因火焰传感器对红外线敏感阳光、白炽灯、甚至某些遥控器都会触发。数字输出模式下电位器没调好阈值太低。解决用模拟输出加软件阈值阈值设高一点比如 ADC 满量程的 70%。再加一个持续时间判断火焰强度连续 3 次采样都超阈值才确认。如果场地有强光干扰给传感器加一个黑色热缩管遮光只留正前方一个小孔。4.4 电机启动导致单片机复位现象小车一启动电机就重启串口打印从头开始。原因电机启动瞬间电流大电池电压被拉低如果单片机和电机共用一路 LDOLDO 输入跌破最小压差输出 3.3V 跟着掉STM32 欠压复位。解决单片机供电和电机供电分开走或者至少在 LDO 输入端并一个 470uF 到 1000uF 的电解电容。电池用动力型 18650普通容量型内阻大带电机吃力。4.5 寻迹红外对管受地面颜色影响现象白色地面能循迹黑色地面直接冲出去。原因红外对管靠反射光强度判断黑白黑色吸光接收管导通程度不够输出电平变化不明显。解决调电位器让传感器在白色和黑色上的输出电压差最大。如果还是不行降低传感器离地高度到 5mm 左右或者换用灰度传感器模块。代码里加一个动态阈值上电时先左右各采样几次取中间值作为基准。5. 进阶调参与验证用串口波形和分步测试把玄学变成确定5.1 分步验证法先让每个模块单独说话拿到这份源码包后不要一上来就整车跑。我习惯按这个顺序分步验证每一步都通过串口输出确认第一步只烧电机测试代码让左右轮正转 2 秒、反转 2 秒、停 1 秒观察 L293D 发热和轮子方向。如果方向反了交换电机两根线或者改代码里的方向电平。第二步只跑超声波代码串口每 100ms 打印一次距离用手在传感器前面来回移动看数值是否跟手。如果跳变严重先解决电源滤波再调代码滤波。第三步只跑火焰传感器 ADC串口打印原始值和电压值用打火机在安全距离外靠近记录触发时的数值范围确定阈值。第四步只跑寻迹逻辑用黑胶带在白纸上贴一条线手动拿小车沿线走看左右轮差速是否正确。第五步全部合起来先低速跑再逐步提高 PWM 上限。这个顺序看起来笨但能省下大量“整车跑不起来不知道哪里的问题”的时间。血泪经验跳过单模块验证直接整车调试最后往往要花三倍时间定位一个接反的线。5.2 用串口波形看状态切换STM32 的串口可以输出简单的文本波形比如用printf打印距离值和火焰值然后在串口助手里看数值变化。更直观的做法是输出一个字符画距离小于 20cm 打印#否则打印.每 50ms 一行。这样能直接看到小车靠近障碍物时距离值的变化趋势比看数字快得多。// 简易串口波形每 50ms 输出一行 void Debug_PrintWave(float dist, float flame) { char buf[64]; int bar (int)(dist / 5.0f); // 每 5cm 一个字符 if (bar 40) bar 40; int i; for (i 0; i bar; i) buf[i] ; buf[bar] \0; printf(D:%5.1f [%s] F:%4.0f\r\n, dist, buf, flame); }这个波形能帮你判断超声波滤波参数是否合适。如果波形边缘很毛糙说明滤波不够如果响应太慢说明滤波过度。我一般调到波形在障碍物前 30cm 开始明显下降到 20cm 时稳定就算合格。5.3 参数整定表与推荐值下面这张表是我在 1.5 米 × 1.5 米模拟场地里跑通后的一组参数可以直接抄但要根据自己的电机和电池微调。参数推荐值调整方向PWM 频率9kHz发热大就提高啸叫就降低寻迹基础速度400/1000太快丢线太慢灭火超时避障距离阈值20cm场地小就降到 15cm火焰阈值ADC 70%误触发就提高超声波滤波窗口5 次去极值平均抖动大就加到 7 次灭火延时2500ms风扇弱就加到 3500ms换向刹车延时20msL293D 发热就加到 50ms调参的顺序建议是先定 PWM 频率和基础速度再调避障距离最后调火焰阈值。因为火焰阈值受环境影响最大放在最后调可以避免反复。5.4 一个容易忽略的细节电池电压监测小车跑着跑着变慢不一定是代码问题可能是电池电压掉了。STM32 有内部参考电压或者可以用 ADC 采分压后的电池电压。在代码里加一个低电压保护电压低于 6.5V2 节 18650时停车闪灯提示换电池。这个功能在调试阶段特别有用能避免你把电池没电误判成代码 bug。从那以后我每次拿到新的小车源码包都强制先跑一遍分步验证再花 10 分钟把电池电压监测加上。这个习惯让我少熬了很多个通宵。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑