第1章:数据采集与特征工程
大家好,我是你们这门课的主讲。做了这么多年环境异常检测,我最大的感触就是——数据质量决定模型上限。你模型再花哨,数据采集这步没做好,后面全是白搭。今天咱们就从最底层的传感器数据采集讲起,一步步聊到特征工程。
本章核心脉络:传感器怎么把物理世界的信号变成数字信号?数据怎么传回来?传回来之后怎么提炼成有用的特征?最后怎么给模型喂数据?
1.1 传感器数据采集原理
传感器这东西,说白了就是个「翻译官」。把温度、湿度、气压、振动这些物理量,翻译成电信号。我当年第一次接触工业传感器时,还以为直接拿根线接电脑就能读数据——太天真了。
实际项目中,传感器采集有几个关键点:
- 采样率:每秒采多少个点。环境监测一般1Hz就够,振动监测可能要1000Hz以上。我踩过坑——用1Hz采振动数据,结果高频异常全丢了。
- 分辨率:12位还是16位ADC?分辨率越高,数据越精细,但成本也上去了。
- 量程:传感器能测的最大范围。选小了会削波,选大了精度不够。
我的经验:选传感器时,量程留20%余量。比如你测室温0-50℃,选个-10到80℃的传感器。别问我为什么——有一次夏天机房空调坏了,温度冲到65℃,量程不够的传感器直接烧了。
1.2 数据传输协议
数据采回来了,怎么传到服务器?这里有两个主流协议,我分别说说。
1.2.1 MQTT
MQTT是物联网场景的标配。它基于发布/订阅模式,轻量级,适合低带宽、高延迟的网络环境。
核心概念就三个:
- Broker:消息中转站。我常用Mosquitto或EMQX。
- Topic:消息主题。比如
factory/zone1/temperature。 - QoS:服务质量等级。0、1、2三级,等级越高越可靠,但延迟也越大。
举个实际例子。我在一个化工厂项目中,用MQTT采集200多个传感器的数据。代码大概长这样:
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, rc):
print("连接成功,返回码:", rc)
client.subscribe("factory/+/temperature")
def on_message(client, userdata, msg):
# 解析JSON数据
payload = json.loads(msg.payload)
print(f"Topic: {msg.topic}, 温度: {payload['value']}°C")
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.emqx.io", 1883, 60)
client.loop_forever()
注意:QoS别盲目设成2。我曾经在一个项目中全用QoS2,结果网络抖动时消息积压,Broker直接OOM了。后来改成QoS1,配合本地缓存,稳得很。
1.2.2 Modbus
Modbus是工业界的「老黄牛」。它走RS485串口,或者TCP/IP。PLC、变频器、智能仪表基本都支持。
Modbus有两种常用模式:
- RTU模式:二进制传输,效率高。适合串口通信。
- TCP模式:走以太网,配置简单。
我习惯用 pymodbus 库来读数据:
from pymodbus.client import ModbusTcpClient
client = ModbusTcpClient('192.168.1.100', port=502)
client.connect()
# 读取保持寄存器,从地址0开始,读10个寄存器
result = client.read_holding_registers(0, 10, unit=1)
if not result.isError():
values = result.registers
print("寄存器值:", values)
client.close()
嗯,这里要注意——Modbus地址是从0开始的,但有些设备文档写的是从1开始。我当年因为这个bug排查了整整两天。
1.3 特征提取方法
数据拿到手了,原始信号能直接扔给模型吗?不行。原始数据太「脏」了,维度也高。我们需要提取特征。
1.3.1 统计特征
最简单也最常用。对一段窗口数据,算几个统计量:
- 均值:反映整体水平
- 方差/标准差:反映波动程度
- 最大值/最小值:反映极端情况
- 偏度/峰度:反映分布形态
举个例子,监测一个水泵的振动:
import numpy as np
def extract_statistical_features(data):
features = {
'mean': np.mean(data),
'std': np.std(data),
'max': np.max(data),
'min': np.min(data),
'skew': pd.Series(data).skew(),
'kurtosis': pd.Series(data).kurtosis()
}
return features
1.3.2 频域特征
有些异常在时域看不出来,但在频域一目了然。比如电机轴承磨损,会在特定频率出现能量峰值。
我常用的频域特征:
- FFT幅值谱:各频率成分的幅值
- 功率谱密度:能量在频率上的分布
- 频谱质心:能量集中的频率位置
- 频谱带宽:能量分布的宽度
from scipy.fft import fft
def extract_frequency_features(data, fs=100):
n = len(data)
fft_vals = fft(data)
fft_mag = np.abs(fft_vals[:n//2]) * 2 / n
freqs = np.fft.fftfreq(n, 1/fs)[:n//2]
# 频谱质心
spectral_centroid = np.sum(freqs * fft_mag) / np.sum(fft_mag)
return {'spectral_centroid': spectral_centroid, 'fft_magnitude': fft_mag}
1.3.3 时域特征
时域特征关注信号随时间的变化模式。除了前面说的统计特征,还有:
- 过零率:信号穿越零点的次数
- 峰值因子:峰值与有效值的比值
- 波形因子:有效值与整流平均值的比值
- 脉冲因子:峰值与整流平均值的比值
实战建议:别把所有特征都算一遍。我一般先根据业务场景选3-5个关键特征。比如监测管道泄漏,我重点看频域特征;监测设备老化,我重点看时域统计特征。特征不是越多越好,多了反而容易过拟合。
1.4 特征选择与降维
特征提取完了,你可能得到几十甚至上百个特征。怎么办?降维。
1.4.1 PCA(主成分分析)
PCA是我最常用的降维方法。它把原始特征投影到新的正交坐标系上,保留方差最大的方向。
用sklearn实现很简单:
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
# 先标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# PCA降维到2维
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
print("解释方差比:", pca.explained_variance_ratio_)
我一般保留能解释95%方差的主成分数量。但要注意——PCA假设数据是线性的,如果你的数据有非线性结构,PCA效果可能不好。
1.4.2 t-SNE
t-SNE主要用于可视化。它能把高维数据映射到2D或3D,保留局部结构。但t-SNE有个毛病——每次运行结果都不一样,而且计算慢。
from sklearn.manifold import TSNE
tsne = TSNE(n_components=2, perplexity=30, random_state=42)
X_tsne = tsne.fit_transform(X_scaled)
避坑指南:t-SNE的perplexity参数很敏感。我一般设成样本数的5%-10%。设太小了,局部结构被放大;设太大了,全局结构被扭曲。还有,t-SNE不能用于模型推理,它只是可视化工具。
1.5 数据标准化与归一化
最后一步,给模型喂数据前,一定要做标准化或归一化。为什么?因为不同特征的量纲不一样。温度是0-100,压力是0-10000,如果不处理,模型会认为压力更重要——其实只是数值大而已。
两种常用方法:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| Z-score标准化 | x' = (x - μ) / σ | 数据近似正态分布,有异常值 |
| Min-Max归一化 | x' = (x - min) / (max - min) | 数据有明确边界,无异常值 |
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Z-score标准化
scaler_std = StandardScaler()
X_std = scaler_std.fit_transform(X)
# Min-Max归一化
scaler_mm = MinMaxScaler()
X_mm = scaler_mm.fit_transform(X)
我的习惯:对于环境监测数据,我优先用Z-score标准化。因为环境数据经常有异常值(比如传感器瞬间跳变),Min-Max会被异常值拉偏。Z-score对异常值鲁棒性更好。当然,如果你确定数据范围固定,比如pH值0-14,用Min-Max也没问题。
好了,这一章的内容就到这。数据采集和特征工程是环境异常检测的基石,打好基础,后面模型调优才能事半功倍。记住一句话——垃圾进,垃圾出。数据质量永远第一。