ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

python的工业过程控制场景模拟第一百三十三篇:编写程序统计调节阀动作次数,到达阀值推送维保提醒。

2026/8/14 13:35:52 拓冰建站 浏览量
python的工业过程控制场景模拟第一百三十三篇:编写程序统计调节阀动作次数,到达阀值推送维保提醒。

调节阀动作次数统计与维保预警:用状态机+计数器构建预测性维护系统

"一条化工生产线,68台调节阀分布全厂,以前靠'坏了再修',全年非计划停机7次,单次紧急维修平均花费2.8万元,停产损失约15万元/次;上了动作次数统计+阈值预警后,全年非计划停机降至1次,计划性维护占比从23%提升到89%,年维保成本从46万降到14万,直接省下32万。"

—— 参考哈尔滨工程大学《工业过程控制》第7章"执行器"、第8章"过程控制系统的运行与维护"

一、实际应用场景描述

在化工、石化、制药、电力等流程工业中,调节阀(Control Valve)是过程控制系统的"手脚"——它接收控制器输出的4~20mA信号,通过气动/电动执行机构驱动阀芯位移,调节介质流量。一个典型的化工厂调节阀维保管理场景如下:

┌──────────────────────────────────────────────────────────────┐

│ 调节阀动作统计与维保预警系统 │

│ │

│ 【系统架构】 │

│ ┌──────────┐ 4~20mA ┌──────────┐ Modbus/DCS ┌──────────┐│

│ │ 控制器 │ ──────────→ │ 调节阀 │ ─────────────→ │ 上位机 ││

│ │ (PID输出)│ AO信号 │ 阀门定位器│ 反馈信号+状态 │ 维保系统 ││

│ └──────────┘ └──────────┘ └──────────┘│

│ │ │ │ │

│ │ ▼ ▼ │

│ │ ┌──────────────┐ ┌────────────┐│

│ │ │ 阀门动作计数 │ │ 预警推送 ││

│ │ │ (开/关/调节) │ │ (声光/短信)││

│ │ └──────────────┘ └────────────┘│

│ │

│ 【典型阀门清单(某精细化工车间)】 │

│ ┌────┬──────────┬────────┬────────┬────────┬────────┬────────┐│

│ │位号│ 阀门名称 │ 介质 │ 阀类型 │ 动作寿命│ 当前计数│ 维护阈值││

│ ├────┼──────────┼────────┼────────┼────────┼────────┼────────┤│

│ │TV01│ 蒸汽温控阀│ 饱和蒸汽│ 气动薄膜│ 50万次 │ 48.2万 │ 50万 ││

│ │FV02│ 进料流量阀│ 甲醇 │ 气动活塞│ 30万次 │ 29.8万 │ 30万 ││

│ │PV03│ 压力分程阀│ 氮气 │ 电气 │ 100万次│ 67.3万 │ 80万 ││

│ │... │ ... │ ... │ ... │ ... │ ... │ ... ││

│ │LV68│ 液位排放阀│ 废水 │ 气动 │ 20万次 │ 19.5万 │ 20万 ││

│ └────┴──────────┴────────┴────────┴────────┴────────┴────────┘│

│ │

│ 【阀门动作类型与计数规则】 │

│ • 全行程动作:阀位从0%→100%或100%→0%(计1次) │

│ • 调节动作:阀位变化≥5%且非全行程(计1次) │

│ • 微动:阀位变化<5%(不计数,避免噪声误判) │

│ • 保持:阀位变化<1%且持续>60s(计为"稳态",不计动作) │

│ │

│ 【维保策略】 │

│ • 轻度预警(80%寿命):提醒"关注阀门状态,准备备件" │

│ • 中度预警(90%寿命):建议"下次停车时安排检修" │

│ • 重度预警(≥100%寿命):强制"立即安排更换,避免故障" │

│ • 紧急预警(连续3天超阈值未处理):升级通知车间主任 │

│ │

│ 【传统做法】 │

│ • 坏了再修(Reactive Maintenance):阀门卡死才报修 │

│ • 定期大修(Preventive Maintenance):每年全厂停车检修 │

│ • 无动作统计:靠经验判断阀门"应该还能用" │

│ • 纸质台账:维护记录写在纸上,丢失、涂改、不可追溯 │

│ • 无预警推送:维保人员不知道哪台阀门快到寿命 │

└──────────────────────────────────────────────────────────────┘

二、引入痛点(含量化对比)

2.1 现场真实困境

某精细化工车间设备主管的反馈:

"我们车间68台调节阀,控制着蒸汽、甲醇、溶剂、酸碱等各种介质。以前阀门维护就是'坏了再修'——阀门卡了、漏了、定位器坏了,操作工打电话来,我们才派维修工去处理。

去年最惨的一次:TV01蒸汽温控阀,控制着反应釜夹套温度。那天正在跑一批高附加值产品,阀门突然卡在50%开度不动了。PID控制器拼命输出100%想升温,但阀门没反应。温度从120℃掉到85℃,反应不完全,整批产品报废,直接损失18万。加上紧急维修费、停产等待时间,那一次总共损失快25万。

事后拆开阀门一看,阀芯磨损严重,填料也老化了。维修师傅说:'这个阀动作了快50万次,早该换了。'我问:'你怎么知道它动作了多少次?'他说:'拆开看阀芯磨损程度猜的。'

我就想,如果有个系统能统计每台阀门的动作次数,到寿命前提醒我换掉,那批产品就不会报废了。但市面上的MES系统太贵(动辄几十万),而且我们车间用的是老DCS,不支持这种自定义统计。能不能自己用Python搞一个轻量级的?"

2.2 传统"坏了再修" vs 动作统计+阈值预警(量化对比)

指标 传统坏了再修 动作统计+阈值预警 提升效果

非计划停机次数/年 7次 1次 -85.7%

单次紧急维修成本 2.8万元 0.4万元(计划性) -85.7%

单次停产损失 约15万元 ≈0元(计划停车) -100%

年维保总成本 46万元 14万元 -69.6%

计划性维护占比 23% 89% +66个百分点

阀门平均使用寿命 约2.1年(提前损坏) 接近设计寿命3年 +42.9%

产品报废批次/年 3~4批 0~1批 -75%

维修响应时间 2~4小时(紧急) 提前备件,0紧急响应 质的提升

数据可追溯性 纸质记录,易丢失 电子化台账,完整追溯 合规提升

关键发现:传统"坏了再修"模式的隐性成本极高——紧急维修费+停产损失+产品报废三项合计,单次故障平均损失17.8万元。而预测性维护的核心价值不是"修阀门更便宜",而是"避免非计划停机导致的连锁损失"。动作次数统计是最简单、最可靠的预测性维护指标之一——阀门的机械磨损与动作次数强相关。

2.3 核心矛盾

调节阀维护的核心矛盾是"机械磨损的确定性"与"维护时机的盲目性"之间的冲突。

阀门的机械寿命(动作次数)是可预知的——厂家手册明确写着"额定寿命30万次"。但传统管理模式下,维护时机是盲目的——要么等坏了再修(太晚),要么定期全换(太早、太贵)。动作次数统计+阈值预警,把"寿命消耗"变成"可观测的数字",让维护决策从"猜"变成"算"。

三、核心逻辑讲解(大白话版)

3.1 用大白话解释"阀门动作统计与预警"

想象你在管理一支出租车队:

每辆车(阀门):

- 仪表盘上有个里程表(动作计数器),记录跑了多少公里(动作了多少次)。

- 厂家说:这车跑到50万公里必须大修(阀门寿命)。

你的管理方式(传统):

- 车坏了才拖去修,乘客被困在路上,投诉+拖车费+误工费,一次损失好几千。

- 或者:每年把所有车都大修一遍,不管跑了多少公里。跑了5万的车也大修,浪费钱。

聪明的管理方式(本方案):

- 每辆车装个自动里程统计器,实时记录里程。

- 设定预警线:

- 跑到40万公里(80%):提醒司机"注意车况,准备备件"。

- 跑到45万公里(90%):通知车队"下次回场时安排检修"。

- 跑到50万公里(100%):强制"立即停运,必须大修"。

- 到了预警线,系统自动发短信给调度员,不用司机自己记。

工业现场版:

- 出租车 = 调节阀

- 里程表 = 动作计数器(软件统计阀位变化)

- 50万公里 = 阀门额定动作寿命(如30万次)

- 预警线 = 80%/90%/100% 寿命阈值

- 短信通知 = 上位机弹窗/声光报警/企业微信推送

进阶问题(去抖动):

"阀门在50%开度附近来回抖,每次抖0.5%,算不算动作?"

不算!这就像车在堵车时每秒挪5厘米——虽然里程表在走,但不算"一次有意义的行驶"。本方案设定最小变化阈值(5%),小于这个的不计数,避免"噪声抖动"导致计数虚高。

3.2 工业过程控制模型(哈工程《工业过程控制》标准建模)

调节阀动态模型与动作计数原理:

阀门位置特性:

y(t) = f(u(t))

其中:

- u(t) :控制器输出信号(4~20mA 或 0~100%)

- y(t) :阀门实际开度(%)

动作判定逻辑(状态机):

S_{prev} = y(t - \Delta t)

\Delta y = |y(t) - S_{prev}|

\text{ActionType}(t) = \begin{cases} \text{FullStroke}, & \Delta y \geq 95\% \\ \text{Regulation}, & 5\% \leq \Delta y < 95\% \\ \text{MicroMovement}, & 1\% \leq \Delta y < 5\% \\ \text{Steady}, & \Delta y < 1\% \end{cases}

计数器更新:

C(t) = C(t - \Delta t) + \begin{cases} 1, & \text{ActionType} \in \{\text{FullStroke, Regulation}\} \\ 0, & \text{otherwise} \end{cases}

寿命消耗率:

R(t) = \frac{C(t)}{L_{rated}} \times 100\%

其中 L_{rated} 为额定动作寿命。

多级预警触发条件:

\text{AlertLevel}(t) = \begin{cases} \text{None}, & R(t) < 80\% \\ \text{Light}, & 80\% \leq R(t) < 90\% \\ \text{Medium}, & 90\% \leq R(t) < 100\% \\ \text{Heavy}, & R(t) \geq 100\% \\ \text{Emergency}, & R(t) \geq 100\% \text{ 且持续超过 } T_{emergency} \text{ 天} \end{cases}

参考哈工程《工业过程控制》第7章:

- §7.1 "执行器的类型与特性":调节阀的摩擦、死区、回差是动作计数的主要噪声源。

- §7.3 "阀门定位器":定位器的闭环控制使阀门频繁微动,需设定合理的不灵敏区避免误计数。

- §8.2 "控制系统的日常维护":定期校准、填料更换、密封件检查的时机应与动作次数挂钩。

3.3 如何映射到代码中

数学模型/概念 Python 代码

阀门位置 y(t)

"ValveMonitor.current_position"

动作判定 \Delta y

"_classify_movement(delta)"

计数器 C(t)

"ValveMonitor.action_count"

寿命消耗率 R(t)

"ValveMonitor.life_consumed_pct"

多级预警

"AlertLevel" 枚举 +

"_check_alert_level()"

去抖动(最小阈值)

"MIN_COUNTING_CHANGE = 5.0"

稳态检测

"STEADY_THRESHOLD = 1.0"

预警推送

"on_alert" 回调函数 +

"AlertManager"

核心思想:

1. 每次采集到新阀位数据,计算变化量。

2. 根据变化量分类:全行程/调节/微动/稳态。

3. 仅对"有意义的动作"计数,过滤噪声。

4. 实时计算寿命消耗率,与多级阈值比较。

5. 触发预警时通过回调通知上层系统(数据库记录+消息推送)。

6. 支持模拟模式,无需真实DCS即可开发和测试。

四、OOP 代码实现(精简可运行)

4.1 项目结构

valve_maintenance_monitor/

├── valve_monitor.py # 核心代码(单文件,~360行)

├── README.md # 使用说明

└── requirements.txt # 依赖库

4.2 完整源代码(可直接运行)

<details>

<summary></summary>

"""

调节阀动作次数统计与维保预警系统

参考: 哈尔滨工程大学《工业过程控制》第7章"执行器"、第8章"运行与维护"

作者: 工业控制与上位机开发工程师

适用: CSDN技术博客完整发布版(单文件, ~360行)

功能:

- 实时统计调节阀动作次数(全行程/调节/微动分类)

- 多级阈值预警(80%/90%/100%/紧急)

- 去抖动处理(最小变化阈值过滤噪声)

- 阀门健康状态评估

- 模拟数据生成(无需真实DCS即可运行)

- 预警推送(回调机制,可接短信/微信/数据库)

"""

import time

import math

from dataclasses import dataclass, field

from datetime import datetime, timedelta

from enum import Enum, auto

from typing import Callable, Dict, List, Optional, Tuple

import json

# ─── 枚举定义 ────────────────────────────────────────────────────────────────

class MovementType(Enum):

"""阀门动作类型"""

FULL_STROKE = "全行程动作" # 0%↔100% 或变化≥95%

REGULATION = "调节动作" # 5%~95%变化

MICRO_MOVEMENT = "微动" # 1%~5%变化(不计入动作次数)

STEADY = "稳态" # <1%变化(不计入)

class AlertLevel(Enum):

"""预警等级"""

NONE = "正常"

LIGHT = "轻度预警(80%)" # 关注

MEDIUM = "中度预警(90%)" # 建议检修

HEAVY = "重度预警(100%)" # 立即更换

EMERGENCY = "紧急(超期未处理)" # 升级通知

class ValveType(Enum):

"""阀门类型"""

PNEUMATIC_DIAPHRAGM = "气动薄膜"

PNEUMATIC_PISTON = "气动活塞"

ELECTRIC = "电动"

HYDRAULIC = "液动"

# ─── 数据类(值对象) ─────────────────────────────────────────────────────────

@dataclass(frozen=True)

class ValveSpec:

"""阀门规格 —— 值对象(不可变)"""

tag: str # 位号,如 TV01

name: str # 阀门名称

valve_type: ValveType # 阀门类型

medium: str # 介质

rated_life: int = 300_000 # 额定动作寿命(次)

install_date: str = "2024-01-01" # 安装日期

def __repr__(self) -> str:

return f"[{self.tag}] {self.name}({self.valve_type.value}, 寿命:{self.rated_life:,}次)"

@dataclass

class ValveStats:

"""阀门运行统计(可变)"""

action_count: int = 0 # 总动作次数

full_stroke_count: int = 0 # 全行程次数

regulation_count: int = 0 # 调节次数

micro_movement_count: int = 0 # 微动次数(不计入寿命消耗)

steady_count: int = 0 # 稳态次数

last_position: float = 0.0 # 上次阀位

last_movement_time: Optional[datetime] = None

last_alert_level: AlertLevel = AlertLevel.NONE

last_alert_time: Optional[datetime] = None

first_action_time: Optional[datetime] = None

alert_acknowledged: bool = False # 预警是否已确认

@property

def life_consumed_pct(self) -> float:

"""寿命消耗百分比"""

if self.action_count == 0:

return 0.0

# 微动不消耗寿命,所以只算有效动作

return (self.action_count / 300_000) * 100 # 分母会在实际计算时动态传入

@property

def days_in_service(self) -> int:

"""投用天数"""

if self.first_action_time:

return (datetime.now() - self.first_action_time).days

return 0

@property

def actions_per_day(self) -> float:

"""日均动作次数"""

days = self.days_in_service

if days > 0:

return self.action_count / days

return 0.0

# ─── 核心类:单台阀门监控器 ──────────────────────────────────────────────────

class ValveMonitor:

"""

单台调节阀监控器

设计模式: 状态模式(State Pattern) + 观察者模式(Observer)

参考: 哈工程《工业过程控制》§7.3 "阀门定位器与执行机构"

"""

# 去抖动阈值(类常量)

MIN_COUNTING_CHANGE = 5.0 # 最小计数变化(%),小于此值不计为有效动作

STEADY_THRESHOLD = 1.0 # 稳态阈值(%)

FULL_STROKE_THRESHOLD = 95.0 # 全行程判定阈值(%)

def __init__(

self,

spec: ValveSpec,

on_alert: Optional[Callable[["ValveMonitor", AlertLevel, str], None]] = None,

):

self.spec = spec

self.stats = ValveStats()

self._on_alert = on_alert

self._alert_cooldown_hours = 24 # 同一级别预警24小时内不重复推送

self._emergency_days = 3 # 超100%超过此天数升级为紧急

self._alert_history: List[Dict] = []

@property

def current_position(self) -> float:

"""当前阀位(从DCS/PLC读取,此处由update_position传入)"""

return self.stats.last_position

@property

def life_consumed_pct(self) -> float:

"""寿命消耗百分比(使用实际额定寿命)"""

if self.spec.rated_life <= 0:

return 0.0

return (self.stats.action_count / self.spec.rated_life) * 100

@property

def remaining_life_pct(self) -> float:

"""剩余寿命百分比"""

return max(0.0, 100.0 - self.life_consumed_pct)

@property

def estimated_days_remaining(self) -> Optional[float]:

"""预计剩余天数"""

daily_rate = self.stats.actions_per_day

if daily_rate <= 0:

return None

remaining_actions = self.spec.rated_life - self.stats.action_count

if remaining_actions <= 0:

return 0.0

return remaining_actions / daily_rate

def update_position(self, new_position: float, timestamp: Optional[datetime] = None) -> MovementType:

"""

更新阀位并统计动作

Args:

new_position: 新阀位(0~100%)

timestamp: 时间戳,默认当前时间

Returns:

MovementType: 本次动作类型

"""

if timestamp is None:

timestamp = datetime.now()

# 首次动作记录起始时间

if self.stats.first_action_time is None:

self.stats.first_action_time = timestamp

# 计算变化量

delta = abs(new_position - self.stats.last_position)

# 分类动作

movement = self._classify_movement(delta)

self._update_counts(movement)

# 记录时间

if movement in (MovementType.FULL_STROKE, MovementType.REGULATION):

self.stats.last_movement_time = timestamp

# 更新位置

self.stats.last_position = new_position

# 检查预警

if movement in (MovementType.FULL_STROKE, MovementType.REGULATION):

self._check_alert_level(timestamp)

return movement

def _classify_movement(self, delta: float) -> MovementType:

"""根据变化量分类动作"""

if delta >= self.FULL_STROKE_THRESHOLD:

return MovementType.FULL_STROKE

elif delta >= self.MIN_COUNTING_CHANGE:

return MovementType.REGULATION

elif delta >= self.STEADY_THRESHOLD:

return MovementType.MICRO_MOVEMENT

else:

return MovementType.STEADY

def _update_counts(self, movement: MovementType) -> None:

"""更新计数"""

if movement == MovementType.FULL_STROKE:

self.stats.action_count += 1

self.stats.full_stroke_count += 1

elif movement == MovementType.REGULATION:

self.stats.action_count += 1

self.stats.regulation_count += 1

elif movement == MovementType.MICRO_MOVEMENT:

self.stats.micro_movement_count += 1

else:

self.stats.steady_count += 1

def _check_alert_level(self, timestamp: datetime) -> None:

"""检查并触发预警"""

pct = self.life_consumed_pct

new_level = self._calculate_alert_level(pct, timestamp)

if new_level != self.stats.last_alert_level:

# 预警升级或新触发

self.stats.last_alert_level = new_level

self.stats.last_alert_time = timestamp

self.stats.alert_acknowledged = False

if new_level != AlertLevel.NONE:

message = self._build_alert_message(new_level, pct)

self._alert_history.append({

"time": timestamp.isoformat(),

"level": new_level.name,

"message": message,

"life_pct": pct,

})

if self._on_alert:

try:

self._on_alert(self, new_level, message)

except Exception as e:

print(f"⚠️ 预警回调异常: {e}")

def _calculate_alert_level(self, pct: float, timestamp: datetime) -> AlertLevel:

"""计算当前预警等级"""

# 紧急判定:超100%且超过指定天数未确认

if pct >= 100.0:

if self.stats.last_alert_time:

days_over = (timestamp - self.stats.last_alert_time).days

if days_over >= self._emergency_days and not self.stats.alert_acknowledged:

return AlertLevel.EMERGENCY

return AlertLevel.HEAVY

elif pct >= 90.0:

return AlertLevel.MEDIUM

elif pct >= 80.0:

return AlertLevel.LIGHT

else:

return AlertLevel.NONE

def _build_alert_message(self, level: AlertLevel, pct: float) -> str:

"""构建预警消息"""

remaining = self.estimated_days_remaining

days_str = f"约{remaining:.0f}天" if remaining else "未知"

base = (

f"【{level.value}】阀门{self.spec.tag}({self.spec.name}): "

f"寿命消耗{pct:.1f}%,已动作{self.stats.action_count:,}次/"

f"{self.spec.rated_life:,}次,预计剩余{days_str}。"

)

if level == AlertLevel.LIGHT:

return base + "建议:关注阀门状态,提前准备备件。"

elif level == AlertLevel.MEDIUM:

return base + "建议:下次计划停车时安排检修或更换。"

elif level == AlertLevel.HEAVY:

return base + "⚠️ 强制:立即安排更换,避免非计划停机!"

else: # EMERGENCY

return base + "🚨 紧急:已超期未处理,已升级通知车间主任!"

def acknowledge_alert(self) -> None:

"""确认预警(消除紧急状态)"""

self.stats.alert_acknowledged = True

def get_health_report(self) -> Dict:

"""生成健康报告"""

return {

"tag": self.spec.tag,

"name": self.spec.name,

"valve_type": self.spec.valve_type.value,

"medium": self.spec.medium,

"rated_life": self.spec.rated_life,

"action_count": self.stats.action_count,

"full_stroke_count": self.stats.full_stroke_count,

"regulation_count": self.stats.regulation_count,

"micro_movement_count": self.stats.micro_movement_count,

"life_consumed_pct": round(self.life_consumed_pct, 2),

"remaining_life_pct": round(self.remaining_life_pct, 2),

"days_in_service": self.stats.days_in_service,

"actions_per_day": round(self.stats.actions_per_day, 1),

"estimated_days_remaining": round(self.estimated_days_remaining, 1)

if self.estimated_days_remaining else None,

"current_position": self.stats.last_position,

"alert_level": self.stats.last_alert_level.name,

"alert_acknowledged": self.stats.alert_acknowledged,

}

def get_alert_history(self) -> List[Dict]:

"""获取预警历史"""

return list(self._alert_history)

# ─── 阀门群管理器 ────────────────────────────────────────────────────────────

class ValveFleetManager:

"""

多阀门群管理器

设计模式: 外观模式(Facade) + 观察者模式(Observer)

"""

def __init__(self):

self._valves: Dict[str, ValveMonitor] = {}

self._global_alert_callbacks: List[Callable] = []

def add_valve(self, monitor: ValveMonitor) -> None:

"""添加阀门监控器"""

self._valves[monitor.spec.tag] = monitor

def register_global_alert_callback(self, cb: Callable) -> None:

"""注册全局预警回调"""

self._global_alert_callbacks.append(cb)

def get_valve(self, tag: str) -> Optional[ValveMonitor]:

return self._valves.get(tag)

def get_all_health_reports(self) -> List[Dict]:

"""获取所有阀门健康报告"""

return [v.get_health_report() for v in self._valves.values()]

def get_summary(self) -> str:

"""生成汇总报告"""

lines = ["\n" + "=" * 85, " 调节阀维保状态汇总", "=" * 85]

lines.append(

f" {'位号':<6} {'名称':<14} {'类型':<10} {'寿命消耗':<10} "

f"{'动作次数':<12} {'预警等级':<16} {'预计剩余'}"

)

lines.append(" " + "-" * 82)

for tag, valve in self._valves.items():

spec = valve.spec

stats = valve.stats

pct = valve.life_consumed_pct

remaining = valve.estimated_days_remaining

rem_str = f"{remaining:.0f}天" if remaining else "已超限"

lines.append(

f" {tag:<6} {spec.name:<14} {spec.valve_type.value:<10} "

f"{pct:<10.1f}% {stats.action_count:<12,} "

f"{stats.last_alert_level.value:<16} {rem_str}"

)

# 统计

total = len(self._valves)

alerting = sum(1 for v in self._valves.values()

if v.stats.last_alert_level != AlertLevel.NONE)

lines.append("-" * 85)

lines.append(f" 总计: {total}台阀门, {alerting}台需要关注")

lines.append("=" * 85)

return "\n".join(lines)

def _on_valve_alert(self, valve: ValveMonitor, level: AlertLevel, message: str) -> None:

"""内部预警处理(转发给全局回调)"""

for cb in self._global_alert_callbacks:

try:

cb(valve, level, message)

except Exception as e:

print(f"⚠️ 全局预警回调异常: {e}")

def bulk_acknowledge(self) -> int:

"""批量确认所有预警"""

count = 0

for valve in self._valves.values():

if not valve.stats.alert_acknowledged and valve.stats.last_alert_level != AlertLevel.NONE:

valve.acknowledge_alert()

count += 1

return count

# ─── 模拟数据生成器 ──────────────────────────────────────────────────────────

class ValveDataSimulator:

"""

阀门数据模拟器(用于演示和测试)

模拟真实的阀门动作模式:

- 正常调节:在设定值附近小幅波动

- 阶跃变化:偶尔大幅调整

- 稳态保持:长时间不变

"""

def __init__(self, initial_position: float = 50.0):

self.position = initial_position

self._step_probability = 0.05 # 5%概率发生阶跃

self._noise_std = 1.5 # 噪声标准差

def next_position(self, target: float = 50.0) -> float:

"""

生成下一个阀位值

模拟真实阀门:大部分时间在目标附近小幅波动,

偶尔有较大阶跃(如PID大幅调整)

"""

import random

# 随机阶跃(模拟大调整)

if random.random() < self._

利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!