Files
flow_control/test_flow_control.py
T
louis cfeedcf887 完善流量控制前馈、稳态判定与阀门标定流程
- 前馈冻结改用可配置的 3 秒流量绝对误差滑窗,修复采样抖动造成的重复解冻,并保留下游扰动后的自动更新
- 支持非单调阀特性反解、最小可测面积以下直接全关,以及闭阀端精细扫点与辨识开关
- 调整双压力判稳、最长等待时间、在线入口全开收尾和闭环四联图
- 更新配置、README、阀模型及离线测试,归档本轮实验数据与诊断产物
2026-09-02 16:14:42 +08:00

97 lines
3.3 KiB
Python

"""flow_control 前馈误差滑窗的离线自测(不连接硬件)。"""
from collections import deque
import pytest
from flow_control import FlowControlLoop
def _pid_threshold_loop():
"""只构造测试 FAR/NEAR 阈值计算所需的状态。"""
loop = FlowControlLoop.__new__(FlowControlLoop)
loop.pid_near_error_min_slm = 1.0
loop.pid_near_error_max_slm = 3.0
loop.pid_near_error_target_ratio = 0.05
loop.pid_far_error_min_slm = 4.0
loop.pid_far_error_max_slm = 12.0
loop.pid_far_error_target_ratio = 0.20
return loop
@pytest.mark.parametrize(
("target", "expected_near", "expected_far"),
[
(10.0, 1.0, 4.0), # 比例值低于下限
(30.0, 1.5, 6.0), # 比例值位于上下限之间
(60.0, 3.0, 12.0), # 比例值恰好达到上限
(300.0, 3.0, 12.0), # 比例值超过上限
],
)
def test_pid_error_thresholds_are_clamped(target, expected_near, expected_far):
loop = _pid_threshold_loop()
near, far = loop._pid_error_thresholds(target)
assert near == pytest.approx(expected_near)
assert far == pytest.approx(expected_far)
def _feedforward_window_loop():
"""只构造测试滑窗所需的最小 FlowControlLoop 状态。"""
loop = FlowControlLoop.__new__(FlowControlLoop)
loop.target_flow_slm = 100.0
loop.feedforward_update_window_s = 3.0
loop._feedforward_error_window = deque()
loop._feedforward_window_ready = False
return loop
def test_feedforward_window_stays_ready_despite_sampling_jitter():
loop = _feedforward_window_loop()
# 首个完整 3 s 窗口内误差恒为 2 SLM。
sample_times = [0.0, 0.099]
sample_times.extend(0.201 + 0.1 * index for index in range(28))
sample_times.append(3.0)
means = [
loop._feedforward_window_mean_abs_error(timestamp, 98.0)
for timestamp in sample_times
]
assert all(value is None for value in means[:-1])
assert means[-1] == pytest.approx(2.0)
assert loop._feedforward_window_ready
# 下一周期淘汰旧样本后,队列首尾跨度仅 2.899 s。窗口仍应保持
# ready,并返回最近 3 s 的误差均值,不能因约 0.1 s 抖动返回 None。
mean = loop._feedforward_window_mean_abs_error(3.1, 98.0)
span = (
loop._feedforward_error_window[-1][0]
- loop._feedforward_error_window[0][0]
)
assert span == pytest.approx(2.899)
assert mean == pytest.approx(2.0)
assert loop._feedforward_window_ready
def test_ready_window_keeps_tracking_recent_error_and_can_reset():
loop = _feedforward_window_loop()
loop._feedforward_window_mean_abs_error(0.0, 98.0)
assert loop._feedforward_window_mean_abs_error(3.0, 98.0) == pytest.approx(2.0)
# 就绪状态不会冻结均值本身;最近 3 s 都变成 10 SLM 误差后,
# 返回的新均值会超过默认 5 SLM 阈值,从而触发上层前馈更新。
for index in range(1, 32):
mean = loop._feedforward_window_mean_abs_error(
3.0 + index * 0.1,
90.0,
)
assert mean == pytest.approx(10.0)
loop._reset_feedforward_error_window()
assert not loop._feedforward_window_ready
assert not loop._feedforward_error_window
assert loop._feedforward_window_mean_abs_error(10.0, 90.0) is None