Files
flow_control/config.py
T
louis a03735387c 实现流量控制前馈 + 增益调度(MFC 结构)
按 plan.md 新增三个交付物,全部默认关闭以保持与纯 PID 的 A/B 兼容:

- valve_model.py:阀特性模型 Q_ss = A_eff(x)·P1_abs·F(r),含 ISO 6358
  椭圆 F(r)、行程/面积互逆插值、单调性校验、JSON 存取。
- identify_valve.py:从 open_loop.py 扫点 CSV 拟合 A_eff 表并输出 JSON。
- 控制器集成:config/flow_control/main/data_logger 新增阀前压读取
  (channel 2)、前馈打底 + PI 修残差、按 P1 增益调度,并记录/绘制前馈项。

新增配置 FEEDFORWARD_ENABLED / GAIN_SCHEDULE_ENABLED 默认 False,
VALVE_MODEL_PATH 默认空,未加载模型时行为与原先纯 PID 完全一致。
2026-08-27 16:00:51 +08:00

201 lines
8.8 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.
"""气体流量闭环控制的集中配置。
首次连接真实设备前,请重点核对:MT2-AM8 地址、AI/AO 通道、压力上限、
阀门打开端行程和关闭端行程。PID 参数是安全起步值,不是最终整定结果。
"""
# ---------------------------------------------------------------------------
# MT2-AM8 通讯与量程
# ---------------------------------------------------------------------------
MT2AM8_HOST = "192.168.1.12"
MT2AM8_PORT = 502
MT2AM8_SLAVE_ID = 1
# MT2AM8Client 用这些量程把模拟量转换成物理量。
PRESSURE_RANGE_KPA = 1600.0
FLOW_METER_RANGE_SLM = 300.0
# AI/AO 通道均从 0 开始。AI0 和 AO0 属于不同的寄存器区,可以同时使用。
FLOW_INPUT_CHANNEL = 1
PRESSURE_INPUT_CHANNEL = 0 # 没有压力传感器时改为 None
MOTOR_OUTPUT_CHANNEL = 1
# ---------------------------------------------------------------------------
# 控制目标和控制周期
# ---------------------------------------------------------------------------
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 = 20.0
MAX_OPENING_RATE_NEAR_PCT_S = 2.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(前馈缩放 / 判阻塞 / 增益调度用),按实际接线确认。
PRESSURE_BEFORE_CHANNEL = 2
# 阀特性表 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_METER_RANGE_SLM:
raise ValueError("目标流量上限不得超过流量计量程")
if FLOW_VALID_MIN_SLM > FLOW_VALID_MAX_SLM:
raise ValueError("流量有效范围上下限配置错误")
if FLOW_VALID_MAX_SLM > FLOW_METER_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("电机行程必须位于 MT2AM8Client 当前采用的 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_INPUT_CHANNEL is not None and MAX_PRESSURE_KPA is None:
raise ValueError("启用压力通道时必须配置 MAX_PRESSURE_KPA")
if PRESSURE_BEFORE_CHANNEL is not None:
if not isinstance(PRESSURE_BEFORE_CHANNEL, int):
raise ValueError("PRESSURE_BEFORE_CHANNEL 必须为整数通道号或 None")
if PRESSURE_BEFORE_CHANNEL < 0:
raise ValueError("PRESSURE_BEFORE_CHANNEL 不得为负数")
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")