python的工业过程控制场景模拟第十六篇:统计每班原料进料,成品出料数据,计算物料损耗率,定位泄漏或者投料误差。
物料平衡与损耗分析系统 —— 基于OOP的工业数据实战原料进去了多少、成品出来了什么、中间的窟窿去哪了——这是每个车间主任每天该问的问题。—— 哈尔滨工程大学《工业过程控制》课程核心思想一、实际应用场景描述在精细化工、制药、食品加工、冶金等行业物料平衡是最基础也最容易被忽视的管理工具。每一班、每一天原料进去多少、成品出来多少、废料排掉多少——理论上应该收支相等但现实中永远差着一个损耗项。典型的车间物料流向┌─────────────────────────────────┐│ 原料储罐 (日投料) ││ 批次: B-2025XXX 材质: 不锈钢 │└──────────────┬──────────────────┘│ 进料阀(流量计 FT-101)▼┌─────────────────────────────────┐│ 反应釜 R-101 ││ 工艺: 连续进料-反应-出料 ││ 设计产能: 500 吨/天 │└────┬──────────────┬─────────────┘│ │┌──────────┴──┐ ┌────┴──────────┐▼ ▼ ▼ ▼┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐│成品出料 │ │副产品 │ │ 废料 │ │ 废气 ││流量计 │ │收集罐 │ │ 处理 │ │ 处理 ││FT-201 │ │ │ │ │ │ │└────────┘ └────────┘ └────────┘ └────────┘哈尔滨工程大学《工业过程控制》课程彭秀艳教授主讲国家级一流本科课程在第二章控制系统基本概念中将物料平衡列为过程控制的第一性原理——比 PID 更基础比串级更底层。课程明确指出物料守恒是过程工业的铁律。任何控制系统的设计都必须先回答一个问题进来的东西去哪了如果连平衡都算不清控制就是建在沙子上。二、引入痛点2.1 现场的真实困境物料平衡的问题在于——它永远不会突然坏掉但会慢慢吃掉你的利润。场景 现场发生了什么 根因月末盘点 这个月亏了3吨原料去哪了 没有按班次的进出料台账工艺优化 成品率从88%掉到82% 没有损耗率趋势发现太晚投料争议 秤上850kg系统记的是590kg 流量计故障/投料漏记环保审计 废料排放超标请提供批次记录 数据散落在不同日志里泄漏事故 管道漏了三天才发现 没有实时损耗率监控2.2 核心矛盾每 30 分钟 SCADA 都在记录进料和出料但这班亏了多少、哪段亏得最厉害从来没人系统算过。- 月末盘点才发现亏料已经无法追溯哪天哪班出的问题- 流量计故障是渐变的从 100% 准度慢慢漂到 70%日常看不出- 投料工偶尔漏记一两条累积一个月就是几吨的黑洞2.3 我们要解决什么用一段 Python 程序读取车间历史 CSV 数据自动完成1. 按班次日期聚合 —— 进料 / 成品 / 副产品 / 废料2. 物料守恒计算 —— 平衡量 进料 - 出料3. 损耗率计算 —— 核心 KPI(进料-出料) / 进料 × 100%4. 四类异常检测 —— 进料突变 / 出料骤降 / 高损耗率 / 负平衡5. 风险评级 A(优) → D(严重) 工程建议6. 输出 Excel CSV 5 张图表三、核心逻辑讲解3.1 理论依据物料守恒第一性原理本工具全部算法基于哈工程《工业过程控制》第二章物料平衡 第六章单回路控制系统设计① 物料守恒方程M_{in} M_{out} M_{accum} M_{loss}稳态下无累积进料 出料 损耗。损耗包括蒸发、泄漏、废料、计量误差、投料漏记。② 损耗率核心 KPIL\% \frac{M_{in} - M_{out}}{M_{in}} \times 100\%损耗率 含义 行动 0.5% 优秀 维持现状0.5%~1.0% 良好 持续监测1.0%~3.0% 预警 计划排查 3.0% 危险 立即检查泄漏/计量③ 负平衡最危险的信号M_{out} M_{in} \times 1.05出料比进料还多 5% 以上 → 不可能物理成立 → 必然是进料流量计少计 / 投料漏记 / 出料流量计多计。3.2 四类异常检测逻辑逐点物料数据 ──→ 滚动窗口统计│┌───────────────┼───────────────┐↓ ↓ ↓① 进料突变 ② 出料骤降 ③④ 高损耗/负平衡(相对变化30%) (骤降25%) (滚动损耗率阈值)│ │ │└───────────────┼───────────────┘↓cumsum 合并连续异常段↓严重度: 轻度/中度/严重↓推断可能原因3.3 风险评级矩阵维度 A(优) B(良好) C(中等) D(严重)损耗率 0.5% 0.5~1% 1~3% 3%负平衡次数 0 0 1~2 ≥3严重异常 0 0~1 2~3 ≥4取最差维度作为综合等级——保守原则安全第一。3.4 软件实现思路CSV 物料日志 ──→ 数据加载 / 编码探测│↓┌── 数据质量评估 ──┐│ 缺失率/跳变率/等间隔 ││ 班次自动标注 │└────────┬───────────┘↓┌── 班次聚合 ──┐│ 按日期班次分组 ││ 进料/出料/废料求和 ││ 平衡量/平衡率/损耗率 │└────────┬───────────┘↓┌── 异常检测 ──┐│ 进料突变(相对变化) ││ 出料骤降(相对变化) ││ 高损耗率(滚动窗口) ││ 负平衡(出料进料) ││ cumsum 合并事件 │└────────┬───────────┘↓┌── 综合评级 ──┐│ 损耗率维度 ││ 负平衡维度 ││ 异常数量维度 ││ → 取最差 综合 │└────────┬───────────┘↓┌── 报表生成 ──┐│ Excel(4Sheet) ││ CSV × 2 ││ 图表 × 5 │└─────────────────┘四、代码模块化讲解面向对象设计4.1 类结构总览本项目严格采用面向对象编程OOP共设计 6 个核心类 3 个不可变数据类类名 职责 设计模式AppConfig聚合根 聚合 6 个子配置 聚合根模式PlantConfig /ShiftConfig /MaterialConfig 工厂/班次/物料参数 值对象AnomalyConfig /OutputConfig /LoggingConfig 异常/输出/日志参数 值对象MaterialDataLoader CSV 加载、编码探测、质量评估、班次标注 封装MaterialBalanceAnalyzer ★ 核心分析引擎 模板方法ReportGenerator 多格式报表输出 模板方法ShiftSummary /AnomalyEvent /BalanceReport 不可变结果对象 值对象模式4.2 配置层dataclass 聚合根# config_loader.py 核心片段dataclassclass MaterialConfig:物料平衡参数 —— 值对象 内聚阈值raw_material: str raw_feed_kgproduct_out: str product_out_kgloss_warning_pct: float 1.0 # 1% 预警loss_danger_pct: float 3.0 # 3% 危险negative_balance_pct: float -0.5 # 负平衡预警dataclassclass AppConfig:聚合根 —— 持有所有子配置plant: PlantConfig field(default_factoryPlantConfig)shift: ShiftConfig field(default_factoryShiftConfig)material: MaterialConfig field(default_factoryMaterialConfig)# ... 其他子配置classmethoddef from_yaml(cls, path) - AppConfig:工厂方法: YAML → AppConfig# ... 逐段映射, 缺段用默认值亮点外部只需cfg AppConfig.from_yaml(config.yaml)持有一个对象即可访问全部参数。换工艺只改 YAML分析器零修改——单一职责原则。4.3 数据加载层编码自动探测 班次标注# data_loader.py 核心片段class MaterialDataLoader:数据加载器封装staticmethoddef detect_encoding(filepath: str) - str:依次尝试常见编码candidates [utf-8-sig, utf-8, gbk, gb2312, latin1]for enc in candidates:try:with open(filepath, r, encodingenc) as f:f.read(2048)return encexcept (UnicodeDecodeError, OSError):continuereturn utf-8-sigdef load(self, filepath, column_mappingNone) - pd.DataFrame:主入口: 加载 → 解析时间 → 排序 → 标注班次df pd.read_csv(filepath, encodingself.detect_encoding(filepath))# 时间解析df[timestamp] pd.to_datetime(df[timestamp], errorscoerce)df df.dropna(subset[timestamp]).sort_values(timestamp)df.index df[timestamp]# ★ 自动班次标注按小时分段if shift not in df.columns:df[shift] self._label_shift(df.index, AppConfig().shift)return dfdef _label_shift(self, index, shift_cfg) - pd.Series:08:00-16:00 早班, 16:00-24:00 中班, 其余夜班hour index.hourlabels pd.Series(夜班, indexindex)labels[(hour 8) (hour 16)] 早班labels[(hour 16) (hour 24)] 中班return labels亮点班次标注自动完成——只要数据有时间列程序自动按 8-16-24 三段切分。4.4 核心算法①班次物料平衡# core_analyzer.py 核心片段class MaterialBalanceAnalyzer:物料平衡分析引擎模板方法def _compute_shift_balances(self, df) - List[ShiftSummary]:按 日期班次 聚合物料results []for (date, shift_name), grp in df.groupby([date, shift]):raw grp[raw_feed_kg].sum()prod grp[product_out_kg].sum()byprod grp[byproduct_out_kg].sum()waste grp[waste_out_kg].sum()total_out prod byprod wastebalance raw - total_outbalance_pct (balance / raw * 100) if raw 0 else 0loss_rate (-balance / raw * 100) if raw 0 else 0results.append(ShiftSummary(shiftshift_name,datedate,raw_feed_kground(raw, 2),product_out_kground(prod, 2),# ... 其他字段balance_pctround(balance_pct, 4),loss_rate_pctround(loss_rate, 4),))return results关键设计ShiftSummary 是不可变数据类dataclass(frozenTrue)——创建后无法修改安全传递给报表模块不怕意外篡改。4.5 核心算法②四类异常检测cumsum 技巧def _detect_anomalies(self, df) - List[AnomalyEvent]:四类异常检测# ① 进料突变: 相对变化 30%feed df[raw_feed_kg].fillna(0.0)rolling_mean feed.rolling(5, min_periods2).mean()rel_change (feed - rolling_mean) / rolling_mean.replace(0, np.nan)spike_mask rel_change.abs() 0.30events self._extract_events(spike_mask, df, feed_spike, ...)# ② 出料骤降: 相对下降 25%prod df[product_out_kg].fillna(0.0)rolling_p prod.rolling(5, min_periods2).mean()rel_drop (rolling_p - prod) / rolling_p.replace(0, np.nan)drop_mask rel_drop 0.25events self._extract_events(drop_mask, df, output_drop, ...)# ③ 高损耗率: 滚动窗口损耗 3%# ④ 负平衡: 出料 进料 × 1.05# ...return eventsdef _extract_events(self, mask, df, event_type, ...) - List[AnomalyEvent]:★ cumsum 技巧合并连续异常段flag mask.fillna(False).astype(bool)if not flag.any():return []# 核心一行: 标签变化时 group_id 1group_id (~flag).cumsum()min_pts max(2, int(np.ceil(min_duration / dt_min)))events []for gid, grp in flag[flag].groupby(group_id[flag]):if len(grp) min_pts:continuedur len(grp) * dt_min# 严重度if dur 60: sev 严重elif dur 30: sev 中度else: sev 轻度events.append(AnomalyEvent(...))return events亮点四类异常共用同一个_extract_events 方法——只需传入不同的布尔掩码代码复用率极高。4.6 核心算法③综合风险评级def _assess_risk(self, loss_rate, balance_pct) - Tuple[str, str]:综合风险评级 —— 取最差维度reasons []# 维度1: 损耗率if loss_rate self.cfg.material.loss_danger_pct:level D(严重)reasons.append(f损耗率{loss_rate:.2f}%≥危险线)elif loss_rate self.cfg.material.loss_warning_pct:level C(中等)reasons.append(f损耗率{loss_rate:.2f}%≥预警线)elif loss_rate 0.5:level B(良好)else:level A(优)# 维度2: 负平衡投料漏记neg_count sum(1 for a in self._anomaliesif a.event_type negative_balance)if neg_count 0:if level A(优):level B(良好)reasons.append(f{neg_count}次负平衡(投料可能漏记))# 维度3: 严重异常数量severe sum(1 for a in self._anomalies if a.severity 严重)if severe 3 and not level.startswith(D):level C(中等)reasons.append(f{severe}次严重异常)return level, ; .join(reasons) or 物料平衡正常亮点三维度独立评估取最差作为综合等级——保守原则绝不掩盖风险。4.7 实际运行输出$ python main.py物料平衡与损耗分析系统 v1.0.0基于哈尔滨工程大学《工业过程控制》课程理论(进料/出料统计 → 损耗率 → 泄漏/投料误差定位) 配置摘要:工厂: 某精细化工车间工艺: 连续进料-反应-出料设计产能: 500.0 吨/天损耗预警: 1.0%损耗危险: 3.0%负平衡预警: -0.5% 数据文件: data/material_log.csv· 编码探测: utf-8-sig· 总记录数: 1440· 时间范围: 2025-07-01 00:00:00 → 2025-07-30 23:30:00 数据质量评估:· raw_feed_kg_missing_pct: 0.0· product_out_kg_missing_pct: 0.0· 采样间隔: 中位1800s 最大1800s ✅· product_out_kg_jump_count: 3 开始物料平衡分析...· 班次统计: 90 条· 异常事件: 5 个· 综合风险等级: C(中等) 分析摘要─────────────────────────────────────────────总原料进料: 1,037,953.1 kg总成品出料: 897,073.7 kg总副产品: 72,661.3 kg总废料: 33,028.5 kg总出料: 1,002,763.5 kg总平衡量: 35,189.6 kg总平衡率: 3.39 %综合损耗率: -3.39 %异常事件数: 5★ 风险等级: C(中等)★ 风险原因: 5次严重异常 班次摘要 (前10条): 2025-07-01 中班 进料 8,429 出料 8,281 损耗 -1.75% 2025-07-01 夜班 进料 14,202 出料 13,854 损耗 -2.45% 2025-07-01 早班 进料 11,992 出料 11,790 损耗 -1.68% 2025-07-02 中班 进料 8,443 出料 8,278 损耗 -1.95%...⚠ 异常事件 (共5个): #1 07-09 22:00 → 07-09 23:00 类型feed_spike 持续90min 进料突变 30% #2 07-09 16:00 → 07-09 17:00 类型output_drop 持续90min 出料骤降 25% #3 07-13 00:00 → 07-13 01:00 类型output_drop 持续90min 出料骤降 25% #4 07-26 10:00 → 07-26 11:30 类型output_drop 持续120min 出料骤降 25% #5 07-01 02:00 → 07-30 23:30 类型high_loss 持续43080min 损耗率3% 工程建议 风险等级中等 → 计划排查:· 加强进料/出料计量校验· 检查密封件磨损情况 异常类型统计:· 出料骤降: 3 次· 进料突变: 1 次· 高损耗率: 1 次⚠ 1 次负平衡事件 → 出料进料, 可能原因:· 进料流量计故障(少计)· 投料记录漏记· 出料流量计故障(多计) 生成报表... Excel → output/material_balance_report.xlsx CSV → output/shift_summary.csv CSV → output/anomaly_events.csv 01_shift_balance.png 02_loss_trend.png 03_anomaly_timeline.png 04_dashboard.png 05_material_flow.png✅ 报表生成完成✅ 分析完成⏱ 总耗时: 3.4s关键成果- 90 个班次全部自动聚合损耗率逐班可查- 5 个异常事件精准定位3 次出料骤降管道堵塞、1 次进料突变投料过量、1 次持续高损耗慢性泄漏- 综合评级 C(中等) —— 损耗率 3.39% 超过预警线触发排查建议- 自动推断负平衡可能原因流量计故障 / 投料漏记 / 计量误差五、README 与使用说明5.1 项目结构material_balance_monitor/├── config.yaml # 配置文件改它不改代码├── config_loader.py # 配置加载dataclass 聚合根├── generate_sample_data.py # 模拟数据生成30天×3班×异常工况├── data_loader.py # 数据加载 编码探测 班次标注├── core_analyzer.py # ★ 核心分析引擎模板方法├── report_generator.py # 报表生成Excel/CSV/图表├── main.py # 主入口流程编排├── requirements.txt # numpy / pandas / matplotlib / pyyaml / openpyxl├── README.md # 本说明├── data/ # 输入 CSV自动创建└── output/ # 输出报表自动创建├── material_balance_report.xlsx # 4 个 Sheet├── shift_summary.csv├── anomaly_events.csv└── charts/ # 5 张 PNG5.2 三步上手# 第 1 步安装依赖pip install -r requirements.txt# 第 2 步生成模拟数据30 天含 8 次异常工况python generate_sample_data.py# 第 3 步一键分析python main.py5.3 使用你自己的数据将 SCADA/DCS 导出的物料日志整理为 CSVtimestamp,raw_feed_kg,product_out_kg,byproduct_out_kg,waste_out_kg,shift,batch_id2025-07-01 08:00:00,850.5,748.2,59.3,25.1,早班,B-2025070012025-07-01 08:30:00,842.0,751.5,58.9,24.8,早班,B-202507001放入data/material_log.csv编辑config.yaml 中的阈值匹配你的现场运行python main.py 即可。支持中文列名——在config.yaml 的material 段修改映射material:raw_material: 原料进料量product_out: 成品出料量byproduct_out: 副产品量waste_out: 废料量5.4 配置文件说明config.yamlplant:name: 某精细化工车间process: 连续进料-反应-出料design_capacity_tpd: 500.0shift:names: [早班, 中班, 夜班]hours: [8, 8, 8]start_times: [08:00, 16:00, 00:00]material:loss_warning_pct: 1.0 # 1% 预警loss_danger_pct: 3.0 # 3% 危险negative_balance_pct: -0.5 # 负平衡预警anomaly:min_anomaly_duration_min: 15.0 # 最短异常持续feed_spike_threshold_pct: 30.0 # 进料突变阈值output_drop_threshold_pct: 25.0 # 出料骤降阈值rolling_window_n: 55.5 命令行参数python main.py --config my.yaml # 使用自定义配置python main.py --data path.csv # 指定数据文件python main.py --gen-data # 强制重新生成模拟数据python main.py --no-charts # 跳过图表仅表格python main.py --verbose / -v # 详细调试日志5.6 输出文件说明文件 内容output/material_balance_report.xlsx Sheet1 总览 / Sheet2 班次明细(着色) / Sheet3 异常事件 / Sheet4 配置参数output/shift_summary.csv 每班次物料平衡摘要90 行output/anomaly_events.csv 异常事件明细类型/时长/严重度/描述output/charts/01_shift_balance.png 各班次进料/出料/废料对比柱图output/charts/02_loss_trend.png 损耗率与平衡率趋势折线图output/charts/03_anomaly_timeline.png 异常事件时间线颜色类型标记严重度output/charts/04_dashboard.png 综合仪表盘饼图仪表排行分布output/charts/05_material_flow.png 物料流向堆叠面积图每小时六、核心知识点卡片 卡片 1物料守恒第一性原理M_{in} M_{out} M_{accum} M_{loss}项 含义 本课程对应M_{in} 原料进料总量 §2.1 过程系统物料衡算M_{out} 成品副产品废料 §2.2 物料平衡方程M_{accum} 设备内累积变化 §4.1 动态过程特性M_{loss} 损耗泄漏/蒸发/计量误差 §6.1 控制性能评估 参考《工业过程控制》§2 过程系统的物料与能量衡算 核心洞察稳态下 M_{accum}0 所以 M_{loss} M_{in} - M_{out} 。这个差值就是你要追查的黑洞。 卡片 2损耗率 vs 平衡率指标 公式 物理含义平衡量 M_{in} - M_{out} 正盈余负亏料平衡率 (M_{in} - M_{out}) / M_{in} \times 100\% 正好负异常损耗率 (M_{out} - M_{in}) / M_{in} \times 100\% 永远≥0理论值负平衡 M_{out} M_{in} \times 1.05 计量故障/投料漏记 本项目同时计算平衡率和损耗率——前者可正可负用于发现负平衡异常后者只看亏多少用于评估效率。 卡片 3四类异常检测对照类型 检测方法 捕获什么 可能原因feed_spike 进料相对变化 30% 投料过量/脉冲 投料泵失控/人工误操作output_drop 出料相对下降 25% 出料骤降 管道堵塞/泵故障/阀门误关high_loss 滚动损耗率 3% 慢性泄漏 密封磨损/管道砂眼/蒸发negative_balance 出料 进料×1.05 计量反向 流量计故障/投料漏记 四类异常覆盖了突然的和缓慢的两种故障模式——前者用瞬时相对变化捕获后者用滚动窗口累积捕获。 卡片 4cumsum 连续分段万能 idiom# 问题把 [正常, 异常, 异常, 正常, 异常, 异常, 异常] 分段mask pd.Series([False, True, True, False, True, True, True])# ★ 核心一行group_id (~mask).cumsum()# 结果: [0, 0, 0, 1, 1, 1, 1]# → 异常段被自动归组# 然后 groupby 即可for gid, grp in mask[mask].groupby(group_id[mask]):print(f事件{gid}: 持续{len(grp)}个点) 这是 pandas 处理连续区间问题的标准解法比循环快 100 倍。本项目四类异常检测共用同一段代码。 卡片 5班次划分的三种策略策略 适用场景 优缺点固定时段本项目 三班倒8-16-24 简单但夜班跨日需注意按批号分组 批次生产每批 6~12h 精准但需有 batch_id 列聚类自动分 无固定班次连续运行 灵活但需 sklearn 依赖 本项目默认固定时段法同时支持 CSV 中自带shift 列覆盖。 卡片 6OOP 设计模式速查模式 本项目应用 解决的问题聚合根AppConfig 包含 6 个子配置 外部只需持有一个对象模板方法MaterialBalanceAnalyzer.analyze() 主流程固定步骤可替换值对象ShiftSummary/AnomalyEvent 不可变 安全传递、可序列化策略模式MaterialConfig 内聚阈值判定 换标准只改 YAML工厂方法AppConfig.from_yaml() 封装创建逻辑封装ReportGenerator._apply_excel_styles() 着色逻辑对外不可见七、总结7.1 本工具做了什么步骤 内容 对应课程章节① 配置加载 YAML → dataclass 聚合根 —② 数据加载 编码自动探测 时间解析 §3.1 测量误差③ 班次标注 按小时分段8-16-24 —④ 质量评估 缺失率/跳变率/等间隔性 §3.1 测量误差⑤ 班次聚合 进料/出料/废料按日期班次求和 §2.1 物料衡算⑥ 平衡计算 平衡量/平衡率/损耗率 §2.2 守恒方程⑦ 异常检测 进料突变/出料骤降/高损耗/负平衡 §6.1 控制性能评估⑧ 风险评级 三维度取最差 → A/B/C/D —⑨ 工程建议 按风险等级输出排查方向 —⑩ 报表输出 Excel(4Sheet)CSV×2图表×5 —7.2 OOP 设计回顾设计决策 好处 体现的原则AppConfig 聚合根 外部只需持有一个对象 聚合根模式MaterialConfig 内聚阈值 换工艺只改 YAML 单一职责ShiftSummary 不可变 安全传递、可序列化 值对象模式MaterialBalanceAnalyzer.analyze() 模板方法 主流程固定步骤可替换 模板方法模式_extract_events 共用方法 四类异常复用同一段代码 代码复用ReportGenerator 多格式输出 新增格式不改流程 开闭原则OCP编码自动探测 不同 SCADA 导出即插即用 鲁棒性设计7.3 适用与不适用✅ 适用 ❌ 不适用有进出料历史 CSV 的车间 实时在线监控需 OPC UA 直连月度/季度物料盘点分析 多相流/气液两相需专用模型投料误差追溯 秒级快速泄漏检测需高频数据环保审计数据准备 安全联锁系统需 SIS多车间横向对标 非连续工艺如离散制造7.4 下一步可以做什么- 接实时数据用opcua 直读 SCADA变成在线物料平衡看板- 计量校验提醒统计负平衡频率自动建议校准流量计- 多车间对标扩展为多车间的物料效率排名- 泄漏定位沿管线布多个计量点用分段平衡精确定位泄漏段- 机器学习增强用 Isolation Forest 检测非阈值型缓慢漂移- 数字孪生用物料衡算模型生成理想曲线作为基准对比- 碳足迹核算物料损耗 × 碳排放因子 → 对接碳交易免责声明本工具仅用于历史数据的后分析与报表生成不可替代在线计量系统或安全仪表功能。损耗阈值、异常检测参数应依据实际工艺和物料特性调整默认参数仅供参考。负平衡分析仅作为疑似计量故障的提示最终判定需结合现场校验。利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛