实现流量控制前馈 + 增益调度(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 完全一致。
This commit is contained in:
2026-08-27 16:00:51 +08:00
parent d7e712ede7
commit a03735387c
7 changed files with 958 additions and 20 deletions
+25
View File
@@ -13,8 +13,12 @@ CSV_FIELDS = (
"measured_flow_slm",
"opening_pct",
"pressure_kpa",
"pressure_before_kpa",
"error_slm",
"motor_position",
"feedforward_pct",
"correction_pct",
"gain_scale",
"actual_dt_s",
"status",
)
@@ -59,8 +63,14 @@ class FlowRunRecorder:
),
"opening_pct": self._format_value(result.opening_pct),
"pressure_kpa": self._format_value(result.pressure_kpa),
"pressure_before_kpa": self._format_value(
result.pressure_before_kpa
),
"error_slm": self._format_value(result.error_slm),
"motor_position": self._format_value(result.motor_position),
"feedforward_pct": self._format_value(result.feedforward_pct),
"correction_pct": self._format_value(result.correction_pct),
"gain_scale": self._format_value(result.gain_scale),
"actual_dt_s": self._format_value(result.actual_dt_s),
"status": result.status,
}
@@ -93,6 +103,7 @@ class FlowRunRecorder:
target_flow = []
measured_flow = []
opening = []
feedforward = []
pressure = []
with self.csv_path.open("r", newline="", encoding="utf-8-sig") as file:
@@ -103,6 +114,9 @@ class FlowRunRecorder:
self._float_or_nan(row["measured_flow_slm"])
)
opening.append(self._float_or_nan(row["opening_pct"]))
feedforward.append(
self._float_or_nan(row.get("feedforward_pct"))
)
pressure.append(self._float_or_nan(row["pressure_kpa"]))
figure, axes = plt.subplots(
@@ -138,7 +152,18 @@ class FlowRunRecorder:
opening,
color="#2E7D32",
linewidth=1.4,
label="Opening",
)
if any(math.isfinite(value) for value in feedforward):
axes[1].plot(
time_s,
feedforward,
color="#EF6C00",
linewidth=1.2,
linestyle="--",
label="Feedforward",
)
axes[1].legend(loc="best")
axes[1].set_ylabel("Opening (%)")
axes[1].set_title("Valve opening")
axes[1].set_ylim(-2, 102)