从 MT2-AM8 模拟量模块迁移到 Easy521 PLC(Modbus TCP)
- 读取 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 中的硬件接线、配置默认值与模块职责描述
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 气体流量控制与阀门开环标定
|
||||
|
||||
本项目基于艾莫迅 **MT2-AM8 模拟量采集/输出模块**,提供三个可独立运行的在线入口和两个离线工具:
|
||||
本项目基于信捷 **Easy521 PLC**(Modbus TCP)提供三个可独立运行的在线入口和两个离线工具:
|
||||
|
||||
- [main.py](main.py):以 10 Hz 周期运行单回路气体流量闭环控制,支持增量式 PID,并可选“前馈反解打底 + PI 修残差”与按阀前压力缩放增益的增益调度;
|
||||
- [open_loop.py](open_loop.py):随机扫描阀门开度,采集阀前/阀后的压力与阀后流量,用于开环标定、动态特性观察和临界压力比分析。
|
||||
@@ -12,11 +12,11 @@
|
||||
|
||||
## 重要安全说明
|
||||
|
||||
> 本项目会直接控制真实阀门。首次运行前必须核对 IP、站号、AI/AO 通道、传感器量程、阀门行程方向、压力上限和执行机构的断电/失驱位置,并准备独立的机械或电气急停手段。
|
||||
> 本项目会直接控制真实阀门。首次运行前必须核对 IP、站号、寄存器地址、传感器量程、阀门行程方向、压力上限和执行机构的断电/失驱位置,并准备独立的机械或电气急停手段。
|
||||
|
||||
三个入口当前采用不同的阀门初始化与退出策略:
|
||||
|
||||
- **闭环 `main.py`**:启动时不再预先下发关闭位置,PID 内部输出由 `INITIAL_OPENING_PCT=100%` 开始;故障或退出时停止控制并断开 MT2-AM8,不主动下发关阀命令。当前系统按“执行机构失驱后回到全开”设计,现场必须确认实际硬件确实如此。
|
||||
- **闭环 `main.py`**:启动时不再预先下发关闭位置,PID 内部输出由 `INITIAL_OPENING_PCT=100%` 开始;故障或退出时停止控制并断开 PLC,不主动下发关阀命令。当前系统按“执行机构失驱后回到全开”设计,现场必须确认实际硬件确实如此。
|
||||
- **开环 `open_loop.py`**:连接成功后先主动设置 `0%` 开度,实验结束、异常或 `Ctrl+C` 时再次尝试设置 `0%` 开度,然后断开设备。
|
||||
- **手动测试 `test_valve.py`**:连接时仅按已知状态记录 `100%` 开度,不向电机写入初始位置;退出时不主动下发新开度,仅断开设备。该脚本不执行流量/压力越限和连续读取失败保护,只适合在具备独立安全措施的受控测试中使用。
|
||||
|
||||
@@ -24,23 +24,22 @@
|
||||
|
||||
## 硬件与默认接线
|
||||
|
||||
- MT2-AM8:Modbus TCP,默认 `192.168.1.12:502`,站号 `1`;
|
||||
- AI 输入寄存器:地址 `0x00~0x03`;
|
||||
- AO 保持寄存器:地址 `0x00~0x03`;
|
||||
- 模拟量原始范围:`0~4095`,传感器物理量换算使用 `819~4095` 对应有效量程;
|
||||
- 默认流量量程:`300 SLM`;默认压力量程:`1600 kPa`;
|
||||
- PLC(Easy521):Modbus TCP,默认 `192.168.1.88:502`,站号 `1`;
|
||||
- D 寄存器为直接 Modbus 地址:读用功能码 `03`(保持寄存器),写用功能码 `06`;
|
||||
- 模拟量原始范围:`0~32000`,线性对应各量程满量程;
|
||||
- 默认流量量程:`300 SLM`;阀后压力量程:`400 kPa`;阀前压力量程:`300 kPa`;
|
||||
- 电机行程范围:`0~1000`,且行程与阀门开度反向作用。
|
||||
|
||||
默认通道如下,通道编号均从 `0` 开始:
|
||||
默认寄存器地址与量程如下:
|
||||
|
||||
| 用途 | 通道 | 配置位置 |
|
||||
| --- | ---: | --- |
|
||||
| 闭环/阀后压力 | `AI0` | `config.PRESSURE_INPUT_CHANNEL` |
|
||||
| 闭环/阀后流量 | `AI1` | `config.FLOW_INPUT_CHANNEL` |
|
||||
| 阀前压力(前馈/增益调度/开环共用) | `AI2` | `config.PRESSURE_BEFORE_CHANNEL` / `open_loop.PRESSURE_BEFORE_CHANNEL` |
|
||||
| 电机位置指令 | `AO1` | `config.MOTOR_OUTPUT_CHANNEL` |
|
||||
| 用途 | 地址 | 量程 | 配置位置 |
|
||||
| --- | ---: | ---: | --- |
|
||||
| 阀后流量 | `D700` | `300 SLM` | `config.FLOW_AFTER_ADDR` |
|
||||
| 阀后压力 | `D710` | `400 kPa` | `config.PRESSURE_AFTER_ADDR` |
|
||||
| 阀前压力(前馈/增益调度/开环共用) | `D720` | `300 kPa` | `config.PRESSURE_BEFORE_ADDR` / `open_loop.PRESSURE_BEFORE_CHANNEL` |
|
||||
| 电机位置指令(0~10V) | `D730` | 行程 `0~1000` | `config.MOTOR_OUTPUT_ADDR` |
|
||||
|
||||
> 控流阀前的流量传感器(原 `AI3`)已删除,`open_loop.FLOW_BEFORE_CHANNEL` 现为 `None`:开环 CSV 的 `flow_before_slm` 列与 PNG 中的 “Flow before valve” 曲线将保持为空。
|
||||
> 控流阀前的流量传感器已删除,`open_loop.FLOW_BEFORE_CHANNEL` 现为 `None`:开环 CSV 的 `flow_before_slm` 列与 PNG 中的 “Flow before valve” 曲线将保持为空。
|
||||
|
||||
开度与行程的默认映射为:
|
||||
|
||||
@@ -49,7 +48,7 @@ opening = 0% -> motor_position = 1000(关闭)
|
||||
opening = 100% -> motor_position = 800(当前打开端/机械死区边界)
|
||||
```
|
||||
|
||||
虽然 `MT2AM8Client` 接受的物理行程仍为 `0~1000`,当前控制映射只使用 `800~1000`。这是近期根据阀门有效调节区间设置的打开端边界;逻辑 `100%` 不再对应物理行程 `0`。
|
||||
虽然硬件层行程坐标仍为 `0~1000`,当前控制映射只使用 `800~1000`。这是近期根据阀门有效调节区间设置的打开端边界;逻辑 `100%` 不再对应物理行程 `0`。
|
||||
|
||||
所有闭环通道、量程和行程端值集中在 [config.py](config.py);开环新增的阀前通道和实验参数位于 [open_loop.py](open_loop.py) 顶部。
|
||||
|
||||
@@ -97,7 +96,7 @@ python main.py --target 50
|
||||
启动流程为:
|
||||
|
||||
```text
|
||||
校验配置 -> 连接 MT2-AM8 -> 读取一次初始流量
|
||||
校验配置 -> 连接 PLC -> 读取一次初始流量
|
||||
-> PID 输出初始化为 INITIAL_OPENING_PCT
|
||||
-> 进入周期控制(启动阶段不主动写入关闭位置)
|
||||
```
|
||||
@@ -118,7 +117,7 @@ error = target - measurement
|
||||
opening_to_motor_position()
|
||||
|
|
||||
v
|
||||
反向行程 1000~800 -> MT2-AM8 AO1
|
||||
反向行程 1000~800 -> PLC D730
|
||||
```
|
||||
|
||||
增量 PID 的输出直接定义为阀门开度,因此控制器本身不包含电机行程或死区映射。目标流量不高于 `ZERO_FLOW_THRESHOLD_SLM`(当前 `0.5 SLM`)时,程序跳过常规 PID 更新并直接命令 `0%` 开度。
|
||||
@@ -187,9 +186,9 @@ Kp_eff = scale · Kp_base,Ki_eff = scale · Ki_base # Kd 不调度
|
||||
| `FEEDFORWARD_ENABLED` | `False` | 启用前馈反解打底 + PI 修残差 |
|
||||
| `GAIN_SCHEDULE_ENABLED` | `False` | 启用按阀前压力缩放 Kp/Ki |
|
||||
| `VALVE_MODEL_PATH` | `""` | 阀特性表 JSON 路径(空串 = 关闭前馈) |
|
||||
| `PRESSURE_BEFORE_CHANNEL` | `2` | 阀前压力 P1 的 AI 通道 |
|
||||
| `PRESSURE_BEFORE_ADDR` | `720` | 阀前压力 P1 的寄存器地址(D720) |
|
||||
|
||||
前馈生效需同时满足:`FEEDFORWARD_ENABLED=True`、`VALVE_MODEL_PATH` 指向有效 JSON、`PRESSURE_BEFORE_CHANNEL` 与 `PRESSURE_INPUT_CHANNEL` 均已配置。任一条件缺失时自动退化为纯 PID,并在启动日志中提示。
|
||||
前馈生效需同时满足:`FEEDFORWARD_ENABLED=True`、`VALVE_MODEL_PATH` 指向有效 JSON、`PRESSURE_BEFORE_ADDR` 与 `PRESSURE_AFTER_ADDR` 均已配置。任一条件缺失时自动退化为纯 PID,并在启动日志中提示。
|
||||
|
||||
### 运行输出
|
||||
|
||||
@@ -290,18 +289,20 @@ python test_valve.py
|
||||
|
||||
## 阀特性辨识与离线自测
|
||||
|
||||
前馈需要一张 `A_eff(x)` 阀特性表。先用 [open_loop.py](open_loop.py) 扫点得到开环 CSV,再用 [identify_valve.py](identify_valve.py) 离线拟合:
|
||||
前馈需要一张 `A_eff(x)` 阀特性表。先用 [open_loop.py](open_loop.py) 扫点得到开环 CSV(时间戳命名、从不覆盖),再用 [identify_valve.py](identify_valve.py) 离线拟合。`--csv` 支持多个路径与 glob 模式,可把多次实验合并重拟合,数据越多表越稳:
|
||||
|
||||
```powershell
|
||||
python identify_valve.py --csv open_loop_data/open_loop_YYYYMMDD_HHMMSS.csv --out valve_model.json [--tail 20] [--plot]
|
||||
python identify_valve.py --csv open_loop_data/open_loop_YYYYMMDD_HHMMSS.csv --out valve_model.json
|
||||
python identify_valve.py --csv "open_loop_data/*.csv" --out valve_model.json --plot
|
||||
python identify_valve.py --plot # 省略 --csv 时默认合并 open_loop_data/ 下全部 CSV
|
||||
```
|
||||
|
||||
- 按 `step_index` 分组,每组取末尾 `--tail` 个采样点平均,得到稳态 `(行程 x, 流量 Q, 阀前压 P1, 阀后压 P2)`;
|
||||
- 对每个稳态点反解 `A_eff = Q / (P1_abs · F(r))`;
|
||||
- 每个 CSV 按 `step_index` 分组,每组取末尾 `--tail` 个采样点平均,得到稳态 `(行程 x, 流量 Q, 阀前压 P1, 阀后压 P2)`,并按开度访问顺序标记逼近方向(上升 / 下降 / 该文件起始步);
|
||||
- 对每个稳态点反解 `A_eff = Q / (P1_abs · F(r))`;同一行程 x 的多个点(来自不同次实验)先取平均再构表;
|
||||
- 按行程升序构表并做单调性检查;若整体非单调,只保留最长单调连续段,其余点丢弃并给出警告;
|
||||
- 用 [valve_model.py](valve_model.py) 存成 JSON,并打印阻塞/亚声速点数等摘要;加 `--plot` 额外生成诊断图。
|
||||
- 用 [valve_model.py](valve_model.py) 存成 JSON,并打印合并文件数、稳态点数与阻塞/亚声速点统计等摘要;加 `--plot` 额外生成诊断图,实测点按逼近方向着色:同一行程下上升/下降两团点明显分离说明阀门迟滞显著,前馈表应考虑分方向处理;完全重叠则迟滞可忽略。
|
||||
|
||||
将 `config.VALVE_MODEL_PATH` 指到该 JSON、确认 `PRESSURE_BEFORE_CHANNEL` 接线无误、并把 `FEEDFORWARD_ENABLED` 设为 `True` 后,闭环即可启用前馈。
|
||||
将 `config.VALVE_MODEL_PATH` 指到该 JSON、确认 `PRESSURE_BEFORE_ADDR` 接线无误、并把 `FEEDFORWARD_ENABLED` 设为 `True` 后,闭环即可启用前馈。
|
||||
|
||||
[test_valve_model.py](test_valve_model.py) 是纯计算离线自测,不接触硬件,可在任意环境运行:
|
||||
|
||||
@@ -342,10 +343,11 @@ python test_valve_model.py
|
||||
|
||||
| 配置 | 当前默认值 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `MT2AM8_HOST` / `MT2AM8_PORT` / `MT2AM8_SLAVE_ID` | `192.168.1.12` / `502` / `1` | 通讯参数 |
|
||||
| `FLOW_INPUT_CHANNEL` / `PRESSURE_INPUT_CHANNEL` | `1` / `0` | 闭环 AI 通道;压力可设为 `None` |
|
||||
| `MOTOR_OUTPUT_CHANNEL` | `1` | 电机 AO 通道 |
|
||||
| `FLOW_METER_RANGE_SLM` / `PRESSURE_RANGE_KPA` | `300` / `1600` | 传感器量程 |
|
||||
| `PLC_HOST` / `PLC_PORT` / `PLC_SLAVE_ID` | `192.168.1.88` / `502` / `1` | PLC 通讯参数 |
|
||||
| `FLOW_AFTER_ADDR` / `FLOW_AFTER_RANGE_SLM` | `700` / `300` | 阀后流量 D700 与量程 |
|
||||
| `PRESSURE_AFTER_ADDR` / `PRESSURE_AFTER_RANGE_KPA` | `710` / `400` | 阀后压力 D710 与量程 |
|
||||
| `PRESSURE_BEFORE_ADDR` / `PRESSURE_BEFORE_RANGE_KPA` | `720` / `300` | 阀前压力 D720 与量程 |
|
||||
| `MOTOR_OUTPUT_ADDR` | `730` | 电机输出 D730(0~10V,行程 0~1000) |
|
||||
| `TARGET_FLOW_MIN_SLM` / `TARGET_FLOW_MAX_SLM` | `0` / `300` | 允许的目标范围 |
|
||||
| `TARGET_SWITCH_KEY` | `s` | 运行中按此键进入"输入新目标流量"模式 |
|
||||
| `CONTROL_PERIOD_S` / `MAX_CONTROL_DT_S` | `0.1` / `0.3` | 目标周期 / 超时阈值 |
|
||||
@@ -358,7 +360,7 @@ python test_valve_model.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` |
|
||||
| `PRESSURE_BEFORE_ADDR` | `720` | 阀前压力 P1 的寄存器地址(前馈/增益调度/开环共用) |
|
||||
| `VALVE_MODEL_PATH` | `""` | 阀特性表 JSON 路径;空串关闭前馈 |
|
||||
| `FEEDFORWARD_ENABLED` | `False` | 是否启用前馈反解打底 + PI 修残差 |
|
||||
| `FEEDFORWARD_CORRECTION_BAND_PCT` | `20` | 前馈开启时 PI 修正量的对称限幅(±%) |
|
||||
@@ -385,9 +387,9 @@ python test_valve_model.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` |
|
||||
| [identify_valve.py](identify_valve.py) | 从一份或多份开环扫点 CSV(支持 glob 合并)拟合 `A_eff(x)` 表,输出 `valve_model.json` |
|
||||
| [test_valve_model.py](test_valve_model.py) | 阀特性模型与前馈反解的离线自测 |
|
||||
| [PcControl.py](PcControl.py) | Modbus 硬件客户端;本项目三个入口实际使用 `MT2AM8Client` |
|
||||
| [PcControl.py](PcControl.py) | Modbus 硬件客户端;本项目三个入口实际使用 `Easy521ModbusClient` |
|
||||
|
||||
## 项目结构
|
||||
|
||||
|
||||
Reference in New Issue
Block a user