ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

火电厂预测系统实战:负荷与脱硝三线并行的数据清洗与模型选型

火电厂预测系统实战:负荷与脱硝三线并行的数据清洗与模型选型 简介针对火电厂脱销预测的MATLAB应用资料面向火电厂运行人员、环保工程师及电力数据分析学习者重点解决NOx排放预测与脱硝系统优化控制问题。资料基于历史运行数据建立预测模型覆盖发电负荷、燃烧条件、脱硝系统状态等关键变量可用于估算烟气温度、流量等参数辅助SCR/SNCR还原剂注入量调节兼顾环保合规与运行成本。包体共2个文件压缩包约325KB。其中1个xlsx实验数据文件记录机组历史运行参数是建模与验证的基础1个m脚本实现数据预处理、模型建立、训练与结果评估流程便于扩展时间序列或神经网络等预测算法。目前已有99人学习浏览适合需要快速上手MATLAB脱硝预测建模的读者。通过学习可掌握从数据清洗、特征工程到模型评估的完整思路并可直接修改脚本适配自有电厂数据缩短项目开发周期。1. 火电厂预测系统的真实边界负荷、脱硝与电气三线并行电厂预测这个标题很容易被误解成做个模型、预测一个数。实际做过就会知道一座 600MW 机组的预测项目至少同时扛着三件事电气侧的超短期负荷预测、脱硝系统的入口 NOx 与喷氨量预测、以及贯穿两者的辅机工况预测。这里标题里写的脱销应该是脱硝——火电厂烟气脱硝系统SCR 或者 SNCR 那套环保装置行业内手写纪要时经常混用。三者的共同点在于都是时序预测但数据来源、滞后时间、失败后果完全不同。负荷预测错了顶多是考核偏差脱硝预测错了可能意味着氨逃逸超标、催化剂堵塞甚至环保平台上的超标记录。这篇内容面向的是准备接类似项目的从业者我按数据接入、模型选型、分场景落地的顺序把一套能归档复用的做法拆开讲。2. DCS 点位选型、采样对齐与迟滞特征构造2.1 点位清单是预测项目的第一个交付物火电厂的预测数据基本都来自 DCS 或 SIS 系统。很多人一上来就拉几百个点位丢给模型结果训练出来的模型在运行工程师眼里就是黑盒根本不敢用。我一般会把点位分成四类控制量、状态量、环境量、导前量。控制量是风门开度、给煤机转速、喷氨调节阀开度状态量是炉膛负压、主蒸汽压力、烟温环境量包括环境温度、湿度、风速导前量是那些在目标变量变化之前就有反应的点位比如总风量变化会比 NOx 变化早几十秒。下面这个表格是一个 300MW 机组典型的最小点位集按优先级排序点位宁缺毋滥。信号分类点位名称采样频率用途优先级电气侧有功功率、发电机端电压、负荷率1s负荷预测输出与工况分段P0电气侧环境温度、气压、风速1min负荷预测外生变量P1脱硝侧省煤器出口 NOx、氨喷射流量1s脱硝预测目标与特征P0脱硝侧SCR 入口烟温、入口烟气流量1s脱硝反应条件P0燃烧侧总风量、给煤量、一次风压1sNOx 生成导前信号P0燃烧侧磨煤机电流、出口温度1s制粉系统延迟特征P1状态侧主蒸汽压力、炉膛负压1s工况辨识P2点位表定下来之后第一步不是建模而是用 SQL 把 DCS 历史库或者关系库里的数据捞出来做质量检查。常见问题是零点漂移、信号卡死、检修期数据缺失。检修期的数据必须剔除否则模型会学到停机状态这种无意义的模式。2.2 时序对齐与清洗先用 SQL 做粗清洗再用 pandas 精修DCS 的原始采样虽然是 1s但做预测时通常聚合到 1min 或 5min预测步长选 15min 或 1h 比较合适。太短的步长给运行人员的操作时间不够太长的步长误差累积明显。数据对齐的代码我习惯写成独立脚本方便每次上线前重复跑。import pandas as pd # 假设从 DCS 历史库取出的原始数据time 为时间戳value 为测点值 df pd.read_csv(dcs_raw.csv, parse_dates[time]) df.sort_values(time, inplaceTrue) # 去除检修期数据主蒸汽压力持续低于限值视为停机 df df[~((df[main_steam_press] 2.0) (df[time].dt.hour 6))].copy() # 剔除跳变与卡死信号变化率超过物理上限的置空 df[noise_flag] df[stoichiometric_air].diff().abs() 2000 df.loc[df[noise_flag], stoichiometric_air] None # 1s 重采样到 1min 均值空值限幅前向填充 df.set_index(time, inplaceTrue) resampled df[[scr_no, ammonia_flow, total_air, coal_flow]].resample(1min).mean() resampled resampled.interpolate(methodlinear, limit5, limit_directionboth)参数说明main_steam_press 2.0是机组停运判据不同机组阈值不一样建议从历史数据里找负荷最低那段的压力值。limit5表示最多连续填充 5 个缺失点超过 5 分钟的数据缺失说明 DCS 本身有问题不应当靠插值造假。重采样到 1min 均值能有效抑制 DCS 信号的电气噪声比单纯用原始 1s 数据训练模型收敛更快而且不容易震荡。2.3 迟滞特征脱硝系统响应比你想象中慢火电厂的脱硝系统是一个大惯性系统。从给煤量变化到省煤器出口 NOx 变化中间要经过磨煤机、炉膛燃烧、烟气流动这些环节典型滞后时间在 60~180 秒之间。如果用当前时刻的给煤量去预测 15 分钟后的 NOx模型会很难学因为在时序上这两个变量根本没对齐。# 对导前量做滞后处理形成滞后特征列 def shift_features(df, cols, shifts(3, 5, 10, 15)): df_out df.copy() for col in cols: for s in shifts: df_out[f{col}_shift_{s}m] df[col].shift(s) return df_out # 滞后 3/5/10/15 分钟匹配脱硝系统的响应窗口 lagged shift_features(resampled, [total_air, coal_flow, main_steam_press])这里 shift 的单位是分钟shift_3m表示取过去第 3 分钟的信号作为当前特征。运行工程师平时手动调喷氨时记住的工况变化后 3~5 分钟看 NOx在特征层面就是用这些滞后列来模拟的。实际上我个人验证过光是把总风量和给煤量做 3、5、10、15 分钟四档滞后XGBoost 的 R2 就能提升 0.05 左右效果好于加复杂模型结构。3. 时序模型选型从 XGBoost 基线到 LSTM 的参数清单3.1 先跑 XGBoost 基线模型原因是可以对齐领域知识很多团队一上来就上 LSTM最后被特征维度、训练稳定性、上线部署折腾到崩溃。我的习惯是先搭一个 XGBoost 基线理由有三个树模型对特征尺度不敏感风量、压强、温度这些不同量纲的特征可以直接喂不用做标准化训练快迭代特征方便XGBoost 的 feature importance 可以直接拿给运行工程师看他们认可给煤量重要这种结论但不认可一个 128 维的隐藏向量。import xgboost as xgb dtrain xgb.DMatrix(X_train, labely_train) params { max_depth: 6, eta: 0.05, subsample: 0.8, colsample_bytree: 0.7, objective: reg:squarederror, eval_metric: mae, } model xgb.train(params, dtrain, num_boost_round300, early_stopping_rounds20)参数说明max_depth6适合特征量在 30~50 个区间的场景再深容易过拟合到检修数据上subsample0.8和colsample_bytree0.7是减小树之间相关性的常规配置火电厂数据季节性不强这两个参数按 0.7~0.9 之间调即可early_stopping_rounds20必须配因为时序问题的验证集不随机很容易在 100 轮之后开始过拟合历史工况。3.2 在滑窗验证下评估而不是随机 K 折火电厂时序预测最容易犯的错是用随机交叉验证来决定模型参数。负荷和 NOx 都有很强的自相关性昨天的数据几乎和今天一样随机切分会让模型偷偷看到未来信息。应该用 walk-forward 验证把时间序列按先后顺序切成训练段和测试段一步步往后滚动。from sklearn.metrics import mean_absolute_error horizon 15 mae_list [] for test_start in range(5000, len(X_data), 500): train_mask np.arange(0, test_start) test_mask np.arange(test_start, test_start horizon) pred model.predict(xgb.DMatrix(X_data[test_mask])) mae_list.append(mean_absolute_error(y_data[test_mask], pred)) print(fWalk-forward MAE: {np.mean(mae_list):.3f})这里的horizon15表示每次验证未来 15 个采样点test_start每 500 步滑动一次覆盖多种工况的连续性变化。评估结果比随机 K 折的 MAE 高 20%~40% 是正常的不要惊讶。这个值才是上线后的真实预期误差。3.3 LSTM 的输入构造与训练边界当特征序列长度超过 30 步、且依赖长周期工况记忆时XGBoost 会显得力不从心。典型场景是磨煤机切换、吹灰动作这些持续十几分钟的事件对 NOx 的影响在时间上呈长尾分布。此时我才会切到 LSTM。import torch import torch.nn as nn class NOxLSTM(nn.Module): def __init__(self, n_features, hidden_size64, num_layers2): super().__init__() self.lstm nn.LSTM(input_sizen_features, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue) self.fc nn.Linear(hidden_size, 1) def forward(self, x): out, _ self.lstm(x) # out: (batch, seq_len, hidden) return self.fc(out[:, -1, :]) # 只取最后一个时间步的输出关键参数解释seq_len取 30~60 之间对应 30~60 分钟的历史窗口hidden_size64对于单目标预测足够加大到 128 对精度的提升很小但训练时间翻倍num_layers2是我在电厂数据上验证过的经验值太多层容易梯度消失并且小样本下训练不稳定。训练时学习率从 1e-3 开始每 10 轮减半batch size 用 64 或 128。需要警惕的是LSTM 对数据长度很贪婪。几百兆的 DCS 历史数据看起来很多但清洗完、按工况筛掉停机段之后有效样本可能只有几万条这个体量下 LSTM 未必比 XGBoost 好。先跑基线再做复杂模型永远是正确顺序。4. 分场景落地电气负荷预测与脱硝喷氨预测的配置差异4.1 电气侧负荷预测必须加上计划值和气象外生变量电气负荷预测和脱硝预测最大的不同在于负荷变化不完全由物理系统自身决定还取决于电网调度曲线。DCS 里的有功功率受 AGC 指令控制运行工程师关心的是未来 1~4 小时负荷会怎么走这直接决定他们要提前备多少煤、调整哪些磨煤机组合。纯时间序列模型能看到负荷的历史趋势但看不到明天的调度计划。我的做法是把日前计划负荷作为硬特征输入。电厂一般会有调度下发的计划曲线这个值对预测未来 4~24 小时帮助极大。特征里还需要加入星期几工作日和非工作日压负荷差异明显、环境温度空调负荷、风速风电大发时火电会被压负荷。下面是一个典型的特征表特征来源语义过去 7 天同时段平均负荷历史库周期性基准日前调度计划值调度系统未来趋势硬约束环境温度、湿度、风速气象系统用电需求外因高压缸调节级压力DCS实时工况导前信号当前负荷率DCS机组出力水平模型输出是未来 1h/4h 的负荷曲线用 MAE 评估。注意一点负荷预测模型更新频率要高至少每小时重训一次。因为电网调度方式每隔一段时间会变化模型学到的是过去两周的调度习惯不更新就会逐渐失准。4.2 脱硝预测的模型输出设计入口 NOx 是主角喷氨量是控制目标脱硝系统预测容易走入一个误区直接预测出口 NOx 排放浓度。实际上出口 NOx 受喷氨调节阀的闭环控制影响非常大模型预测的其实是当前喷氨量下系统会怎么响应。更好的做法是分成两步先预测省煤器出口即 SCR 入口NOx 浓度再根据入口 NOx 和烟气流量计算需要的喷氨量。入口 NOx 浓度典型的影响因素包括总燃料量、总风量、二次风门开度、磨煤机组合方式、煤质发热量。煤质是最难处理的变量因为实际入炉煤的发热量和含氮量每天都在变。工程上常用累计给煤量来近似煤质变化的影响给煤机转速和称重信号本身就是对煤量的积分。脱硝喷氨量的前馈控制逻辑可以简化为喷氨量目标 烟气流量 × (入口NOx浓度 × 氨氮摩尔比系数 × 脱硝效率目标) × 补偿系数模型要做的是把入口 NOx 浓度预测准然后这个公式就能给出前馈指令。运行工程师不信任模型直接控制氨水阀门但接受入口 NOx 在 5 分钟后会升到 380mg/m3建议提前增加喷氨 8%这种建议方式。4.3 电气和脱硝的耦合点用负荷率做工况分段火电厂预测最容易忽略的是电气侧与脱硝侧的耦合关系。机组升负荷时给煤量和风量同时增加炉膛温度升高NOx 生成量会显著上升。如果模型只学脱硝侧的局部特征在负荷大幅波动时会漏掉最关键的驱动因素。我采用的方案是加入负荷率作为全局工况特征并且按负荷率分段建模。分段阈值一般取 50% 和 80%原因是低于 50% 是低负荷稳燃工况燃烧不完全NOx 排放特征复杂50%~80% 是常规变负荷区间数据量大且规律性最强高于 80% 是接近满负荷工况炉温高NOx 生成处于高位。负荷率特征不仅对脱硝预测有用对电气负荷预测本身也增加了工况记忆能力。# 按负荷率分段训练每段一个模型 df[load_zone] pd.cut(df[load_rate], bins[0, 0.5, 0.8, 1.0], labels[low, mid, high]) models {} for zone in [low, mid, high]: zone_data df[df[load_zone] zone] X zone_data[[total_air, coal_flow, scr_temp, load_rate]].values y zone_data[scr_inlet_nox].shift(-5).values models[zone] xgb.train(params, xgb.DMatrix(X[:-5], y[:-5]))分段建模的问题是段边界处预测会跳变解决方法是让各段模型的重叠区域用线性插值过渡。在边界附近取两个模型的加权平均权重随负荷率线性变化预测曲线会平滑很多。5. 归档项目最有价值的动作上线后漂移监测与参数复盘表预测系统上线之后最容易被忽略的就是持续监测。火电厂设备会老化、煤质会变化、电网调度方式会调整这些都导致模型性能随时间退化。我归档过的项目里三个月后 MAE 漂移超过 30% 的并不罕见。监测方式用两层特征分布漂移 预测误差漂移。import numpy as np def compute_psi(expected, actual, bins10): # 分箱归一化计算群体稳定性指数 hist_e, edges np.histogram(expected, binsbins) hist_a, _ np.histogram(actual, binsedges) pct_e hist_e / max(hist_e.sum(), 1) 1e-5 pct_a hist_a / max(hist_a.sum(), 1) 1e-5 psi np.sum((pct_a - pct_e) * np.log(pct_a / pct_e)) return psi # 以给煤量为特征示例对比上线首周与当前分布 psi_value compute_psi(base_distribution_X, current_distribution_X)PSI 大于 0.1 时需要排查工况是否发生变化大于 0.25 时基本可以直接判定特征失效。预测误差的监测用 EWMA指数加权移动平均控制图误差均值连续 7 个点超过 2 倍标准差时触发重训练告警。归档时除模型文件外必须沉淀的参数复盘表包括数据时间窗口比如取前 90 天还是 180 天、滞后特征分钟档位3/5/10/15、负荷分段阈值、LSTM 的 seq_len 与 hidden_size、各阶段验证 MAE 基线值、PSI 阈值。这些参数在项目移交时比模型权重更有价值。保持这样的节奏以后接手新机组或者新 DCS 型号时对照复盘表重新拉数据、调点位、跑基线三天内就能把整套流程复制到另一台机组上。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进