diff --git a/open_loop.py b/open_loop.py index d624983..ae3138d 100644 --- a/open_loop.py +++ b/open_loop.py @@ -6,8 +6,8 @@ 2. 逐个开度:写入电机行程 → 以固定周期采集控流阀前后 4 个传感器 (前压力、前流量、后压力、后流量)→ 等待“控流阀后流量”进入稳态后 切换到下一个开度; -3. 全程逐周期写 CSV,结束后在单个窗口内输出两张子图(前后压力、前后流量), - 保存到工程目录下的 open_loop_data/。 +3. 全程逐周期写 CSV,结束后在单个窗口内输出三张子图(前后压力、前后流量、 + 绝对压力比),保存到工程目录下的 open_loop_data/。 示例: python open_loop.py @@ -71,6 +71,11 @@ PLOT_DPI = config.PLOT_DPI ANNOTATE_STEPS = True # 曲线上标注开度步 +# 绝对压力 = 表压读数 + 大气压;临界压力比用于判断是否出现壅塞(choked)流动。 +ATMOSPHERIC_PRESSURE_KPA = 101.325 # 标准大气压(kPa) +CRITICAL_PRESSURE_RATIO = 0.528 # 临界压力比(阀后绝对压力 / 阀前绝对压力) + + CSV_FIELDS = ( "time_s", "step_index", @@ -80,6 +85,8 @@ CSV_FIELDS = ( "flow_after_slm", "pressure_before_kpa", "pressure_after_kpa", + "P_abs_ratio", + "is_ratio_smaller_than_0.528", ) @@ -140,7 +147,7 @@ class SteadyStateDetector: # --------------------------------------------------------------------------- class OpenLoopRecorder: - """逐周期写 CSV,结束时输出两张子图(前后压力、前后流量)。""" + """逐周期写 CSV,结束时输出三张子图(前后压力、前后流量、绝对压力比)。""" def __init__(self, output_directory, *, plot_dpi=160): output_dir = Path(output_directory) @@ -176,6 +183,18 @@ class OpenLoopRecorder: if self._closed: raise RuntimeError("记录器已关闭") + p_abs_ratio = self._absolute_pressure_ratio( + pressure_before_kpa, pressure_after_kpa + ) + if p_abs_ratio is None: + p_abs_ratio_text = "" + is_smaller_text = "" + else: + p_abs_ratio_text = f"{p_abs_ratio:.6f}" + is_smaller_text = ( + "Y" if p_abs_ratio < CRITICAL_PRESSURE_RATIO else "N" + ) + self._writer.writerow( { "time_s": self._fmt(time_s), @@ -186,6 +205,8 @@ class OpenLoopRecorder: "flow_after_slm": self._fmt(flow_after_slm), "pressure_before_kpa": self._fmt(pressure_before_kpa), "pressure_after_kpa": self._fmt(pressure_after_kpa), + "P_abs_ratio": p_abs_ratio_text, + "is_ratio_smaller_than_0.528": is_smaller_text, } ) self._file.flush() @@ -195,6 +216,21 @@ class OpenLoopRecorder: self.steps.append((float(time_s), float(opening_pct))) self._last_step_index = step_index + @staticmethod + def _absolute_pressure_ratio(pressure_before_kpa, pressure_after_kpa): + """阀后绝对压力 / 阀前绝对压力;读数缺失或分母非法时返回 None。""" + if pressure_before_kpa is None or pressure_after_kpa is None: + return None + try: + before_abs = float(pressure_before_kpa) + ATMOSPHERIC_PRESSURE_KPA + after_abs = float(pressure_after_kpa) + ATMOSPHERIC_PRESSURE_KPA + except (TypeError, ValueError): + return None + if before_abs <= 0: + return None + ratio = after_abs / before_abs + return ratio if math.isfinite(ratio) else None + def finalize(self): """关闭 CSV 并生成 PNG;无采样点时只保留 CSV。""" self.close() @@ -221,6 +257,7 @@ class OpenLoopRecorder: flow_after = [] pressure_before = [] pressure_after = [] + p_abs_ratio = [] with self.csv_path.open("r", newline="", encoding="utf-8-sig") as file: for row in csv.DictReader(file): @@ -233,11 +270,12 @@ class OpenLoopRecorder: pressure_after.append( self._float_or_nan(row["pressure_after_kpa"]) ) + p_abs_ratio.append(self._float_or_nan(row["P_abs_ratio"])) figure, axes = plt.subplots( - 2, + 3, 1, - figsize=(12, 9), + figsize=(12, 12), sharex=True, constrained_layout=True, ) @@ -264,10 +302,25 @@ class OpenLoopRecorder: "Flow after valve", "#6A1B9A", ) axes[1].set_ylabel("Flow (SLM)") - axes[1].set_xlabel("Time (s)") axes[1].set_title("Flow across control valve") axes[1].legend(loc="best") + self._plot_series( + axes[2], time_s, p_abs_ratio, + "P_abs_ratio (after/before)", "#EF6C00", + ) + axes[2].axhline( + CRITICAL_PRESSURE_RATIO, + color="#B71C1C", + linestyle="--", + linewidth=1.2, + label=f"Critical ratio {CRITICAL_PRESSURE_RATIO:.3f}", + ) + axes[2].set_ylabel("P_abs_ratio") + axes[2].set_xlabel("Time (s)") + axes[2].set_title("Absolute pressure ratio across control valve") + axes[2].legend(loc="best") + if ANNOTATE_STEPS and self.steps: y_top = axes[1].get_ylim()[1] for step_time, opening in self.steps: