- 读取 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 中的硬件接线、配置默认值与模块职责描述
214 lines
9.6 KiB
Python
214 lines
9.6 KiB
Python
"""气体流量闭环控制的集中配置。
|
||
|
||
首次连接真实设备前,请重点核对: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
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# PLC(Easy521)Modbus 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 # 电机输出 D730,0~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 ms,10 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。
|
||
|
||
# 阀特性表 JSON(identify_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")
|