第1章:数据采集与特征工程

大家好,我是你们这门课的主讲。做了这么多年环境异常检测,我最大的感触就是——数据质量决定模型上限。你模型再花哨,数据采集这步没做好,后面全是白搭。今天咱们就从最底层的传感器数据采集讲起,一步步聊到特征工程。

本章核心脉络:传感器怎么把物理世界的信号变成数字信号?数据怎么传回来?传回来之后怎么提炼成有用的特征?最后怎么给模型喂数据?

环境异常检测 · 数据采集与特征工程知识体系 传感器数据采集 传输协议:MQTT | Modbus 特征提取方法 统计特征 频域特征 时域特征 特征选择与降维 PCA t-SNE 特征选择 标准化 | 归一化

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也没问题。

好了,这一章的内容就到这。数据采集和特征工程是环境异常检测的基石,打好基础,后面模型调优才能事半功倍。记住一句话——垃圾进,垃圾出。数据质量永远第一。


专注资料整理