瀏覽代碼

功图:长窗口慢趋势分析(数月/加密),验证能抓缓慢阶跃;7号机六缸盖侧为传感器失效

18922397810 3 周之前
父節點
當前提交
efdf1512aa
共有 2 個文件被更改,包括 63 次插入 和 1 次删除
  1. 54 1
      backend/indicator_features/trend.py
  2. 9 0
      backend/indicator_features/观察记录.md

+ 54 - 1
backend/indicator_features/trend.py

@@ -28,7 +28,7 @@ def load_features(path):
     rows = list(csv.DictReader(open(path, encoding="utf-8-sig")))
     for row in rows:
         row["day"] = row["sample_time"][:10]
-        for name in TREND_FEATURES:
+        for name in TREND_FEATURES + ("indicated_work",):
             try:
                 row[name] = float(row[name])
             except (TypeError, ValueError):
@@ -94,6 +94,58 @@ def peer_sudden_changes(rows, feature, segments):
                 print(f"   {cylinder}缸{side}: 偏差 {values[i-1]*100:+.1f}% → {values[i]*100:+.1f}% 于 {days[i]} ({delta/scale:+.1f}×)  {' '.join(flags)}")
 
 
+def _fit_drift(days, values):
+    x = np.array([(datetime.strptime(d, "%Y-%m-%d") - datetime.strptime(days[0], "%Y-%m-%d")).days for d in days], dtype=float)
+    y = np.asarray(values, dtype=float)
+    if x[-1] <= 0 or len(y) < 8:
+        return None
+    slope, intercept = np.polyfit(x, y, 1)
+    pred = slope * x + intercept
+    ss_res = float(np.sum((y - pred) ** 2))
+    ss_tot = float(np.sum((y - y.mean()) ** 2))
+    r2 = 1 - ss_res / ss_tot if ss_tot > 0 else 0.0
+    center = float(np.median(y))
+    rel30 = (slope * 30.0 / center) if abs(center) > 1e-9 else 0.0
+    return rel30, r2, float(y[0]), float(y[-1])
+
+
+def slow_trends(rows, features=("p_discharge", "pressure_ratio", "efficiency", "n_comp", "indicated_work")):
+    """Slow, weak drifts: linear fit per (cylinder, side) and per peer-relative series."""
+    print("\n== 慢趋势:自基线(斜率 %/30天,要求 |%|>=3 且 R2>=0.3) ==")
+    by_series = defaultdict(list)
+    for row in rows:
+        by_series[(row["cylinder"], row["side"])].append(row)
+    for (cylinder, side), series in sorted(by_series.items()):
+        days = sorted({r["day"] for r in series})
+        for feature in features:
+            daily = {d: np.median([r[feature] for r in series if r["day"] == d and r[feature] is not None]) for d in days}
+            present = [d for d in days if daily.get(d) is not None]
+            fit = _fit_drift(present, [daily[d] for d in present])
+            if fit and abs(fit[0]) >= 0.03 and fit[1] >= 0.3:
+                print(f"   {cylinder}缸{side} {feature:<16} {fit[0]*100:+.1f}%/30天 R2={fit[1]:.2f} ({fit[2]:.3f}->{fit[3]:.3f})")
+
+    print("\n== 慢趋势:同级 peer 相对(%/30天) ==")
+    by_ps = defaultdict(dict)
+    for row in rows:
+        if row["pressure_ratio"] is not None:
+            by_ps[(row["stage"], row["side"], row["day"])][row["cylinder"]] = row["pressure_ratio"]
+    rel = defaultdict(dict)
+    for (stage, side, day), members in by_ps.items():
+        clean = {c: v for c, v in members.items() if v is not None}
+        if len(clean) < 2:
+            continue
+        med = float(np.median(list(clean.values())))
+        if abs(med) < 1e-9:
+            continue
+        for cylinder, value in clean.items():
+            rel[(cylinder, side)][day] = (value - med) / med
+    for (cylinder, side), daily in sorted(rel.items()):
+        days = sorted(daily)
+        fit = _fit_drift(days, [daily[d] for d in days])
+        if fit and abs(fit[0]) >= 0.03 and fit[1] >= 0.3:
+            print(f"   {cylinder}缸{side} pressure_ratio(peer) {fit[0]*100:+.1f}%/30天 R2={fit[1]:.2f} ({fit[2]*100:+.1f}%->{fit[3]*100:+.1f}%)")
+
+
 def main():
     parser = argparse.ArgumentParser(description="功图特征长期偏离检测(对齐 evolution 异常段)")
     parser.add_argument("csv_path")
@@ -148,6 +200,7 @@ def main():
                     tag = "  ".join(flags)
                     print(f"      [突发] {feature}: Δ={delta:+.3f} ({delta / scale:+.1f}×基线尺度) 于 {day_jump}  {tag}")
     peer_sudden_changes(rows, "p_discharge", segments)
+    slow_trends(rows)
 
 
 if __name__ == "__main__":

+ 9 - 0
backend/indicator_features/观察记录.md

@@ -45,6 +45,15 @@
 
 ## 4. 关键观察
 
+### 4.5 长窗口慢趋势(`trend.py` 的 slow_trends,数月、每点每天4条)
+| 机组 | 最主要的慢变化 | 判断 |
+|---|---|---|
+| 7 号机 | **六缸盖侧 p_discharge 从 4 月 ~1.9 骤降到 5 月后 ~0.81 并保持**(p_span 0.85→0.27);六缸**轴侧正常** | **单侧传感器失效**(不是缸故障);方法能抓到阶跃 |
+| 8 号机 | 一缸(三级)压比/指示功缓降(负荷耦合);四缸轴侧 peer 压比 +30%→+11%(配置) | 无单缸故障 |
+| 9 号机 | **全部缸** p_discharge/指示功同步 +3~4%/30天 | 负荷级整体漂移,peer 相对无单缸趋势 |
+
+结论:长窗口慢趋势能稳定捕捉**缓慢/阶跃变化**(已用 7 号机六缸盖侧传感器失效验证),但**这些变化都不是气阀/活塞缸故障**。
+
 ### 4.4 形状/波形角度(`shape_analysis.py`)
 用归一化相位剖面(PV 环面积对齐 + 平台归一化,消工况),比较“修前/修后 L2、同级 peer、双腔”: