4. 润滑油压温度趋势数据清洗与基线建立

为什么先清洗再建基线

圆锥破润滑系统的油压、油温原始趋势里混着三类“脏数据”:停机段(油泵停转,油压归零、油温缓慢下降)、启停瞬态(油压冲高、油温滞后上升)、以及传感器毛刺或单点跳变。若直接对全量数据求均值与分位数,基线会被停机段拉低、被瞬态拉宽,后续异常识别就会漏报或误报。本例的任务是:把多源数据对齐到统一时间轴,剔除无效点,按工况分段,只在“稳定正常运行”段上统计油压与油温的基线区间。

数据对象与对齐口径

假设已有 1 分钟粒度的历史记录,字段包括:ts(时间戳)、p_oil(润滑油压,MPa)、t_oil(油温,℃)、t_amb(环境温度,℃)、load(主电机电流占额定百分比,%)、pump_on(油泵运行信号 0/1)。

  • 时间对齐:以 1 分钟为基准栅格,各测点按最近时间戳重采样;同一分钟内多条记录取中位数,避免单点毛刺污染对齐结果。
  • 工况判定:pump_on=1 且 load≥40% 且连续稳定 ≥10 分钟,记为“稳定运行段”;pump_on=0 为停机段;其余为过渡段。
  • 异常点剔除:对稳定段内的油压、油温分别做滑动中位数偏差检验,偏差超过 3 倍稳健标准差(MAD 换算)判为离群点并剔除;同时剔除物理越界值(油压 ≤0 或 >0.6 MPa,油温 <20℃ 或 >75℃)。
  • 基线统计:仅用清洗后的稳定段样本,取 P5~P95 作为正常区间,中位数作为基线中心;并按环境温度分档(<10℃、10~30℃、>30℃)分别统计,减少季节影响。
多源原始数据(1 min) p_oil 油压 MPa t_oil 油温 ℃ t_amb 环境温度 ℃ load 负荷 % pump_on 泵信号 时间对齐 1 min 栅格重采样 同分钟取中位数 缺失标记 NaN 工况分段 停机段 pump_on=0 过渡段 启停/变负荷 稳定运行段 泵开+负荷≥40%+≥10min 异常点剔除 滑动中位数偏差 >3×MAD 判离群 物理越界值剔除 正常工况基线区间 仅用清洗后稳定段样本 油压 P5~P95,中位数为中心 油温 P5~P95,按环境温度分档 仅稳定段 示例统计结果(教学示例数据,非实测) 环境温度档 <10℃:油压基线 0.21 MPa(0.18~0.25),油温基线 38℃(34~43) 环境温度档 10~30℃:油压基线 0.20 MPa(0.17~0.24),油温基线 45℃(40~51) 环境温度档 >30℃:油压基线 0.19 MPa(0.16~0.23),油温基线 52℃(46~58) 剔除统计:停机段 31%,过渡段 9%,离群点 1.8%,有效稳定样本 58.2% 判读:新样本油压低于同档 P5 或油温高于同档 P95 且持续 ≥5 min → 触发异常复核

可执行的数据清洗与基线脚本

下面用 pandas 完成对齐、分段、剔除与基线统计。输入为一份 CSV(列名与上文一致),输出每个环境温度档的油压、油温基线区间,可直接作为后续异常识别的参照表。

import pandas as pd
import numpy as np

df = pd.read_csv("lube_history.csv", parse_dates=["ts"])
df = df.sort_values("ts").set_index("ts")

# 1) 1 min 栅格对齐,同分钟取中位数
grid = df.resample("1min").median(numeric_only=True)

# 2) 工况分段
pump = grid["pump_on"].fillna(0)
load = grid["load"]
stable = (pump == 1) & (load >= 40)
# 连续稳定 ≥10 min:滚动窗口内全为 True
stable_run = stable.rolling(10, min_periods=10).sum() == 10
grid["seg"] = np.where(pump == 0, "stop",
                np.where(stable_run, "stable", "transit"))

# 3) 稳定段内离群点剔除(稳健 MAD)
def mad_mask(s, k=3.0):
    med = s.rolling(31, center=True, min_periods=5).median()
    mad = (s - med).abs().rolling(31, center=True, min_periods=5).median()
    scale = 1.4826 * mad
    return (s - med).abs() > k * scale.replace(0, np.nan)

for col, lo, hi in [("p_oil", 0.0, 0.6), ("t_oil", 20.0, 75.0)]:
    bad = mad_mask(grid[col]) | (grid[col] <= lo) | (grid[col] > hi)
    grid.loc[bad, col] = np.nan

# 4) 环境温度分档 + 基线统计
grid["amb_bin"] = pd.cut(grid["t_amb"], [-50, 10, 30, 80],
                         labels=["<10", "10-30", ">30"])
base = (grid[grid["seg"] == "stable"]
        .groupby("amb_bin", observed=True)
        .agg(p_med=("p_oil", "median"),
             p_lo=("p_oil", lambda x: x.quantile(0.05)),
             p_hi=("p_oil", lambda x: x.quantile(0.95)),
             t_med=("t_oil", "median"),
             t_lo=("t_oil", lambda x: x.quantile(0.05)),
             t_hi=("t_oil", lambda x: x.quantile(0.95))))
print(base.round(2))
base.to_csv("lube_baseline.csv")

结果判读与使用约束

  • 先看样本占比:若稳定段有效样本不足总时长的 40%,说明历史数据中停机或过渡过多,基线代表性不足,应延长取数窗口或补充数据。
  • 看分档差异:若 >30℃ 档油温 P95 明显高于 10~30℃ 档(示例中约高 7℃),说明环境温度影响显著,必须分档使用,不能用一个全局区间。
  • 看区间宽度:油压 P5~P95 跨度若超过 0.10 MPa,提示系统压力波动偏大,应回到泵出口压力、过滤器压差等测点复核,而不是直接放宽基线。
  • 基线是“参照”不是“报警阈值”:后续异常识别应结合持续时间(如连续 5 min 越界)与负荷工况共同判断,避免单点越界误报。
清洗脚本中的窗口长度(31 点中位数、10 min 稳定判定)是示例取值,实际应按采样周期和破碎机启停节奏调整;调整后需重新核对稳定段样本占比是否合理。