Files
flow_control/config.py
T
louis 5888b4ccce 从 MT2-AM8 模拟量模块迁移到 Easy521 PLC(Modbus TCP)
- 读取 D700(阀后流量)、D710(阀后压力)、D720(阀前压力),
  读用功能码 03,0~32000 线性映射到各自量程(300 SLM / 400 kPa / 300 kPa)
- 输出 D730(电机位置,0~10V),写用功能码 06,行程仍为 0~1000、死区 800
- 复用 MT2 骨架,保留 get_flow/get_pressure/set_motor_position 接口,
  channel 参数改为直接 Modbus 地址
- config.py 新增 PLC 设置,保留 MT2AM8 设置为死代码(MT2AM8Client 类保留)
- 同步更新 README.md 中的硬件接线、配置默认值与模块职责描述
2026-09-01 17:03:26 +08:00

214 lines
9.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""气体流量闭环控制的集中配置。
首次连接真实设备前,请重点核对:PLC 寄存器地址、量程、阀门打开端行程和
关闭端行程。PID 参数是安全起步值,不是最终整定结果。
"""
# ---------------------------------------------------------------------------
# MT2-AM8 通讯与量程(旧方案;代码保留但当前不再使用)
# ---------------------------------------------------------------------------
MT2AM8_HOST = "192.168.1.12"
MT2AM8_PORT = 502
MT2AM8_SLAVE_ID = 1
# MT2AM8Client 用这些量程把模拟量转换成物理量。
PRESSURE_RANGE_KPA = 400.0
FLOW_METER_RANGE_SLM = 300.0
# ---------------------------------------------------------------------------
# PLCEasy521Modbus TCP 通讯与寄存器地址
# ---------------------------------------------------------------------------
PLC_HOST = "192.168.1.88"
PLC_PORT = 502
PLC_SLAVE_ID = 1
# D 寄存器为直接 Modbus 地址:读用功能码 03(保持寄存器),写用功能码 06。
# 模拟量 0~32000 线性对应各量程满量程(映射常量在 PcControl.py)。
# 三个测量量 + 一个输出,地址与量程集中放在一起(名称区分阀前 / 阀后)。
FLOW_AFTER_ADDR = 700 # 阀后流量 D700,量程 0~300 SLM
FLOW_AFTER_RANGE_SLM = 300.0
PRESSURE_AFTER_ADDR = 710 # 阀后压力 D710,量程 0~400 kPa
PRESSURE_AFTER_RANGE_KPA = 400.0
PRESSURE_BEFORE_ADDR = 720 # 阀前压力 D720,量程 0~300 kPa
PRESSURE_BEFORE_RANGE_KPA = 300.0
MOTOR_OUTPUT_ADDR = 730 # 电机输出 D7300~10V,行程 0~1000 映射 0~32000
# ---------------------------------------------------------------------------
# 控制目标和控制周期
# ---------------------------------------------------------------------------
TARGET_FLOW_MIN_SLM = 0.0
TARGET_FLOW_MAX_SLM = 300.0
ZERO_FLOW_THRESHOLD_SLM = 0.5
TARGET_SWITCH_KEY = "s" # 运行中按此键进入"输入新目标流量"模式
CONTROL_PERIOD_S = 0.1 # 100 ms10 Hz
MAX_CONTROL_DT_S = 0.3 # 超过此值视为控制循环严重超时
STATUS_PRINT_PERIOD_S = 1.0
# ---------------------------------------------------------------------------
# PID:输出统一定义为阀门开度百分比
# ---------------------------------------------------------------------------
# 大误差时使用 FAR 参数快速接近目标;小误差时使用 NEAR 参数抑制振荡。
# 两档之间采用带滞回的状态切换,不会逐周期连续改变 PID 参数。
PID_FAR_KP = 0.5
PID_FAR_KI = 0.5
PID_FAR_KD = 0.0
PID_NEAR_KP = 0.05
PID_NEAR_KI = 0.01
PID_NEAR_KD = 0.0
OPENING_MIN_PCT = 0.0
OPENING_MAX_PCT = 100.0
MAX_OPENING_RATE_FAR_PCT_S = 200.0
MAX_OPENING_RATE_NEAR_PCT_S = 20.0
# FAR -> NEAR:绝对误差连续满足 near 阈值指定周期数后切换。
# NEAR -> FAR:绝对误差连续达到 far 阈值指定周期数后切换。
PID_NEAR_ERROR_MIN_SLM = 3.0
PID_NEAR_ERROR_TARGET_RATIO = 0.05
PID_FAR_ERROR_MIN_SLM = 12.0
PID_FAR_ERROR_TARGET_RATIO = 0.20
PID_MODE_SWITCH_CONFIRM_CYCLES = 10
# ---------------------------------------------------------------------------
# 稳态判定(用于控制性能报告)
# ---------------------------------------------------------------------------
STEADY_STATE_WINDOW_S = 10.0 # 滑动时间窗口长度
STEADY_STATE_BAND_PCT = 1.0 # 滑窗均值相对目标的允许误差带(±1%)
STEADY_STATE_STD_PCT = 1.0 # 滑窗标准差阈值(相对目标百分比)
AUTO_STOP_ON_STEADY = False # 达到稳态后是否自动停止控制(False 则持续控制)
# ---------------------------------------------------------------------------
# 阀门执行机构
# ---------------------------------------------------------------------------
# 电机行程越大,阀门开度越小。
# opening=0% -> MOTOR_CLOSED_POSITION(完全关闭)
# opening=100% -> MOTOR_OPEN_POSITION(打开端/机械死区边界)
MOTOR_CLOSED_POSITION = 1000.0
MOTOR_OPEN_POSITION = 800.0
# 控制启动时的阀门初始开度;增量 PID 从该开度开始累加 Δu。
INITIAL_OPENING_PCT = 100.0
# ---------------------------------------------------------------------------
# 前馈 + 增益调度(默认关闭,便于与纯 PID 做 A/B 对比;接线确认后再开启)
# ---------------------------------------------------------------------------
# 阀前压力 P1(前馈缩放 / 判阻塞 / 增益调度用),地址见上 PLC 段 PRESSURE_BEFORE_ADDR。
# 阀特性表 JSONidentify_valve.py 产出)。空字符串表示关闭前馈。
VALVE_MODEL_PATH = ""
# 前馈反解打底 + PI 修残差(市面 MFC 结构)。
FEEDFORWARD_ENABLED = False
# 前馈开启时,PI 输出是“围绕前馈的修正量”,限幅为对称小范围(±%)。
FEEDFORWARD_CORRECTION_BAND_PCT = 20.0
# PI 增益按阀前压力 P1 缩放(摊平 K_x ∝ P1 的非线性)。
GAIN_SCHEDULE_ENABLED = False
GAIN_SCHEDULE_REF_ABS_KPA = 501.325 # 400 + 101.325,满压时调好的基准增益对应压力
GAIN_SCHEDULE_FLOOR_ABS_KPA = 60.0 # 绝对压力下限,防止 P1 很低时增益爆炸
GAIN_SCHEDULE_SCALE_CHANGE_THRESHOLD = 0.05 # scale 相对变化超过此值才重算增益
# ---------------------------------------------------------------------------
# 安全阈值
# ---------------------------------------------------------------------------
# 物理读数允许范围;超过范围立即报故障停机(阀门不被驱动时回到全开)。
FLOW_VALID_MIN_SLM = 0.0
FLOW_VALID_MAX_SLM = 300.0
MAX_PRESSURE_KPA = 500 # 必须按实际管路额定压力确认
# 短暂读取失败时保持上一输出,不下发新位置;连续达到阈值后报故障停机。
MAX_CONSECUTIVE_FLOW_FAILURES = 3
MAX_CONSECUTIVE_PRESSURE_FAILURES = 3
# 日志目录相对于工程目录。
LOG_DIRECTORY = "logs"
LOG_FILE_NAME = "flow_control.log"
# 每次运行的逐周期数据和曲线图保存目录(相对于工程目录)。
DATA_DIRECTORY = "data"
PLOT_DPI = 160
def validate_config():
"""在接触硬件前检查明显的配置错误。"""
if CONTROL_PERIOD_S <= 0:
raise ValueError("CONTROL_PERIOD_S 必须大于 0")
if STEADY_STATE_WINDOW_S <= CONTROL_PERIOD_S:
raise ValueError("STEADY_STATE_WINDOW_S 必须大于 CONTROL_PERIOD_S")
if STEADY_STATE_BAND_PCT < 0 or STEADY_STATE_STD_PCT < 0:
raise ValueError("稳态误差带和标准差阈值不得为负数")
if MAX_CONTROL_DT_S < CONTROL_PERIOD_S:
raise ValueError("MAX_CONTROL_DT_S 不得小于 CONTROL_PERIOD_S")
if TARGET_FLOW_MIN_SLM > TARGET_FLOW_MAX_SLM:
raise ValueError("目标流量上下限配置错误")
if TARGET_FLOW_MAX_SLM > FLOW_AFTER_RANGE_SLM:
raise ValueError("目标流量上限不得超过流量计量程")
if FLOW_VALID_MIN_SLM > FLOW_VALID_MAX_SLM:
raise ValueError("流量有效范围上下限配置错误")
if FLOW_VALID_MAX_SLM > FLOW_AFTER_RANGE_SLM:
raise ValueError("流量有效范围上限不得超过流量计量程")
if not 0 <= OPENING_MIN_PCT < OPENING_MAX_PCT <= 100:
raise ValueError("阀门开度范围必须位于 0~100%,且下限小于上限")
if MOTOR_OPEN_POSITION >= MOTOR_CLOSED_POSITION:
raise ValueError("本系统行程越大开度越小,因此打开端行程必须小于关闭端行程")
if MOTOR_OPEN_POSITION < 0 or MOTOR_CLOSED_POSITION > 1000:
raise ValueError("电机行程必须位于当前采用的 0~1000 范围")
pid_gains = (
PID_FAR_KP,
PID_FAR_KI,
PID_FAR_KD,
PID_NEAR_KP,
PID_NEAR_KI,
PID_NEAR_KD,
)
if any(value < 0 for value in pid_gains):
raise ValueError("FAR/NEAR PID 参数不得为负数")
if (
MAX_OPENING_RATE_FAR_PCT_S <= 0
or MAX_OPENING_RATE_NEAR_PCT_S <= 0
):
raise ValueError("FAR/NEAR 最大开度变化速度必须大于 0")
if PID_NEAR_ERROR_MIN_SLM < 0 or PID_FAR_ERROR_MIN_SLM < 0:
raise ValueError("PID 模式切换误差下限不得为负数")
if (
PID_NEAR_ERROR_TARGET_RATIO < 0
or PID_FAR_ERROR_TARGET_RATIO < 0
):
raise ValueError("PID 模式切换目标比例不得为负数")
if (
PID_NEAR_ERROR_MIN_SLM >= PID_FAR_ERROR_MIN_SLM
or PID_NEAR_ERROR_TARGET_RATIO >= PID_FAR_ERROR_TARGET_RATIO
):
raise ValueError("PID NEAR 阈值必须小于 FAR 阈值")
if PID_MODE_SWITCH_CONFIRM_CYCLES < 1:
raise ValueError("PID_MODE_SWITCH_CONFIRM_CYCLES 必须至少为 1")
if MAX_CONSECUTIVE_FLOW_FAILURES < 1:
raise ValueError("MAX_CONSECUTIVE_FLOW_FAILURES 必须至少为 1")
if PRESSURE_AFTER_ADDR is not None and MAX_PRESSURE_KPA is None:
raise ValueError("启用压力通道时必须配置 MAX_PRESSURE_KPA")
if PRESSURE_BEFORE_ADDR is not None:
if not isinstance(PRESSURE_BEFORE_ADDR, int):
raise ValueError("PRESSURE_BEFORE_ADDR 必须为整数地址或 None")
if PRESSURE_BEFORE_ADDR < 0:
raise ValueError("PRESSURE_BEFORE_ADDR 不得为负数")
if MAX_PRESSURE_KPA is None:
raise ValueError("启用阀前压力通道时必须配置 MAX_PRESSURE_KPA")
if GAIN_SCHEDULE_REF_ABS_KPA <= 0:
raise ValueError("GAIN_SCHEDULE_REF_ABS_KPA 必须大于 0")
if GAIN_SCHEDULE_FLOOR_ABS_KPA <= 0:
raise ValueError("GAIN_SCHEDULE_FLOOR_ABS_KPA 必须大于 0")
if not 0 < FEEDFORWARD_CORRECTION_BAND_PCT <= 100:
raise ValueError("FEEDFORWARD_CORRECTION_BAND_PCT 必须位于 0~100")
if GAIN_SCHEDULE_SCALE_CHANGE_THRESHOLD <= 0:
raise ValueError("GAIN_SCHEDULE_SCALE_CHANGE_THRESHOLD 必须大于 0")