ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

STM32实验室火灾预警系统设计:多传感器融合与三级状态机实现

STM32实验室火灾预警系统设计:多传感器融合与三级状态机实现 1. 这项目是做什么的以及我在设计时定的边界实验室对我来说是个特别容易出事的地方干燥箱、烘箱、充电柜一直通着电酒精灯、易燃试剂又经常出现在同一张桌上。传统的烟感报警器等烟雾浓度上来了再响很多时候已经错过了反应窗口。所以我做这套基于 STM32F103C8T6 的实验室消防预警控制系统时核心思路不是等到着火再报而是把温度异常上升、可燃气体轻微泄漏、明火闪烁这类火情前兆抓出来做分级预警。这个开源项目目前包含三部分完整的 STM32 工程源码Keil5 HAL 库、可生产的原理图、以及一套 Proteus 仿真工程。仿真不是摆设我在做板子之前先用它验证了控制逻辑省下了不少白天调试的时间。整套系统能做到的完整能力我列在下面实时采集实验室温湿度、烟雾浓度、明火状态SSD1306 OLED 显示当前状态与告警信息三级状态机正常 / 预警告警 / 消防告警多传感器融合判定带防误报机制继电器联动控制可接排风扇、电磁阀或断电指示蜂鸣器分级报警支持手动消音与状态复位串口输出全量日志方便二次开发接入上位机。如果你正在做嵌入式课程设计、毕业设计或者想把一套预警控制逻辑真正跑通到板子上这篇内容会比较对路。我会按需求边界—硬件选型—传感器工程化—预警状态机—代码结构—仿真联调—实测踩坑的顺序把自己遇到的问题和最终方案摊开讲所有参数都是实测验证过的可直接参考。1.1 实验室火灾预警的痛点为什么不能只靠烟感传统消防报警的逻辑是检测到燃烧产物后报警但在实验室场景里会有两个尴尬第一烟感误报率不低。实验室经常有酒精灯、丙酮、氨水这类气源或者电烙铁焊接时产生的轻微烟雾普通光电烟感很容易被触发频繁误报的结果是大家在多次狼来了之后干脆把报警器当摆设。第二火灾前兆被浪费了。电器过热点往往先从局部高温开始可燃气体泄漏也会有一个浓度的爬升期明火更是先有小火苗再到蔓延。如果能在这几个阶段提前捕捉反应时间可以从秒级拉到分钟级。所以这套系统的设计核心是分级温度偏高但没着火、烟气浓度升高但没到爆点都先进入预警告警让实验室里的人有时间去检查、通风只有多条件同时确认才进入消防告警并启动联动控制。1.2 系统能力边界与开源交付内容我也得说清楚这套系统的能力边界。它不是工业级消防主机而是适合教学、演示和小型实验室改造的预警控制板。它更适合放在明确有人值守的实验室做辅助提醒和联动控制不能替代正规消防物联网设备。项目交付的内容包括交付物说明源码Keil5 工程HAL 库包含 DHT11、MQ-2、火焰、OLED、继电器、蜂鸣器、按键完整驱动原理图嘉立创EDA / AD 均可打开按模块分区标注Proteus 仿真包含 F103C8T6 主控、模拟传感器输入、逻辑验证脚本BOM 表所有元器件型号与参考价格总成本约 60 元左右这样设计的另一个好处是门槛低。手里没有板子的人可以先用仿真工程把状态机和联动逻辑跑明白再决定要不要打样有板子的人可以直接下载源码按原理图焊接后烧录十分钟内就能看到 OLED 显示实时数据。2. 硬件选型和原理图分区设计很多人做 STM32 项目喜欢先堆功能再想原理我建议反过来先定清楚要采集什么信号、要输出什么信号再画原理图。这套系统的信号链其实很清晰三个传感器进来经过主控判断输出到 OLED、蜂鸣器、继电器、状态灯和串口。2.1 主控为什么选 STM32F103C8T6以及最小系统要注意什么STM32F103C8T6 是 Cortex-M3 内核最高主频 72MHz64KB Flash、20KB SRAM。对这个系统来说三个传感器加上 OLED、继电器、串口CPU 占用率连 10% 都不到余量非常大。选它而不是选 C51 或者 Arduino原因有三第一外设多且不冲突。F103 上有 ADC、多个定时器、I2C、USART、SPI一个芯片就能把传感器采集、PWM 蜂鸣器、OLED 显示、串口日志全部搞定。第二成本低、资料多。整板 BOM 里主控只有几块钱毕设、课设选它完全不会因为预算不敢放手做。第三HAL 库和 CubeMX 支持成熟。配置时钟树、ADC、I2C 这些基础外设可以图形化生成适合从零起步的人。但最小系统有几个坑我在原理图里专门做了处理晶振8MHz 无源晶振两个 20pF 负载电容不能省且要尽量靠近 MCU 的 OSC_IN/OSC_OUT复位NRST 引脚用 10kΩ 上拉到 3.3V再接 100nF 电容到地防止杂散干扰误复位去耦MCU 每个电源引脚旁边放 100nF 电容电源入口加 10uF 钽电容或电解电容BOOT0/BOOT1各接一个 10kΩ 下拉电阻必要时通过跳线切换下载模式SWD 下载口留出 SWDIO、SWCLK、GND、3.3V 四根线调试时方便连接 ST-Link。这一套做法不是玄学都是我焊完板子发现为什么有时候程序跑飞、有时候下载失败之后慢慢补上的。最小系统看着简单但每一个小元件都有它存在的理由。2.2 传感器选型DHT11、MQ-2 与火焰传感器的搭配逻辑三个传感器分别负责温度、烟雾气体、明火互相之间是冗余和印证关系。DHT11负责温湿度。它的量程是温度 0~50°C、湿度 20%~90%RH精度并不高温度 ±2°C但作为预警系统完全够用。因为预警关注的是温度往哪个方向走、走得快不快而不是精确到小数点后一位。如果你后续想更精准直接替换成 DHT22 或 SHT30驱动层改动不到十行。MQ-2负责烟雾和可燃气体。它是半导体气敏传感器内部有个加热丝遇到可燃气体或烟雾时电导率会变化输出端电压随之上升。它检测的对象包括液化气、丁烷、甲烷、酒精、氢气、烟雾正好覆盖实验室常见的隐患源。需要注意它工作时要加热模块电流在 150mA 左右不能直接从 3.3V LDO 供电我会单独从 5V 给它供电。火焰传感器负责明火探测。模块上有个对红外光敏感的接收管波长范围大约 760~1100nm后级用 LM393 比较器输出数字信号当检测到火苗时输出低电平。模块上还有一个可调电位器用来调节灵敏度阈值。这三个传感器单独拿出来任何一个都容易误报DHT11 温度瞬间波动、MQ-2 在焊接烟雾中会飙高、火焰传感器在阳光下也可能被红外线干扰。所以我在逻辑层做的是多传感器交叉确认这个在第四章会详细展开。2.3 执行与显示继电器、蜂鸣器、OLED 的接口设计执行端我用的是五伏继电器模块驱动电路直接用 ULN2003 达林顿管阵列。选 ULN2003 而不是单个三极管是因为它内部自带续流二极管继电器线圈断电时产生的反向电动势可以直接被吸收不用我再单独加二极管布线也简单。蜂鸣器我特意选了无源蜂鸣器而不是有源蜂鸣器。无源蜂鸣器需要用 PWM 去驱动发声好处是频率可控我可以区分预警告警慢速短响和消防告警急促长响两种状态一听就知道。驱动电路就是一个 NPN 三极管蜂鸣器接在 5V 和三极管集电极之间基极串 1kΩ 电阻到 MCU 引脚。OLED 是 0.96 寸 SSD1306 屏I2C 接口地址是 0x3C。I2C 的两根线 SCL、SDA 各接一个 4.7kΩ 上拉电阻到 3.3V这个电阻不能省。之前我在仿真里不加上拉也能跑但实物里不加上拉就会出现显示一半花屏。电源方案上USB 5V 进板后分成两路一路直接给 MQ-2 加热、继电器、蜂鸣器供电另一路经过 AMS1117-3.3 给 MCU 和 OLED 供电。我特意把 MQ-2 加热丝的供电和模拟采样电路做了物理隔离不然继电器一吸合、加热棒一工作ADC 采集到的数据会像心电图一样乱跳。这个经验后面会再提到。3. 传感器采样工程化从能读到到读数可靠很多开源代码能跑但换一块板子、换一个环境就抓瞎问题基本都出在采样过于天真。传感器工程化的目标是让读数的波动可控、异常可识别、阈值有复现性。3.1 DHT11 单总线时序与读值坑DHT11 使用单总线协议一根线上既要主机发信号又要从机回数据。完整读一次数据要经过这么几步主机把总线拉低至少 18ms作为启动信号主机释放总线等待 DHT11 响应DHT11 先把总线拉低 80us再拉高 80us表示我要开始传数据了随后连续传 40 位数据每一位都是先拉低 50us然后看高电平持续时间26us 左右是逻辑 070us 左右是逻辑 140 位数据依次是湿度整数、湿度小数、温度整数、温度小数、校验和。我在驱动里用微秒级延时来采样每一位直接贴核心代码uint8_t DHT11_ReadByte(void) { uint8_t value 0; for (uint8_t i 0; i 8; i) { while (!DHT11_PIN_READ()); // 等待50us低电平结束 Delay_Us(40); // 40us后采样判断高电平长度 if (DHT11_PIN_READ()) value | (uint8_t)(0x80 i); while (DHT11_PIN_READ()); // 等待高电平结束 } return value; }这里有几个很实际的坑第一微秒延时不能用 HAL_Delay它是毫秒级且依赖中断。我改用 DWT 计数器的时钟周期做延时精度高还不受 SysTick 中断影响实测读错率几乎为零。第二两次读取之间至少要间隔 1 秒最好放到 2 秒。DHT11 官方手册就要求读取频率不能太高间隔太短会出现校验和错误读出来的数据全变 255。第三总线上拉电阻要靠近 MCU 引脚放如果传感器用杜邦线拉得比较长线间电容会直接把时序磨平我第一次用 30cm 杜邦线就出现了时好时坏的问题。读取结果需要做一次校验如果校验和错误丢弃本次数据保持上一次有效值不变并记录一次DHT11 通信异常。连续异常超过 30 秒系统会把这个传感器标记为失效后面的融合判据会自动跳过它而不是拿脏数据去触发消防告警。3.2 MQ-2 的 ADC 采样、滤波与阈值标定MQ-2 模块的 AO 口输出的是模拟电压浓度越高电压越高。但模块通常工作在 5VAO 输出最高能到 5V直接接到 STM32 的 ADC 引脚会把采样值顶在 4095 然后失真所以原理图上我做了分压AO 经过 20kΩ 和 10kΩ 电阻分压后进 PA 引脚ADC 读到的实际值要换算回传感器原始电压float sensorVoltage adcValue * 3.3f / 4096.0f * (20.0f 10.0f) / 10.0f;ADC 配置上我用了 ADC1 的一个通道采样时间拉到最长的 239.5 个时钟周期连续采 8 次取平均。采样时间长不是为了慢一点而是让内部采样电容充分充电读数稳定。阈值标定我采用了一个非常简单但在实验室环境里够用的办法。新板子通电后让 MQ-2 预热 20 分钟加热丝有个稳定过程冷态读数完全没有参考价值然后记录洁净空气中的电压值作为基线baseline。再用打火机放气、或者点一小片纸制造烟雾让传感器靠近后记录高浓度电压值highValue。预警告警阈值取preThreshold baseline (highValue - baseline) * 0.2f; fireThreshold baseline (highValue - baseline) * 0.6f;实测下来洁净空气里模块输出一般在 0.6V 到 1.0V 之间靠近烟雾源会在 1.5V 到 3.0V 之间跳动。不同模块个体差异比较大所以阈值必须上电后现场标定不能直接照抄我代码里的数字。另外我加了中值滤波连续取 5 个采样值排序取中间数。这个手段对付尖峰干扰特别好使继电器吸合、空调压缩机启动瞬间造成的 ADC 毛刺都会被滤掉。3.3 火焰传感器与多传感器融合判据火焰传感器我主要用数字输出因为模块里 LM393 已经做了阈值比较我读 PA 引脚低电平表示检测到明火。但单纯读一次低电平就报火警很容易出事。阳光里含大量红外线白炽灯、发热设备也会辐射红外传感器放在窗边基本全天都是有火状态。我的处理办法给传感器加上黑色热缩管或者金属套筒缩小接收视角只对准需要监视的区域软件里连续 10 次采样每次间隔 100ms都读到低电平才认为明火存在火焰只能作为辅助判据不能单独触发消防告警。多传感器融合的具体判据放在状态机里讲这里先给出结论任何一路传感器的值异常都先进入预警告警而不是直接告警只有两个及以上的条件同时满足并保持 3 秒才进入消防告警。这样设计的理由是任何单一传感器都可能误报温度冲击、烟味、阳光红外但两个相互独立的物理量同时异常的置信度比单路高了一个数量级。4. 预警控制逻辑状态机、防误报与联动输出控制逻辑是这套系统的灵魂。我见过很多类似项目温度一到 50°C 就响蜂鸣器看起来很灵敏实际在真实实验室里会被各种噪声打到崩溃。所以我把整个系统设计成一个三状态机配合迟滞和连续判定来抑制误报。4.1 三级状态机设计正常 / 预警告警 / 消防告警系统只在三个状态之间切换NORMAL、PRE_ALARM、FIRE_ALARM状态迁移条件如下状态进入条件状态动作NORMAL无任何异常或复位解除OLED 显示正常绿灯蜂鸣器静音PRE_ALARM温度 45°C或 30 秒温升 1.2°C或烟雾值 预警告警阈值或火焰有效持续 1 秒OLED 显示预警原因橙灯慢闪蜂鸣器“短响-间隔-短响”FIRE_ALARM温度 55°C 且烟雾 消防阈值或火焰有效 3 秒且烟雾 消防阈值或任意两个条件同时成立持续 3 秒OLED 显示“立即撤离”红灯快速闪蜂鸣器急促响继电器吸合把预警告警单独列出来的价值是显而易见的它给了人去看一眼的时间。我们实际测试时一个 60W 的加热器在封闭小空间里从温度开始爬升到烟雾出现大约有 3~5 分钟的窗口预警告警完全可以在这段时间把人叫过来处理。状态机里还有一个容易被忽略的点温度判据我加进了温升速率这一项。因为火灾早期最典型的特点是温度快速上升而固定阈值可能还没到就已经着火了。用 30 秒时间窗口内温度的变化量做判断可以比固定阈值提前几分钟触发预警。4.2 防误报机制持续判定、迟滞与传感器失效隔离防误报我用了三层机制每一层都在项目里起实际作用。第一层是持续判定。所有触发条件都要求连续多帧确认比如温度持续 3 个采样周期3 秒超过阈值才进入告警。这个做法代价很小但能过滤掉绝大多数瞬间干扰。第二层是迟滞。进入预警告警的阈值是 45°C退出预警告警的阈值是 40°C不是 44.9°C 就恢复。没有迟滞的话温度在阈值附近抖动会让蜂鸣器和状态灯不停切换那种反复在实验室里一二十次人会疯掉。第三层是传感器失效隔离。每个传感器驱动都维护一个健康状态typedef struct { uint8_t dataValid; // 1: 有效 0: 失效 uint16_t errorCount; // 连续错误次数 uint32_t lastUpdateMs; // 最近一次成功更新时间 } SensorHealth_t;当 DHT11 连续 30 次校验失败、MQ-2 ADC 值长时间不变或直接等于满量程、火焰传感器连续处于同一状态超过 5 分钟系统判定该传感器故障在 OLED 上打出错误码同时融合逻辑自动剔除故障源。这样哪怕坏了一个传感器系统也不会变成永远不报警或者永远报警。独立看门狗IWDG我也挂上了超时 4 秒不喂狗就重启。这个在调试阶段救过我很多次因为只要主循环里有一个阻塞卡死看门狗就会把系统拉回正轨OLED 上还能显示上次复位原因。4.3 联动执行细节与人工干预消防告警确认后继电器的动作是吸合 1 秒、断开 1 秒、再吸合模拟启动排风扇/电磁阀这一类负载。同时系统会持续报警直到人工干预或断电复位。为了演示安全和降低门槛我的原理图里继电器控制的是一个指示灯和一排 12V 风扇接口。如果你要控制 220V 设备继电器一定要选带隔离的型号并且外接负载的地方做好绝缘和标识不要在木桌、实验台旁边用电线裸接强电。人工干预方面做了三个按键消音键消防告警时按一下蜂鸣器静音 30 秒30 秒后如果还在告警状态则重新响测试键按下触发一次完整的蜂鸣器和继电器自检方便日常检查复位键手动清除告警回到正常状态。但保留了一个小逻辑如果复位后 10 秒内告警条件又触发说明现场真的有问题不会再次进入可复位状态强制要求断电处理。串口日志我用固定格式输出方便接上位机或者串口助手实时观察[12:03:15] T23.1C H45.2% SMOKE0.89V FLAME1 STATENORMAL [12:04:22] T47.8C H44.1% SMOKE0.92V FLAME1 STATEPRE_ALARM [12:05:03] T56.2C H43.0% SMOKE1.64V FLAME0 STATEFIRE_ALARM看到STATE变化的同时配合 OLED 和蜂鸣器状态就能很快判断整套逻辑是否符合预期。5. 代码工程结构与仿真联调代码结构我花了很大心思因为这套东西不只是给一个人看的它开源出来别人也应该能很快接手。如果你打开工程发现 main.c 里三千行谁都不愿意读。5.1 工程目录怎么分主循环怎么做到不阻塞我的工程目录是这样的LabFireGuard/ ├─ App/ │ ├─ main.c │ ├─ fire_control.c/h // 状态机主逻辑 │ ├─ menu_key.c/h // 按键和消音/复位逻辑 │ └─ log_report.c/h // 串口日志格式化 ├─ Driver/ │ ├─ dht11.c/h │ ├─ mq2_adc.c/h │ ├─ flame.c/h │ ├─ oled.c/h │ ├─ buzzer.c/h │ ├─ relay.c/h │ └─ usart.c/h └─ Bsp/ ├─ sys.c ├─ delay.c └─ iwdg.cDriver 层只负责把单个外设用对不包含业务逻辑App 层只调用 Driver 接口不直接操作寄存器。这样改一个传感器型号或者换一块主控板代价都压到最低。主循环我采用非阻塞 事件轮询模式核心代码如下while (1) { Key_Scan(); // 10ms 扫描一次按键 Buzzer_Process(); // 非阻塞蜂鸣器状态机 if (g_tick - lastSensorTick 1000) { DHT11_Read(temp, humi); MQ2_ReadAdcAvg(smokeVoltage); Flame_Read(flameState); lastSensorTick g_tick; } if (g_tick - lastControlTick 200) { FireControl_Update(temp, humi, smokeVoltage, flameState); lastControlTick g_tick; } if (g_tick - lastDisplayTick 300) OLED_Update(); }所有时间片都由 SysTick 维护的g_tick毫秒计数控制没有任何地方使用阻塞式延时。OLED 刷新时我加了忙判断SSD1306 在内部 EEPROM 写操作期间不能接收新命令否则会花屏。如果后续想上 FreeRTOS这个结构可以很平滑地拆成传感器任务/控制任务/显示任务三个独立任务g_tick换成语义更清晰的系统节拍即可。5.2 Proteus 仿真搭建步骤与验证清单仿真我用的是 Proteus 8.13。搭建过程可以按下面步骤来新建工程放置 STM32F103C8T6 芯片给 MCU 设置晶振频率 8MHz在元件属性里找到 Advanced Properties 里的晶振参数填 8M否则后续 PLL 乘法时钟会乱放置分压电阻模拟 MQ-2 的 AO 输出用一个电位器 P1 代替放置两个按键模拟火焰数字输入和消音/复位键放置 LED 和蜂鸣器代替继电器和声光输出Keil 工程 Output 选项里勾选 Create HEX File编译后把 .hex 加载到 Proteus 的 MCU 里运行仿真调节电位器模拟烟雾变化观察状态切换和 OLED 显示是否跟随。我的验证清单大约是这样的验证项操作预期结果烟雾预警电位器调到 30% 输出状态进入 PRE_ALARM蜂鸣器慢速短响烟雾消防电位器继续调到 70% 以上并保持 3 秒状态进入 FIRE_ALARM继电器触发火焰辅助火焰键按下保持 3 秒配合烟雾条件进入 FIRE_ALARM消音复位按下消音键蜂鸣器静音按下复位键回到 NORMAL串口日志打开 Virtual Terminal时间戳、温度、烟雾电压、状态字段完整仿真工程的价值不只是演示它还能在打样之前帮你发现逻辑漏洞。比如我最初把温度单独超过 55°C作为消防告警条件仿真里用电位器模拟后发现夏天密闭实验室温度完全可能到 55°C这会造成大量误报后来才把消防判据改成了多条件交叉确认。5.3 仿真联调中特别容易翻车的点仿真和实物之间有几个差异是新手最容易踩的第一DHT11 在 Proteus 里没有官方的稳定模型。社区里能找到各种版本的 DHT11 模型但时序不一定和 STM32 的微秒延时完全匹配有时候在仿真里能读到数据烧到实物就翻车。我的处理办法是把仿真聚焦在控制链路上用一个电位器和虚拟终端来代替 DHT11预设一个温度值验证状态机逻辑就够了。DHT11 的实际时序可靠性在实物上验证更靠谱。第二Proteus 里没有真实传感器的响应延迟。MQ-2 在实际上电预热需要 20 分钟以上仿真里电位器一拧电压就变了这个时间差很容易让人误以为这系统反应真快。看板子的时候才明白传感器本身的响应才是系统响应的大头。第三ADC 分压比例必须和实物原理图一致。仿真里看到的 ADC 原始值经过我代码里的分压系数换算后应该能在虚拟终端打印出接近真实电压的数字。如果这里对不上实物调试时你会怀疑人生。第四Proteus 加载 .hex 文件时如果工程路径里面有中文或者特殊字符偶尔会出现加载后程序不跑的现象。工程路径全英文能少踩很多莫名其妙的坑。6. 实测踩坑记录与优化方向代码能编译、仿真能跑和板子能稳定工作之间还隔着很远。我把实物调试中遇到的三个典型问题拿出来说说这几条经验对任何 STM32 传感器项目都通用。6.1 实物调试中我遇到的三类问题问题一DHT11 读数全为 255。第一次焊好板子OLED 上温度湿度全是 255蜂鸣器直接乱响。排查后发现不是时序代码问题而是 DHT11 的 DATA 引脚上拉电阻焊在了一个已经损坏的焊盘上。用示波器看总线波形启动信号之后完全没有任何响应折腾了半小时。教训就是单总线器件的排查第一件事要用示波器看波形别靠猜。问题二继电器动作瞬间 ADC 数据跳动。MQ-2 的采样值在继电器吸合时会突然跳高导致系统偶发误报。原因是继电器和 MQ-2 共用 5V 电源继电器线圈吸合电流冲击把电源拉了一下。我在继电器电源输入端加了 100uF 电解电容和 104 陶瓷电容同时把 ADC 采样放在继电器动作完成 10ms 之后问题彻底消失。问题三OLED 不定时花屏。排查后是 I2C 线太长、上拉电阻值太小导致波形过冲。把上拉电阻从 1kΩ 换成 4.7kΩ并把 I2C 时钟从 400kHz 降到 200kHz 后连续跑了一天一夜再没出现花屏。这三个问题的共同点是都不是逻辑问题而是电源完整性、信号完整性问题。做嵌入式调试很容易钻进代码哪里写错了的牛角尖但真正的问题往往在原理图和布线上。6.2 后续可以扩展的方向这套系统目前是一个能完整跑通的最小闭环我给它留了几个升级空间加 ESP8266 或 4G 模块报警信息直接推送到手机。代码里已经有串口日志输出只需要在 USART 上接一个 WiFi 透传模块再写一个简单的 MQTT/HTTP 上报函数加温度多点采集用 DS18B20 挂到一根总线上每个实验工位一个探头融合判据会更立体加 BootLoader 远程升级这样不开盖也能更新固件显示界面升级成 LVGL 触摸屏把状态机、实时曲线、历史记录全部可视化。我个人建议的路线是先把这套系统完整复现一遍跑通三级状态机再根据自己的实验室环境去修改传感器数量和报警阈值。传感器选型、分压电阻、滤波逻辑这些部分换任何型号都逃不开信号采集要稳、判据要抗干扰、输出要可验证这三件事。能把这套思路吃透这套开源项目才算真正发挥了价值。
返回列表