第四节:信号预处理——去趋势、去均值、归一化与标准化

各位同学,咱们接着聊。上一节讲了噪声怎么滤,这一节咱们聊聊信号预处理里几个基础但极其重要的操作:去趋势、去均值、归一化、标准化。

说实话,这几个概念在教科书里往往被一笔带过。但我做了这么多年胎压算法,可以负责任地告诉你——预处理做不好,后面再牛的模型也是白搭。就像盖房子,地基没打牢,装修再漂亮也经不起风吹雨打。

4.1 为什么要做预处理?

先问大家一个问题:你拿到一段胎压原始数据,第一件事是什么?

我个人的习惯是——先看一眼波形。不是看细节,而是看整体趋势。比如车辆从冷车启动到热车,胎压会缓慢上升。这个上升趋势,其实是温度变化引起的,跟轮胎漏不漏气没关系。

如果你不把这个趋势去掉,算法就会误判——哎?压力在上升?是不是漏气了?其实不是。这就是去趋势的意义。

再说均值。传感器输出的原始信号,往往带有一个直流偏置。这个偏置可能是传感器本身的零漂,也可能是ADC的参考电压偏差。你不把它去掉,后续计算出来的幅值、能量特征全是错的。

至于归一化和标准化,说白了就是让不同量纲的数据能放在一起比较。胎压单位是kPa,温度单位是℃,车速单位是km/h。你想想看,这三个数值直接扔进模型,谁占主导?肯定是数值大的那个。但实际物理意义呢?不一定。

4.2 去趋势(Detrend)

去趋势,就是去掉信号中缓慢变化的成分。在胎压数据里,最常见的趋势就是温度漂移引起的压力缓慢变化。

我记得有一次做路试,采集了一组数据,用肉眼一看,压力曲线整体向上飘。当时年轻,差点就报故障了。后来仔细一查,是那天太阳暴晒,轮胎温度从20℃升到了50℃,压力自然跟着涨。去掉这个线性趋势后,信号就正常了。

去趋势的方法很简单——线性拟合。把信号拟合成一条直线,然后减去这条直线。

import numpy as np
import matplotlib.pyplot as plt

def detrend_signal(signal):
    """
    去趋势:减去线性拟合的直线
    """
    n = len(signal)
    x = np.arange(n)
    # 线性拟合:y = kx + b
    k, b = np.polyfit(x, signal, 1)
    trend = k * x + b
    detrended = signal - trend
    return detrended, trend

# 示例:模拟带趋势的胎压信号
t = np.linspace(0, 10, 1000)
trend = 0.05 * t  # 缓慢上升趋势
noise = np.random.normal(0, 0.1, 1000)
signal = 100 + trend + noise

clean, trend_line = detrend_signal(signal)
print(f"原始信号均值: {np.mean(signal):.3f}")
print(f"去趋势后均值: {np.mean(clean):.3f}")
我的小技巧: 去趋势不一定要用线性拟合。如果趋势是非线性的(比如温度变化引起的指数型漂移),可以用多项式拟合或者高通滤波器。我个人更推荐高通滤波器,因为它实时性好,适合嵌入式实现。

4.3 去均值(Remove Mean)

去均值,也叫零均值化。就是把整个信号减去它的平均值,让信号围绕0波动。

为什么要这么做?因为很多信号处理算法,比如FFT、自相关函数,都假设信号是零均值的。如果信号有直流分量,FFT的0Hz处会有一个很大的峰值,把其他频率成分都淹没了。

我曾经遇到过一个问题:用FFT分析胎压信号的频谱,发现低频段能量特别大。排查了半天,原来是传感器零漂导致的直流分量没去掉。去掉均值之后,频谱就干净了。

def remove_mean(signal):
    """
    去均值:减去信号的平均值
    """
    mean_val = np.mean(signal)
    centered = signal - mean_val
    return centered, mean_val

# 示例
signal = np.array([100.2, 100.5, 99.8, 100.1, 100.3])
centered, mean_val = remove_mean(signal)
print(f"原始信号: {signal}")
print(f"均值: {mean_val:.2f}")
print(f"去均值后: {centered}")
注意: 去均值和去趋势不要搞混。去均值是减去一个常数,去趋势是减去一条直线(或曲线)。如果信号本身有趋势,只去均值是不够的,趋势依然存在。

4.4 归一化(Normalization)

归一化,就是把数据映射到[0,1]或[-1,1]区间。常用的方法有两种:

  • Min-Max归一化: x' = (x - min) / (max - min),结果在[0,1]之间
  • 绝对值归一化: x' = x / max(|x|),结果在[-1,1]之间

归一化在胎压算法里有什么用?我举两个例子:

第一,多传感器融合。胎压传感器、温度传感器、加速度传感器,量纲完全不同。归一化之后,它们才能放在同一个模型里处理。

第二,神经网络输入。如果你用深度学习做胎压异常检测,输入层的数据必须归一化。不然网络训练的时候,数值大的特征会主导梯度更新,收敛速度慢得让人抓狂。

def min_max_normalize(signal):
    """
    Min-Max归一化到[0,1]
    """
    min_val = np.min(signal)
    max_val = np.max(signal)
    if max_val == min_val:
        return np.zeros_like(signal)
    normalized = (signal - min_val) / (max_val - min_val)
    return normalized

# 示例
pressure = np.array([210, 215, 208, 220, 212])
temp = np.array([25, 28, 22, 30, 26])

p_norm = min_max_normalize(pressure)
t_norm = min_max_normalize(temp)

print("归一化后的胎压:", p_norm)
print("归一化后的温度:", t_norm)
# 现在可以放在一起比较了

4.5 标准化(Standardization)

标准化和归一化经常被混为一谈。其实它们不一样。

标准化是把数据变成均值为0、标准差为1的分布。公式是:x' = (x - μ) / σ,其中μ是均值,σ是标准差。

标准化不改变数据的分布形状,只是把数据"拉"到标准正态分布附近。它比归一化更鲁棒——因为归一化受异常值影响很大,一个离群点就能把整个区间拉偏。

我记得有一次做胎压异常检测,数据里有一个传感器故障导致的异常值(压力飙到了500kPa)。如果用Min-Max归一化,正常数据全被压缩到0.1以下,模型直接废了。换成标准化之后,异常值虽然还在,但正常数据的分布没被破坏。

def standardize(signal):
    """
    标准化:均值为0,标准差为1
    """
    mean = np.mean(signal)
    std = np.std(signal)
    if std == 0:
        return np.zeros_like(signal)
    standardized = (signal - mean) / std
    return standardized

# 示例:对比归一化和标准化对异常值的敏感度
signal_with_outlier = np.array([100, 102, 101, 500, 99, 103])

norm = min_max_normalize(signal_with_outlier)
std = standardize(signal_with_outlier)

print("原始信号:", signal_with_outlier)
print("归一化后:", np.round(norm, 3))
print("标准化后:", np.round(std, 3))
核心区别总结:
方法 公式 输出范围 对异常值敏感度 适用场景
Min-Max归一化 (x-min)/(max-min) [0,1] 已知边界、图像处理
标准化 (x-μ)/σ 无固定范围 异常检测、机器学习
去均值 x-μ 围绕0 频谱分析、滤波
去趋势 x - (kx+b) 无固定范围 消除漂移、温度补偿

4.6 实际应用中的顺序问题

这几个操作,先做哪个后做哪个?我个人的建议是:

  1. 先去趋势——消除缓慢漂移
  2. 再去均值——消除直流偏置
  3. 最后归一化或标准化——根据后续算法选择

为什么先去趋势?因为趋势会影响均值的计算。如果信号有上升趋势,均值会被拉高,去均值之后信号依然有趋势残留。反过来,先去趋势再去均值,结果才干净。

嗯,这里要注意:标准化和归一化是互斥的,选一个就行。不要两个都做,那是画蛇添足。

避坑指南: 我曾经在项目里犯过一个低级错误——先归一化,再去均值。结果归一化把数据压到[0,1]之后,均值变成了0.5左右,去均值之后数据变成了[-0.5, 0.5]。虽然也能用,但总觉得别扭。后来改成先去均值再归一化,数据分布就合理多了。

4.7 小结

好了,这一节的内容就这些。去趋势、去均值、归一化、标准化,这四个操作是信号预处理的基石。你想想看,如果连这些基础都做不好,后面的特征提取、模式识别、异常检测,怎么可能靠谱?

下一节咱们聊聊特征提取——怎么从预处理后的信号里,挖出真正有用的信息。到时候你会发现,预处理做得越干净,特征提取就越轻松。

今天就到这里。有问题随时交流。