数学建模竞赛火灾报警题:Matlab数据处理与阈值优化

发布时间:2026/10/22 2:37:32
数学建模竞赛火灾报警题:Matlab数据处理与阈值优化 简介一份来自西南交通大学的2022年五一数学建模竞赛C题‘火灾报警系统问题’一等奖论文采用Matlab完成建模分析适合数学建模竞赛参赛者、指导教师以及想掌握可靠性评估与综合评价方法的在校学生。论文聚焦城市火灾报警系统误报率偏高、消防资源浪费等现实问题运用TOPSIS模型判断6月1日至18日真实火灾起数为120起并据此完成八类火灾探测器可靠性排序又以最小二乘法建立多元线性回归模型对15个大队的报警信号真实性逐一评价再通过模糊综合评价模型识别出综合管理水平较低的N、J、E三个大队提出针对性改进建议。内容覆盖问题背景、模型假设、符号说明、模型构建与求解、结果分析等完整环节结构规范可供参考学习。资源共1个PDF文件压缩包约916KB已有2233人浏览学习对于希望借鉴一等奖论文框架、熟悉TOPSIS和模糊综合评价模型实现细节的读者具有直观的示范价值。1. 火灾报警赛题为什么值得认真做先明白评委在评什么火灾报警系统听起来是传感器数据处理题本质上却是在“漏报”和“误报”之间找平衡的决策问题。一次漏报意味着火势蔓延后才报警一次误报则会让现场人员对报警器彻底失去信任这个代价往往比不装系统更严重。某高校参赛队能在当年的五一赛C题里拿到一等奖靠的并不是神秘算法而是把温度、烟雾浓度这些普通传感器数据做成了有层次的特征再用 Matlab 实现了一套可解释、可调参的报警决策链路。这篇笔记适合两类人第一次参加数学建模竞赛、想找稳定得分点的队伍以及做传感器报警类系统想压低误报率的工程师。接下来我把建模链路、可直接复现的 Matlab 代码、以及参数调整里容易翻车的地方一次讲清楚。2. 五一赛C题拆解从传感器数据到报警决策的完整链路2.1 赛题到底要你输出什么报警结果之外的三个隐含要求拿到题目先别急着写代码。火灾报警赛题常见的输入格式是多组时间序列至少包含温度和烟雾浓度部分题目还会给一氧化碳浓度或风速。输出表面上是“某一时刻触发报警”但评委真正在看的是三个隐含指标。第一个是报警及时性也就是从起火到系统发出警报之间的时间差。这个值越小越好但小到极致往往意味着极度灵敏正常环境里随便飘点烟就会误报。第二个是误报率没有火情时系统不应报错评委会构造一些纯干扰场景来测你。第三个是模型的泛化能力训练数据里得分再高换一段同样物理过程但噪声不同的数据结果不能崩塌。所以数据预处理占了我整个赛题工作量的六成。时间戳对齐是第一关多个传感器采样频率不一致时必须先插值到同一个时间轴上缺失值和异常尖峰是第二关传感器偶尔丢包会产生 0 值或跳变这些点不处理会直接把特征计算带偏量纲差异是第三关温度在 20 到 100 度之间波动烟雾浓度可能只有 0 到 1直接相加会让温度主导整个评分后面章节会专门讲怎么处理。2.2 建模主线的两种思路规则阈值与综合评分第一次做这类题的人最容易掉进“温度超过 50 度就报警”的简单规则里。单阈值规则的好处是快、好解释坏处是抗噪声能力极差。气温传感器受阳光照射或者加热设备启动温度完全可以瞬间冲高这时候规则就会误报。更精细一点的规则会加上持续时长判断比如“连续 30 秒超过阈值才报警”这已经能解决一部分毛刺问题但本质上还是只看了一个维度。我推荐的获奖级主线是综合评分法。把温度绝对值、温度变化率、烟雾浓度绝对值、烟雾浓度变化率这四类特征分别归一化然后按权重加总得到一个 0 到 1 之间的评分再对评分做连续确认窗口判断。这样设计的好处是物理意义清晰温度高但是变化平稳可能是环境正常波动温度不高但温度和烟雾都在快速上升这才是典型的火灾早期特征。顺带一提变化率特征的重要性往往被低估。火灾初期的信号特征不是“数值高”而是“上升快”。温度从 22 度升到 23 度看起来不吓人但如果在 30 秒内完成这就是强烈预警信号。只盯着绝对值阈值往往会错过最早的十几秒黄金窗口。2.3 用一段 Matlab 代码把最小链路跑通这里给一段可以直接运行的 Matlab 最小链路输入是一个 N 行 3 列的矩阵 data三列分别是时间、温度、烟雾浓度。这段代码完成了特征提取、加权评分和连续确认报警三个核心步骤。% 最小可行火灾报警链路 % data: N x 3 矩阵, 三列分别为时间、温度、烟雾浓度 temp data(:, 2); smoke data(:, 3); % 特征1、2: 温度与烟雾浓度的变化率 % 注意 diff 的结果比原序列短1, 前面补0保持对齐 dTemp [0; diff(temp)]; dSmoke [0; diff(smoke)]; % 特征3、4: 绝对值特征, 用min-max归一化到[0,1] % 注意用 eps 防止除零 normTemp (temp - min(temp)) / (max(temp) - min(temp) eps); normSmoke (smoke - min(smoke)) / (max(smoke) - min(smoke) eps); % 变化率特征也归一化 normDTemp (dTemp - min(dTemp)) / (max(dTemp) - min(dTemp) eps); normDSmoke (dSmoke - min(dSmoke)) / (max(dSmoke) - min(dSmoke) eps); % 加权评分: 温度变化率权重最高, 绝对值温度次之 score 0.30 * normDTemp 0.25 * abs(dTemp)./ (max(abs(dTemp)) eps) ... 0.25 * normSmoke 0.20 * abs(dSmoke) ./ (max(abs(dSmoke)) eps); % 连续 confirm 个采样点都超过阈值才报警 threshold 0.6; confirm 3; alert false(length(score), 1); for i confirm:length(score) if all(score(i-confirm1:i) threshold) alert(i) true; end end % 输出第一次报警时刻 firstAlertTime data(find(alert, 1), 1); fprintf(首次报警时刻: %.1f 秒\n, firstAlertTime);这段代码里的权重不是随便拍的。温度变化率权重最高是因为火灾早期温度上升速率是最稳定的物理信号烟雾绝对值权重次之是因为烟雾浓度受通风影响大单看绝对值容易抖动而烟雾变化率的权重最低因为烟雾传感器本身噪声大变化率会被噪声放大。threshold 参数决定灵敏度0.6 意味着四个特征里至少有两到三个同时走强才会触发。confirm 参数是连续确认窗口这里设 3意思是连续 3 个采样点都超阈值才算数能过滤掉单点毛刺。如果传感器采样率是 1Hzconfirm3 就等价于“连续 3 秒确认”。采样率变成 10Hz 后这个值就要相应调到 30 左右后面第 4 章会详细展开。3. 用Matlab做传感器仿真与特征提取先让数据会说话3.1 生成一份带噪声的火灾演进数据赛题如果没有给配套仿真数据或者你想验证模型在更多场景下的表现就需要自己生成带物理意义的仿真数据。常见做法是用一阶惯性环节来描述火灾升温过程温度不会瞬间跳到最高点而是按指数规律逼近稳态值这个逼近快慢由时间常数决定。% 仿真参数 Fs 1; % 采样率: 每秒1个点 t (0:599); % 共600秒, 10分钟数据 % 背景基线: 22度附近缓慢波动 传感器噪声 baseTemp 22 2*sin(t/50) 0.3*randn(size(t)); baseSmoke 0.02 0.01*randn(size(t)); % 火灾信号: 从 t300 秒开始, 指数趋近上升 fireStart 300; fireTemp 6.0 * (1 - exp(-(t-fireStart)/18)) .* (t fireStart); fireSmoke 0.95 * (1 - exp(-(t-fireStart)/30)) .* (t fireStart); % 叠加得到最终观测序列 temp baseTemp fireTemp; smoke baseSmoke fireSmoke;这里两个时间常数值得说明。温度时间常数取了 18 秒表示温度上升较快但仍有惯性烟雾时间常数取了 30 秒表示烟雾扩散比温度慢半拍。它们体现了火灾过程中“温升在前、烟雾随后”的物理顺序这会让你的模型贴近真实场景。点火时刻取 300 秒是故意的前 300 秒是纯背景数据300 秒后火灾信号逐渐叠加这样在生成的数据里你能明确知道真值在哪。随机噪声用了 randn是标准正态分布幅度上温度噪声 0.3 度、烟雾噪声 0.01比例参照真实低成本传感器。3.2 提取温度变化率与烟雾浓度斜率两个必须做对的特征得到原始序列后不要直接送进模型先做平滑再求变化率否则 diff 操作会把传感器噪声放大成很大的毛刺。这里用滑动平均滤波器窗口取 5 个点。% 滑动平均去毛刺, 窗口为5 win 5; k ones(win,1) / win; tempF filter(k, 1, temp); smokeF filter(k, 1, smoke); % 求平滑后的变化率 % 注意: filter 输出与输入等长, diff 后补0 rateTemp [0; diff(tempF)]; rateSmoke [0; diff(smokeF)];有人会问为什么不用 filter 而是直接用 conv两者区别在于边界处理。filter 是从第一个点开始逐点滤波开头几个采样点会有一段暂态响应conv 用 same 参数可以让输出等长但边界点会补零造成不真实的下拉。我一般会在前 20 个点丢弃不用只分析稳定之后的数据这样可以规避两种滤波方式的边界效应。变化率特征在代码里的体现就是 diff 之后补 0。这行代码看起来简单坑在于如果不补 0后面所有数组长度都不匹配plot 会直接报错而用索引取特征时还会错位。凡是涉及多特征拼接的地方先把长短对齐是铁律。3.3 特征可视化先看图再建模很多参赛队拿到数据直接开跑优化算法这是本末倒置。特征到底有没有区分度画图看一遍比任何统计指标都直观。把原始序列、平滑序列、变化率画在三张子图里一眼就能看出火灾起始位置两侧特征值是否有清晰的跳变。figure; subplot(3,1,1); plot(t, temp, b, t, tempF, r, LineWidth, 1); legend(原始温度, 平滑温度); ylabel(温度/度); subplot(3,1,2); plot(t, smoke, b, t, smokeF, r, LineWidth, 1); legend(原始烟雾, 平滑烟雾); ylabel(烟雾浓度); subplot(3,1,3); plot(t, rateSmoke, k, LineWidth, 1); hold on; yline(0.02, r--); xlabel(时间/秒); ylabel(烟雾变化率);可视化往往能提前暴露两个问题。第一个是特征重叠如果火灾前后特征值大量交叠说明这个特征本身区分度不够后面权重调得再好也白搭。第二个是噪声尺度变化率曲线如果看起来像刺猬说明平滑窗口太小需要加大 win 值而不是后面硬调阈值。我画图的习惯是先画变化率因为它是判断“火灾是否在早期”的核心。温度绝对值要滞后到数值明显升高才看得出来而变化率在温升刚开始时就抬起来了。这就是为什么很多获奖方案把变化率特征的权重放在第一位。4. 从阈值到自动寻优三个必调参数与粒子群优化4.1 报警阈值灵敏度与误报率的跷跷板报警阈值是整套系统里最敏感的参数没有之一。阈值调低模型会更早抓住火灾苗头但正常环境稍微有点烟雾就可能越线阈值调高误报少了报警时间也会往后拖。赛题评分里这两个指标往往是加权组合所以阈值必须当成一个优化变量来对待不能拍脑袋定。我见过不少队伍在论文里写“经实验确定阈值为0.6”但问起为什么是 0.6答不上来。评委恰恰喜欢追问这个问题。正确做法是先把阈值从 0.3 到 0.9 扫一遍记录每个阈值下的报警延迟和误报次数画出曲线然后选择一个拐点处的值。这个曲线一定会呈现明显的“此消彼长”形态。优化目标函数怎么设计也有讲究。常见的做法是把报警延迟和误报次数量纲统一延迟以秒为单位误报次数就是整数两者直接相加会让延迟的主导性太强。我会先把延迟归一化到 0 到 1再乘权重系数误报次数也做同样处理这样两个指标在目标函数里的地位是公平的。4.2 连续确认时间窗口为什么比单一阈值更稳单一阈值最大的问题在于它只看了“这一刻”的评分。传感器偶尔的电磁干扰、烟雾传感器的零点漂移都会产生一个单点高分。连续确认窗口的作用是要求评分必须持续超过阈值一段时间才触发报警等于给决策加了惯性。这个参数必须和采样率挂钩。代码里 confirm 是点数但人话里它应该是一个时间长度。采样率 1Hz 时confirm3 是 3 秒采样率 10Hz 时confirm30 才是 3 秒。很多人换了数据后忘了换算导致确认窗口实际缩短了 10 倍误报率直接起飞。那窗口设多长合适我的经验区间是 2 到 8 秒。太短起不到滤毛刺的作用太长会把真实火灾的报警时间硬生生拖后。窗口的微调可以直接在仿真数据上试但要记得在赛题给的原始数据上也验证一遍因为两套数据的噪声特性可能完全不同。4.3 用粒子群在 Matlab 里自动找最优参数组合当参数之间相互影响时手动调参效率太低。比如 threshold 和 confirm 之间存在耦合confirm 越大threshold 就可以适当降低两者对误报率的抑制效果有重叠。这时候我会直接用粒子群算法做联合寻优Matlab 自带的 particleswarm 函数够用不需要额外工具箱。% 目标函数: 把延迟和误报次数加权惩罚 function cost alarmCost(x, tempF, smokeF, fireStart) threshold x(1); confirm round(x(2)); % 窗口必须是整数 % 计算综合评分(权重固定) dTemp [0; diff(tempF)]; dSmoke [0; diff(smokeF)]; normTemp (tempF - min(tempF)) / (max(tempF) - min(tempF) eps); normSmoke (smokeF - min(smokeF)) / (max(smokeF) - min(smokeF) eps); score 0.30 * abs(dTemp)/max(abs(dTemp)eps) ... 0.25 * normTemp ... 0.25 * normSmoke ... 0.20 * abs(dSmoke)/max(abs(dSmoke)eps); alert false(length(score), 1); for i confirm:length(score) if all(score(i-confirm1:i) threshold) alert(i) true; end end firstAlertIdx find(alert, 1); if isempty(firstAlertIdx) % 完全没报警: 给一个很大的惩罚 cost 1000; return; end delay max(0, firstAlertIdx - fireStart); % 报警延迟(秒) falseAlarm sum(alert(1:fireStart)); % 起火前误报次数 % 归一化后再加权 delayNorm delay / 300; cost 0.6 * delayNorm 0.4 * falseAlarm; end % 主程序: 粒子群寻优 lb [0.3, 1]; % 阈值下限, 确认窗口下限 ub [0.9, 10]; % 阈值上限, 确认窗口上限 options optimoptions(particleswarm, ... SwarmSize, 30, MaxIterations, 60, Display, final); [xBest, fBest] particleswarm((x) alarmCost(x, tempF, smokeF, 300), ... 2, lb, ub, options); fprintf(最优阈值: %.3f, 最优确认窗口: %d, 目标值: %.4f\n, ... xBest(1), round(xBest(2)), fBest);这段代码里 lb 和 ub 的边界设定有实际含义。阈值下限 0.3 是防止模型过于灵敏到失去意义上限 0.9 是防止阈值高到永远不报警。confirm 窗口取 1 到 10 秒覆盖了上一节说的合理区间也保留了向上探索的空间。SwarmSize 设 30 是一个平衡点。粒子太少容易陷入局部最优粒子太多优化时间成倍增加而火灾报警场景下 60 次迭代足够找到稳定结果。如果做参数敏感性分析可以把 SwarmSize 分别设 20、30、50 各跑一次结果差异在 5% 以内就说明寻优稳定。需要提醒的是particleswarm 依赖全局优化工具箱如果现场 Matlab 版本没有这个函数退路是用网格搜索粗扫一遍再用 fminsearch 精调。代码结构和上面的 alarmCost 完全一样只是外层循环换成 for 嵌套遍历参数范围。5. 火灾报警赛题里的避坑与排查五条血泪经验记录5.1 翻车记录一归一化后模型反而漏报现象把所有特征做 min-max 归一化后模型在火灾初期迟迟不报警比原始阈值法的报警时间晚了二十多秒。原因min-max 归一化用的是全局最大值。火灾后期温度冲到很高而初期温度只比背景高 1 度左右归一化后初期的温度变化被压缩到接近 0等于把最关键的早期信号抹掉了。这属于典型的“预处理过度”。解决绝对值特征保留物理量纲不参与全局归一化只在加权求和后用常数尺度统一变化率特征本身已经是相对量可以归一化也可以不归一化。我最后采用的是不归一化绝对值特征只对评分结果做一次 min-max 映射到 0 到 1。5.2 翻车记录二仿真数据自洽导致虚假满分现象模型在自己生成的仿真数据上达到 100% 正确率换成赛题给的原始数据后准确率掉到六成。原因自己生成的仿真数据用了同一套物理参数模型相当于“背了答案”。噪声分布、时间常数、背景波动全部一致模型的决策边界被完美贴合这本质上是过拟合。解决仿真数据只用来调参最终评估必须落到赛题原始数据上。更严格的做法是把仿真分成 A、B 两组A 组参数用于生成训练数据B 组换时间常数和噪声幅度用于验证两组数据在模型调参阶段完全隔离。这条经验帮我避开了很多表面满分的陷阱。5.3 翻车记录三for 循环跑完了人还在等现象6000 个采样点、四个特征、两层循环的代码跑了 20 分钟还没出结果评审现场非常尴尬。原因特征提取时用了大量 for 循环逐点计算没有利用 Matlab 的向量化能力。Matlab 的循环效率远低于 C 系语言但对矩阵运算做了深度优化用循环等于主动放弃了这个优势。解决把所有逐点计算改成矩阵运算。diff、filter、cumsum、逻辑索引都是向量化的原本 20 分钟的循环代码改写后 3 秒跑完。最典型的改写是“连续确认窗口”判断用 movmean 或循环加逻辑索引配合卷积核就能一次算出。5.4 翻车记录四粒子群结果每次跑都不一样现象同一份代码、同一组数据连续跑三次粒子群寻优得到的 threshold 最优值每次都有明显差异。原因粒子群算法本身有随机性包括粒子初始位置和速度扰动。如果不固定随机种子每次优化都会在不同的起点上探索结果自然不同。解决在调用 particleswarm 之前写一行 rng(2022)把随机数生成器种子固定下来。这样每次运行结果可复现论文里的参数表才是稳定的。这个习惯对所有涉及随机初始化的算法都适用包括 k-means、神经网络、模拟退火。5.5 翻车记录五只看温度特征让传感器位置坑了模型现象模型在 A 组数据上表现很好换到 B 组数据后误报率飙升排查发现 B 组数据的温度传感器安装在靠近空调出风口的位置。原因不同位置的传感器采集到的温度特征差异极大。出风口附近温度波动剧烈变化率特征天然偏高模型会把这种波动误判为火灾前兆。这是典型的传感器位置偏差问题。解决引入烟雾浓度的变化率作为“决策否决项”。火灾时温度和烟雾变化率会同向上升空调扰动通常只有温度变化而烟雾不动。具体实现是在评分函数里加一条规则如果烟雾变化率低于阈值即使温度评分很高也不触发报警。这条规则牺牲少量及时性换来了显著更低的误报率。6. 用滑动窗口做报警提前量验证一晚调出可靠模型的实战技巧参数寻优之后还有一步关键验证很多人直接跳过那就是报警提前量的分布验证。粒子群给出的是一组最优参数但它只告诉你在当前数据集上延迟和误报的加权最小没有告诉你模型在不同时间点起火的鲁棒性。我用滑动窗口来验证把测试数据按时间切成多个窗口每个窗口分别模拟一次“当前时刻起火”看模型从起火到报警的延迟有多稳定。具体做法是设定一个长度 120 秒的窗口从数据第 100 秒开始每隔 30 秒滑动一次。每个窗口内将窗口后部视为火灾起点运行报警链路记录报警时刻并计算提前量。这样能收集到几十个提前量样本画出直方图既能看到平均水平也能看到最差情况。% 滑动窗口验证报警提前量 winLen 120; step 30; results []; for startIdx 100:step:(length(temp) - winLen) segTemp temp(startIdx:startIdxwinLen-1); segSmoke smoke(startIdx:startIdxwinLen-1); % 模拟在窗口后半段起火 fakeFireStart round(winLen * 0.7); % 用固定最优阈值和窗口重新跑报警链路 alert runAlarm(segTemp, segSmoke, optThreshold, optConfirm); firstAlert find(alert, 1); if ~isempty(firstAlert) leadTime (firstAlert - fakeFireStart) * Fs; results(end1) leadTime; end end fprintf(平均提前量: %.1f 秒\n, mean(results)); fprintf(最差提前量: %.1f 秒\n, min(results));如果发现最差提前量是负值说明模型在某些片段内报警晚于真实起火时刻这时候要么降低阈值要么缩短确认窗口。如果平均提前量非常充裕但误报率偏高则反向调整。这套验证方法的价值在于它逼你面对“模型在多点起火场景下是否稳定”这个最真实的问题而不是在一组固定数据上自嗨。从那届赛题之后我养成了一个习惯任何报警类模型不画提前量分布图之前绝不提交结果。这张图比单一准确率指标更能暴露模型的脆弱面。希望这个习惯也能帮正在赶赛题的你少走一段弯路。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

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

尧图内容编辑团队

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

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

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

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

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

网站改版的5个关键决策

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

获取专属建站方案

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

立即免费咨询