From abab25326a6c001ee9b9d3664e86b0b0b054c9df Mon Sep 17 00:00:00 2001 From: louissun Date: Tue, 1 Sep 2026 04:04:34 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=89=8D=E9=A6=88+=E5=A2=9E?= =?UTF-8?q?=E7=9B=8A=E8=B0=83=E5=BA=A6=EF=BC=88MFC=20=E7=BB=93=E6=9E=84?= =?UTF-8?q?=EF=BC=89=E7=9A=84=20README=20=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 118 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 104 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 9dc58f1..4292666 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # 气体流量控制与阀门开环标定 -本项目基于艾莫迅 **MT2-AM8 模拟量采集/输出模块**,提供三个可独立运行的入口: +本项目基于艾莫迅 **MT2-AM8 模拟量采集/输出模块**,提供三个可独立运行的在线入口和两个离线工具: -- [main.py](main.py):以 10 Hz 周期运行单回路气体流量增量式 PID 闭环控制; -- [open_loop.py](open_loop.py):随机扫描阀门开度,采集阀前/阀后的压力与流量,用于开环标定、动态特性观察和临界压力比分析。 +- [main.py](main.py):以 10 Hz 周期运行单回路气体流量闭环控制,支持增量式 PID,并可选“前馈反解打底 + PI 修残差”与按阀前压力缩放增益的增益调度; +- [open_loop.py](open_loop.py):随机扫描阀门开度,采集阀前/阀后的压力与阀后流量,用于开环标定、动态特性观察和临界压力比分析。 - [test_valve.py](test_valve.py):在 Windows 终端中手动切换阀门开度,同时观察并记录流量、压力和电机行程。 +- [identify_valve.py](identify_valve.py):离线从开环扫点 CSV 拟合阀特性表,产出供前馈使用的 `valve_model.json`。 +- [test_valve_model.py](test_valve_model.py):阀特性模型与前馈反解的离线自测,不接触硬件。 -三个入口都会持续保存 CSV,并在运行结束时为有效采样数据生成 PNG 曲线图。闭环程序还会持续进行滑动窗口稳态判定,并在首次达到稳态时输出一次控制性能报告。 +三个在线入口都会持续保存 CSV,并在运行结束时为有效采样数据生成 PNG 曲线图。闭环程序还会持续进行滑动窗口稳态判定,并在首次达到稳态时输出一次控制性能报告。 ## 重要安全说明 @@ -35,10 +37,11 @@ | --- | ---: | --- | | 闭环/阀后压力 | `AI0` | `config.PRESSURE_INPUT_CHANNEL` | | 闭环/阀后流量 | `AI1` | `config.FLOW_INPUT_CHANNEL` | -| 开环阀前压力 | `AI2` | `open_loop.PRESSURE_BEFORE_CHANNEL` | -| 开环阀前流量 | `AI3` | `open_loop.FLOW_BEFORE_CHANNEL` | +| 阀前压力(前馈/增益调度/开环共用) | `AI2` | `config.PRESSURE_BEFORE_CHANNEL` / `open_loop.PRESSURE_BEFORE_CHANNEL` | | 电机位置指令 | `AO1` | `config.MOTOR_OUTPUT_CHANNEL` | +> 控流阀前的流量传感器(原 `AI3`)已删除,`open_loop.FLOW_BEFORE_CHANNEL` 现为 `None`:开环 CSV 的 `flow_before_slm` 列与 PNG 中的 “Flow before valve” 曲线将保持为空。 + 开度与行程的默认映射为: ```text @@ -143,13 +146,58 @@ NEAR -> FAR :|error| >= far_threshold,连续满足 10 个采样周期 运行中修改目标后,模式确认计数会清零。如果尚无有效流量,或新目标相对最后有效流量的误差大于 `near_threshold`,控制器立即回到 `FAR`;若误差仍处于 NEAR 阈值内,则保持当前模式。PID 的累计输出和误差历史不会因修改目标而整体重置,性能报告器则会重置并从新目标重新计时、判稳和统计超调。 +### 前馈 + 增益调度(MFC 结构) + +闭环默认仍为纯增量式 PID;在确认阀前压力接线、并用 [identify_valve.py](identify_valve.py) 拟合出阀特性表后,可切换到“前馈反解打底 + PI 修残差”的市面 MFC 结构,并可叠加按阀前压力缩放增益的增益调度。 + +静态阀特性建模为: + +```text +Q_ss = A_eff(x) · P1_abs · F(r) + +F(r) 为压比函数,r = P2_abs / P1_abs: + 阻塞流(r <= 0.528)时 F = 1; + 亚声速(0.528 < r < 1)时按 ISO 6358 椭圆平滑衰减; + r >= 1 时无正向流,F = 0。 +``` + +开启前馈后,控制周期内的输出计算变为: + +```text +前馈开度 opening_ff = 反解(目标流量, 阀前压 P1, 阀后压 P2) +PI 修正量 correction = PI(目标流量 - 实测流量) # 输出限幅改为对称 ±带 +开度 = clamp(opening_ff + correction, 0, 100) +``` + +即前馈把开度送到稳态工作点附近,PI 只负责修正模型残差和动态偏差;PI 输出不再是 `0~100%` 的全行程,而是围绕前馈的小幅修正(限幅由 `FEEDFORWARD_CORRECTION_BAND_PCT` 控制,默认 ±20%)。前馈无法反解时(例如压比下无正向流、压力缺失)会自动退化为纯 PID。 + +增益调度把 FAR/NEAR 的 Kp、Ki 按阀前绝对压力缩放,摊平 K_x ∝ P1 的非线性: + +```text +scale = GAIN_SCHEDULE_REF_ABS_KPA / max(P1_abs, GAIN_SCHEDULE_FLOOR_ABS_KPA) +Kp_eff = scale · Kp_base,Ki_eff = scale · Ki_base # Kd 不调度 +``` + +`scale` 相对变化超过 `GAIN_SCHEDULE_SCALE_CHANGE_THRESHOLD`(默认 5%)才重算并写入 PID,避免每个周期都更新增益。 + +三项均通过 `config.py` 中的开关启用,默认全部关闭: + +| 开关 | 默认 | 说明 | +| --- | --- | --- | +| `FEEDFORWARD_ENABLED` | `False` | 启用前馈反解打底 + PI 修残差 | +| `GAIN_SCHEDULE_ENABLED` | `False` | 启用按阀前压力缩放 Kp/Ki | +| `VALVE_MODEL_PATH` | `""` | 阀特性表 JSON 路径(空串 = 关闭前馈) | +| `PRESSURE_BEFORE_CHANNEL` | `2` | 阀前压力 P1 的 AI 通道 | + +前馈生效需同时满足:`FEEDFORWARD_ENABLED=True`、`VALVE_MODEL_PATH` 指向有效 JSON、`PRESSURE_BEFORE_CHANNEL` 与 `PRESSURE_INPUT_CHANNEL` 均已配置。任一条件缺失时自动退化为纯 PID,并在启动日志中提示。 + ### 运行输出 每次闭环运行会产生: 1. **终端与日志**:约每秒输出一次状态;非 `OK` 状态立即输出。日志保存到 `logs/flow_control.log`。 2. **逐周期 CSV**:默认保存为 `data/flow_run_YYYYMMDD_HHMMSS.csv`,每行写入后立即刷新到磁盘。现有 `data/日期/实验说明/` 子目录是历史数据的人工归档方式,不是程序自动创建的层级。 -3. **三联曲线图**:结束时保存同名 PNG,依次显示目标/实际流量、阀门开度和压力。 +3. **三联曲线图**:结束时保存同名 PNG,依次显示目标/实际流量、阀门开度和压力;启用前馈时,开度子图会叠加虚线显示前馈开度。 4. **性能报告**:首次达到稳态时写入终端和日志;若整个运行过程未达到稳态,只输出未达到稳态的提示。 闭环 CSV 字段: @@ -159,7 +207,11 @@ NEAR -> FAR :|error| >= far_threshold,连续满足 10 个采样周期 | `time_s` / `timestamp` | 相对运行时间 / Unix 时间戳 | | `target_flow_slm` / `measured_flow_slm` | 目标流量 / 实测流量 | | `opening_pct` / `motor_position` | 阀门开度 / 电机行程 | -| `pressure_kpa` | 压力;未启用或读取失败时为空 | +| `pressure_kpa` | 阀后压力;未启用或读取失败时为空 | +| `pressure_before_kpa` | 阀前压力 P1;未启用或读取失败时为空 | +| `feedforward_pct` | 前馈开度;未启用前馈时为空 | +| `correction_pct` | PI 修正量(±带内);未启用前馈时为空 | +| `gain_scale` | 增益调度缩放系数;未启用调度时为 `1.0` | | `error_slm` | `目标流量 - 实测流量` | | `actual_dt_s` | 实际控制周期 | | `status` | `OK` 或传感器读取重试状态 | @@ -177,10 +229,10 @@ NEAR -> FAR :|error| >= far_threshold,连续满足 10 个采样周期 ## 开环扫点实验 -[open_loop.py](open_loop.py) 不参与闭环反馈,也不会被 `main.py` 导入。默认将 `0%~100%`、间隔 `10%` 的 11 个开度按固定随机种子 `42` 打乱,然后逐点执行: +[open_loop.py](open_loop.py) 不参与闭环反馈,也不会被 `main.py` 导入。默认将 `0%~100%`、间隔 `5%` 的 21 个开度按固定随机种子 `42` 打乱,然后逐点执行: ```text -写入开度 -> 每 0.1 s 采集阀前/阀后压力与流量 +写入开度 -> 每 0.1 s 采集阀前/阀后压力与阀后流量 -> 等待阀后流量稳定,或等待满 60 s -> 切换到下一个开度 ``` @@ -207,7 +259,7 @@ std <= max(STEADY_STATE_STD_PCT / 100 * |窗口均值|, MIN_ABS_STD_SLM) 默认窗口为 10 秒,绝对标准差下限为 `0.05 SLM`。阀后流量连续 5 次读取失败会中止实验;其他通道读取失败会在对应 CSV 字段中留空。 -输出保存到 `open_loop_data/open_loop_YYYYMMDD_HHMMSS.csv/.png`,日志保存到 `logs/open_loop.log`。PNG 包含三张子图:阀前/阀后压力、阀前/阀后流量、阀后绝对压力与阀前绝对压力之比。 +输出保存到 `open_loop_data/open_loop_YYYYMMDD_HHMMSS.csv/.png`,日志保存到 `logs/open_loop.log`。PNG 包含三张子图:阀前/阀后压力、阀前/阀后流量、阀后绝对压力与阀前绝对压力之比。由于阀前流量传感器已删除,“阀前流量”曲线与 `flow_before_slm` 列保持为空。 压力比按下式计算: @@ -236,6 +288,29 @@ python test_valve.py > 此脚本不会调用 `config.validate_config()`,也没有闭环/开环程序中的流量越限、压力越限或连续读取失败保护。运行前应人工完成配置核对,并确保测试人员能够立即切断气源或执行急停。 +## 阀特性辨识与离线自测 + +前馈需要一张 `A_eff(x)` 阀特性表。先用 [open_loop.py](open_loop.py) 扫点得到开环 CSV,再用 [identify_valve.py](identify_valve.py) 离线拟合: + +```powershell +python identify_valve.py --csv open_loop_data/open_loop_YYYYMMDD_HHMMSS.csv --out valve_model.json [--tail 20] [--plot] +``` + +- 按 `step_index` 分组,每组取末尾 `--tail` 个采样点平均,得到稳态 `(行程 x, 流量 Q, 阀前压 P1, 阀后压 P2)`; +- 对每个稳态点反解 `A_eff = Q / (P1_abs · F(r))`; +- 按行程升序构表并做单调性检查;若整体非单调,只保留最长单调连续段,其余点丢弃并给出警告; +- 用 [valve_model.py](valve_model.py) 存成 JSON,并打印阻塞/亚声速点数等摘要;加 `--plot` 额外生成诊断图。 + +将 `config.VALVE_MODEL_PATH` 指到该 JSON、确认 `PRESSURE_BEFORE_CHANNEL` 接线无误、并把 `FEEDFORWARD_ENABLED` 设为 `True` 后,闭环即可启用前馈。 + +[test_valve_model.py](test_valve_model.py) 是纯计算离线自测,不接触硬件,可在任意环境运行: + +```powershell +python test_valve_model.py +``` + +它校验压比函数 `F(r)`、绝对压力换算、`A_eff` 插值与前馈反解互逆,以及 `identify_valve` 端到端辨识。 + ## 安全与故障处理 ### 闭环 @@ -283,6 +358,14 @@ python test_valve.py | `PID_MODE_SWITCH_CONFIRM_CYCLES` | `10` | 模式切换条件需连续满足的采样周期数(当前约 1 秒) | | `MOTOR_CLOSED_POSITION` / `MOTOR_OPEN_POSITION` | `1000` / `800` | 当前反向控制区间;打开端为机械死区边界 | | `INITIAL_OPENING_PCT` | `100` | 闭环 PID 内部初始输出 | +| `PRESSURE_BEFORE_CHANNEL` | `2` | 阀前压力 P1 的 AI 通道(前馈/增益调度/开环共用);可设为 `None` | +| `VALVE_MODEL_PATH` | `""` | 阀特性表 JSON 路径;空串关闭前馈 | +| `FEEDFORWARD_ENABLED` | `False` | 是否启用前馈反解打底 + PI 修残差 | +| `FEEDFORWARD_CORRECTION_BAND_PCT` | `20` | 前馈开启时 PI 修正量的对称限幅(±%) | +| `GAIN_SCHEDULE_ENABLED` | `False` | 是否启用按阀前压力缩放 Kp/Ki | +| `GAIN_SCHEDULE_REF_ABS_KPA` | `501.325` | 满压调好基准增益对应的绝对压力(400 + 101.325) | +| `GAIN_SCHEDULE_FLOOR_ABS_KPA` | `60` | 绝对压力下限,防止 P1 很低时增益爆炸 | +| `GAIN_SCHEDULE_SCALE_CHANGE_THRESHOLD` | `0.05` | scale 相对变化超过此值才重算增益 | | `FLOW_VALID_MIN_SLM` / `FLOW_VALID_MAX_SLM` | `0` / `300` | 流量安全范围 | | `MAX_PRESSURE_KPA` | `500` | 压力安全上限,须按实际管路额定压力确认 | | `MAX_CONSECUTIVE_FLOW_FAILURES` / `MAX_CONSECUTIVE_PRESSURE_FAILURES` | `3` / `3` | 闭环连续读取失败阈值 | @@ -294,13 +377,16 @@ python test_valve.py | 文件 | 职责 | | --- | --- | | [main.py](main.py) | 闭环入口:参数解析、日志、控制循环、记录器、性能报告与收尾 | -| [flow_control.py](flow_control.py) | 闭环控制层、传感器读取、开度/行程映射和故障处理 | +| [flow_control.py](flow_control.py) | 闭环控制层、传感器读取、开度/行程映射、前馈与增益调度、故障处理 | | [controllers.py](controllers.py) | 通用增量式 PID,含输出限幅和输出变化率限幅 | | [config.py](config.py) | 闭环集中配置和启动前校验 | -| [data_logger.py](data_logger.py) | 闭环逐周期 CSV 与三联曲线图 | +| [data_logger.py](data_logger.py) | 闭环逐周期 CSV 与三联曲线图(含前馈/修正/增益调度列) | | [performance_reporter.py](performance_reporter.py) | 闭环滑窗判稳和一次性性能报告 | -| [open_loop.py](open_loop.py) | 独立开环扫点、四传感器记录和压力比分析 | +| [open_loop.py](open_loop.py) | 独立开环扫点、传感器记录和压力比分析 | | [test_valve.py](test_valve.py) | Windows 终端手动开度测试、每秒采样、CSV 与三联图 | +| [valve_model.py](valve_model.py) | 静态阀特性模型 `Q_ss = A_eff(x)·P1_abs·F(r)` 与前馈反解(纯计算) | +| [identify_valve.py](identify_valve.py) | 从开环扫点 CSV 拟合 `A_eff(x)` 表,输出 `valve_model.json` | +| [test_valve_model.py](test_valve_model.py) | 阀特性模型与前馈反解的离线自测 | | [PcControl.py](PcControl.py) | Modbus 硬件客户端;本项目三个入口实际使用 `MT2AM8Client` | ## 项目结构 @@ -315,10 +401,14 @@ flow_control/ ├── config.py ├── data_logger.py ├── performance_reporter.py +├── valve_model.py # 静态阀特性模型与前馈反解 +├── identify_valve.py # 从开环 CSV 拟合阀特性表 +├── test_valve_model.py # 阀特性模型离线自测 ├── PcControl.py ├── README.md ├── data/ # 闭环 CSV / PNG;历史数据可能已人工分组 ├── open_loop_data/ # 开环 CSV / PNG(首次运行时创建) ├── test_valve_data/ # 手动阀门测试 CSV / PNG +├── valve_model.json # identify_valve.py 产出的阀特性表(供前馈使用) └── logs/ # flow_control.log / open_loop.log ```