完善流量控制前馈、稳态判定与阀门标定流程

- 前馈冻结改用可配置的 3 秒流量绝对误差滑窗,修复采样抖动造成的重复解冻,并保留下游扰动后的自动更新
- 支持非单调阀特性反解、最小可测面积以下直接全关,以及闭阀端精细扫点与辨识开关
- 调整双压力判稳、最长等待时间、在线入口全开收尾和闭环四联图
- 更新配置、README、阀模型及离线测试,归档本轮实验数据与诊断产物
This commit is contained in:
2026-09-02 16:14:42 +08:00
parent 5888b4ccce
commit cfeedcf887
80 changed files with 40473 additions and 171 deletions
+40 -4
View File
@@ -3,8 +3,8 @@
示例:
python main.py --target 50
程序启动后直接从当前阀门开度(默认全开)开始闭环。Ctrl+C、控制故障或
其他异常会断开 PLC;阀门不被驱动时默认回到全开
程序连接 PLC 后先主动设置全开,再开始采样与闭环。Ctrl+C、控制故障或
其他异常退出时会再次尝试设置全开,然后断开 PLC
"""
import argparse
@@ -126,10 +126,12 @@ def build_control_loop(hardware, logger):
opening_rate_far_pct_s=config.MAX_OPENING_RATE_FAR_PCT_S,
opening_rate_near_pct_s=config.MAX_OPENING_RATE_NEAR_PCT_S,
pid_near_error_min_slm=config.PID_NEAR_ERROR_MIN_SLM,
pid_near_error_max_slm=config.PID_NEAR_ERROR_MAX_SLM,
pid_near_error_target_ratio=(
config.PID_NEAR_ERROR_TARGET_RATIO
),
pid_far_error_min_slm=config.PID_FAR_ERROR_MIN_SLM,
pid_far_error_max_slm=config.PID_FAR_ERROR_MAX_SLM,
pid_far_error_target_ratio=config.PID_FAR_ERROR_TARGET_RATIO,
pid_mode_switch_confirm_cycles=(
config.PID_MODE_SWITCH_CONFIRM_CYCLES
@@ -137,6 +139,10 @@ def build_control_loop(hardware, logger):
valve_model=valve_model,
feedforward_enabled=config.FEEDFORWARD_ENABLED,
feedforward_correction_band_pct=config.FEEDFORWARD_CORRECTION_BAND_PCT,
feedforward_update_error_threshold_slm=(
config.FEEDFORWARD_UPDATE_ERROR_THRESHOLD_SLM
),
feedforward_update_window_s=config.FEEDFORWARD_UPDATE_WINDOW_S,
gain_schedule_enabled=config.GAIN_SCHEDULE_ENABLED,
gain_schedule_ref_abs_kpa=config.GAIN_SCHEDULE_REF_ABS_KPA,
gain_schedule_floor_abs_kpa=config.GAIN_SCHEDULE_FLOOR_ABS_KPA,
@@ -246,6 +252,21 @@ def run(target_flow_slm):
},
)
# 连接后的全开动作发生在初始采样、报告计时和控制计时开始之前。
if not hardware.set_motor_position(
config.MOTOR_OPEN_POSITION,
channel=config.MOTOR_OUTPUT_ADDR,
):
raise FlowControlFault(
"STARTUP_FULL_OPEN_FAILED",
"STARTUP",
"连接后设置阀门 100% 开度失败",
)
logger.info(
"PLC 已连接;阀门已设置为 100%% 开度(行程 %.1f),不计入控制计时",
config.MOTOR_OPEN_POSITION,
)
# 控制前读取一次当前流量,作为超调量方向的基准。
try:
initial_flow_slm = hardware.get_flow(config.FLOW_AFTER_ADDR)
@@ -257,7 +278,7 @@ def run(target_flow_slm):
window_s=config.STEADY_STATE_WINDOW_S,
sample_period_s=config.CONTROL_PERIOD_S,
band_pct=config.STEADY_STATE_BAND_PCT,
std_pct=config.STEADY_STATE_STD_PCT,
pressure_band_kpa=config.STEADY_STATE_PRESSURE_BAND_KPA,
flow_range_min_slm=config.TARGET_FLOW_MIN_SLM,
flow_range_max_slm=config.TARGET_FLOW_MAX_SLM,
initial_flow_slm=initial_flow_slm,
@@ -377,6 +398,21 @@ def run(target_flow_slm):
finally:
console_gate.blocked = False
if connected:
try:
if hardware.set_motor_position(
config.MOTOR_OPEN_POSITION,
channel=config.MOTOR_OUTPUT_ADDR,
):
logger.info(
"断开 PLC 前已设置阀门为 100%% 开度(行程 %.1f),不计入控制计时",
config.MOTOR_OPEN_POSITION,
)
else:
exit_code = max(exit_code, 4)
logger.critical("断开 PLC 前设置阀门 100%% 开度失败")
except Exception:
exit_code = max(exit_code, 4)
logger.exception("断开 PLC 前设置阀门 100%% 开度时发生异常")
try:
hardware.disconnect()
except Exception:
@@ -388,7 +424,7 @@ def run(target_flow_slm):
image_path = recorder.finalize()
logger.info("控制数据已保存:%s", recorder.csv_path)
if image_path is not None:
logger.info("联曲线图已保存:%s", image_path)
logger.info("联曲线图已保存:%s", image_path)
else:
logger.warning("本次运行没有采样点,因此未生成曲线图")
except Exception: