4. 特征工程(上):时域特征提取(均值、方差、峰峰值)
好,咱们进入特征工程这个环节。说实话,这是整个故障预测模型里最吃经验的部分。数据清洗完了,原始信号摆在那,你不能直接扔给模型——它看不懂。你得帮它提炼出有用的信息。
时域特征,说白了就是直接在时间轴上算统计量。我刚开始做储能BMS数据分析那会儿,也犯过傻,恨不得把所有统计量都算一遍。后来发现,真正管用的就那么几个。今天咱们先聊三个最基础的:均值、方差、峰峰值。
核心观点:时域特征提取,本质上是把一段连续波形压缩成几个有物理意义的数字。压缩得巧,模型就聪明;压缩得糙,模型就笨。
4.1 均值(Mean)—— 信号的“重心”
均值,就是平均值。简单吧?但在储能场景下,它的含义可不简单。
对于电压信号,均值反映了电池的静态工作点。比如一个电芯在静置状态下,均值就是它的开路电压。但在充放电过程中,均值会随着SOC变化而漂移。我遇到过一个问题:某批次电池在恒流充电阶段,电压均值比正常值低了50mV。后来排查发现是内阻偏大导致的。你看,一个简单的均值,就能暴露问题。
数学上,均值的定义是:
μ = (1/N) * Σ x_i
其中N是采样点数,x_i是第i个采样值。
在Python里,用NumPy一行搞定:
import numpy as np
# 假设 voltage 是一个一维数组,包含一段时间的电压采样值
voltage = np.array([3.52, 3.53, 3.51, 3.54, 3.52, 3.53])
mean_voltage = np.mean(voltage)
print(f"电压均值: {mean_voltage:.3f} V")
# 输出: 电压均值: 3.525 V
我的经验:均值对异常值很敏感。如果信号里有一个尖峰噪声,均值会被拉偏。所以我在提取均值前,通常会先做一次简单的滤波,比如中值滤波。别小看这一步,它能省掉你后面很多麻烦。
4.2 方差(Variance)—— 信号的“波动程度”
方差衡量的是信号偏离均值的程度。方差越大,说明信号越“躁动”。
在储能系统里,方差特别有用。你想想看,一个健康的电池,电压波动应该是平稳的。但如果电芯内部开始出现微短路,或者连接片松动,电压就会开始抖动。方差就是捕捉这种抖动的利器。
我曾经处理过一个案例:某储能电站的1号电池簇,电压方差突然从0.001飙升到0.05。一开始运维人员以为是采样问题,后来我坚持让他们去现场检查,结果发现是某个模组的汇流排螺丝松了。嗯,从那以后,方差就成了我必看的特征之一。
方差公式:
σ² = (1/N) * Σ (x_i - μ)²
代码实现:
variance_voltage = np.var(voltage)
print(f"电压方差: {variance_voltage:.5f} V²")
# 输出: 电压方差: 0.00011 V²
注意:方差的单位是原始单位的平方。比如电压单位是V,方差单位就是V²。这有时候不太直观。所以很多人更喜欢用标准差(std),它是方差的平方根,单位跟原始信号一致。
4.3 峰峰值(Peak-to-Peak)—— 信号的“最大摆幅”
峰峰值,就是最大值减最小值。它告诉你信号在时间窗口内“折腾”了多大范围。
在储能故障预测中,峰峰值对突发性异常特别敏感。比如电池突然发生热失控前,电压可能会出现剧烈的瞬时跌落,这时候峰峰值会瞬间变大。我见过一个案例:某电芯在热失控前30秒,电压峰峰值从正常的0.02V跳到了0.35V。虽然均值变化不大,但峰峰值已经报警了。
公式很简单:
P2P = max(x) - min(x)
代码:
p2p_voltage = np.max(voltage) - np.min(voltage)
print(f"电压峰峰值: {p2p_voltage:.3f} V")
# 输出: 电压峰峰值: 0.030 V
避坑指南:我曾经犯过一个错误——直接用原始数据的最大值和最小值算峰峰值。结果发现数据里有一个明显的采样毛刺,导致峰峰值虚高。后来我学乖了,先去掉最高和最低的1%数据,再用剩下的算。这样算出来的峰峰值更稳健。
4.4 三个特征的关系与对比
这三个特征,其实是从不同角度描述信号:
- 均值:告诉你信号“在哪”
- 方差:告诉你信号“抖不抖”
- 峰峰值:告诉你信号“最极端的情况”
它们之间没有绝对的优劣,关键看场景。比如:
| 故障类型 | 均值 | 方差 | 峰峰值 |
|---|---|---|---|
| 内阻增大 | 明显变化 | 轻微变化 | 轻微变化 |
| 微短路 | 轻微变化 | 明显变化 | 明显变化 |
| 连接松动 | 无变化 | 明显变化 | 明显变化 |
| 热失控前兆 | 轻微变化 | 剧烈变化 | 剧烈变化 |
你看,不同故障对这三个特征的敏感度是不一样的。所以实际项目中,我一般会把它们组合起来用,形成一个特征向量。
4.5 知识体系结构图
下面这张图,帮你理清时域特征提取的核心逻辑:
4.6 实际项目中的组合用法
光讲理论没意思,咱们来点实际的。假设你有一段电压数据,采样频率是10Hz,时间窗口是10秒(也就是100个采样点)。你会怎么提取特征?
我的做法是这样的:
- 滑动窗口:以1秒为步长,每次取10秒的数据段。
- 对每个窗口,计算均值、方差、峰峰值。
- 形成三维特征向量:[mean, variance, p2p]。
- 送入模型:比如一个简单的SVM或者随机森林。
代码示例:
import numpy as np
def extract_time_features(signal, window_size=100, step=10):
"""
滑动窗口提取时域特征
signal: 一维数组
window_size: 窗口长度(采样点数)
step: 滑动步长(采样点数)
"""
features = []
for start in range(0, len(signal) - window_size + 1, step):
window = signal[start:start + window_size]
mean = np.mean(window)
var = np.var(window)
p2p = np.max(window) - np.min(window)
features.append([mean, var, p2p])
return np.array(features)
# 模拟一段电压信号
np.random.seed(42)
voltage_signal = 3.5 + 0.02 * np.random.randn(1000) # 正常波动
# 在第500个点加入一个异常
voltage_signal[500:510] += 0.15 # 模拟电压突降
feat_matrix = extract_time_features(voltage_signal)
print(f"特征矩阵形状: {feat_matrix.shape}")
# 输出: 特征矩阵形状: (91, 3)
print("前3个窗口的特征:")
print(feat_matrix[:3])
# 输出示例:
# [[3.5002, 0.0004, 0.0812],
# [3.5005, 0.0003, 0.0798],
# [3.4998, 0.0004, 0.0805]]
我的习惯:提取完特征后,我通常会先做一次可视化。把三个特征画成散点图或者平行坐标图,看看正常数据和异常数据能不能分开。如果分不开,说明特征选得不对,得换思路。这一步能帮你省掉大量调参时间。
好了,时域特征的上半部分就聊到这儿。均值、方差、峰峰值,这三个是基础中的基础。你想想看,它们虽然简单,但组合起来已经能覆盖大部分常见故障场景了。下一节咱们会继续聊更高级的时域特征,比如均方根、峭度、波形因子这些。不过先把今天这三个练熟,比什么都强。