4. 时域分析方法:均值滤波、滑动窗口、FFT变换、时频图绘制

各位工程师朋友,咱们接着聊转矩波动分析。上一节讲了频域的基础概念,这一节我带你扎进时域里,看看信号在时间轴上到底是怎么跳动的。

说实话,我刚开始做EPS调校那会儿,最头疼的就是时域信号。一堆毛刺,看着就烦。后来慢慢摸到门道了——时域分析,说白了就是跟噪声做斗争。你想想看,传感器采回来的转矩信号,里面混着电机换向噪声、路面振动、齿轮啮合冲击……乱七八糟的。我们要做的,就是把真正有用的波动信息给揪出来。

核心思路:时域分析的目标是去除噪声、提取特征、定位问题。常用的四板斧——均值滤波、滑动窗口、FFT变换、时频图绘制,各有各的用处。

4.1 均值滤波:最简单的降噪手段

均值滤波,说白了就是取平均。把连续几个点的值加起来除以个数,用这个平均值代替当前点的值。我习惯叫它「抹平」操作。

举个例子,你采了5个点的转矩值:

原始数据:[10.2, 10.5, 9.8, 10.1, 10.3]  (单位Nm)
3点均值滤波后:
第1点:(10.2+10.5+9.8)/3 = 10.17
第2点:(10.5+9.8+10.1)/3 = 10.13
第3点:(9.8+10.1+10.3)/3 = 10.07

你看,波动从±0.3Nm缩小到了±0.1Nm左右。代价是什么?信号变「钝」了,高频细节丢了。

我的经验:滤波窗口大小怎么选?我一般遵循「3倍原则」——窗口宽度取信号中最高频率成分周期的3倍。比如你关心的是10Hz的波动,周期100ms,窗口就取300ms。太小了滤不干净,太大了把有用信号也抹掉了。

4.2 滑动窗口:实时处理的利器

均值滤波有个问题——它是一次性算完的。但在实际ECU里,数据是一点一点进来的,你不能等采完一整段再处理。这时候就要用滑动窗口。

滑动窗口的思路很简单:维护一个固定长度的缓冲区,新数据进来,旧数据出去,每次只算窗口内的均值。我做过一个项目,方向盘在某个角度区间总有「咯噔」一下的感觉,用滑动窗口实时看转矩信号,发现是蜗轮蜗杆啮合点有个微小的台阶。嗯,这就是机械问题,不是算法能解决的。

// 伪代码示例:滑动窗口均值滤波
#define WINDOW_SIZE 10
float buffer[WINDOW_SIZE];
int index = 0;
float sum = 0;

float sliding_average(float new_sample) {
    sum -= buffer[index];        // 去掉最旧的数据
    buffer[index] = new_sample;  // 存入新数据
    sum += new_sample;           // 更新总和
    index = (index + 1) % WINDOW_SIZE;
    return sum / WINDOW_SIZE;
}

注意:滑动窗口的初始阶段,缓冲区还没填满,算出来的均值是偏小的。我一般会先丢弃前N个点,或者用「预热」方式——前几个点直接输出原始值,等缓冲区满了再开始滤波。

4.3 FFT变换:从时域跳到频域

FFT(快速傅里叶变换)是时域分析的「王牌工具」。它能把时间轴上的信号,拆解成不同频率的正弦波分量。你想想看,一个复杂的转矩波动,可能同时包含1Hz的转向频率、10Hz的电机齿槽转矩、50Hz的PWM噪声……在时域里它们混在一起,根本分不清。但一进频域,每个频率都清清楚楚。

我曾经遇到一个案例:某车型在高速直线行驶时,方向盘有微弱的抖动。时域信号看着就是一条毛茸茸的线,啥也看不出来。做了FFT之后,发现12.5Hz处有个明显的峰值——对应的是轮胎动平衡问题。换了轮胎就好了。

import numpy as np
import matplotlib.pyplot as plt

# 假设采样率1000Hz,采样时长2秒
fs = 1000
t = np.linspace(0, 2, 2*fs)
# 模拟转矩信号:基波+谐波+噪声
signal = 5.0 * np.sin(2*np.pi*3*t) + 1.2 * np.sin(2*np.pi*15*t) + 0.8 * np.random.randn(len(t))

# FFT变换
fft_vals = np.fft.fft(signal)
freqs = np.fft.fftfreq(len(signal), 1/fs)
# 只取正频率部分
positive_idx = np.where(freqs >= 0)
freqs = freqs[positive_idx]
magnitude = np.abs(fft_vals[positive_idx]) / len(signal) * 2

# 找峰值
peak_idx = np.argsort(magnitude)[-5:]
print("主要频率成分(Hz):", freqs[peak_idx])
print("对应幅值(Nm):", magnitude[peak_idx])
频率成分 可能来源 典型幅值范围
1-5 Hz 驾驶员转向输入、路面激励 1-10 Nm
8-15 Hz 电机齿槽转矩、轮胎不平衡 0.1-0.5 Nm
20-50 Hz 齿轮啮合、轴承噪声 0.05-0.3 Nm
100 Hz以上 PWM开关噪声、传感器噪声 <0.1 Nm

避坑指南:做FFT之前,一定要先做「去趋势」处理。我曾经吃过这个亏——信号有个缓慢的直流漂移,FFT结果在0Hz附近出现一个巨大的尖峰,把其他频率成分全淹没了。解决办法很简单:先减去信号的均值,或者用高通滤波器把直流分量滤掉。

4.4 时频图绘制:看信号如何随时间变化

FFT有个局限——它假设信号是平稳的。但实际EPS的转矩信号,随着车速、转向角度、路面条件的变化,频率成分也在变。比如原地转向时电机负载大,齿槽转矩明显;高速行驶时负载小,但路面激励频率高。这时候就需要时频图了。

时频图,也叫频谱图或瀑布图。横轴是时间,纵轴是频率,颜色深浅代表能量大小。我习惯用短时傅里叶变换(STFT)来做:把信号切成一小段一小段,每段分别做FFT,然后拼在一起。

from scipy.signal import spectrogram

# 参数设置:窗口长度256点,重叠128点
f, t_spec, Sxx = spectrogram(signal, fs, nperseg=256, noverlap=128)

plt.figure(figsize=(10, 6))
plt.pcolormesh(t_spec, f, 10*np.log10(Sxx), shading='gouraud')
plt.ylabel('频率 (Hz)')
plt.xlabel('时间 (s)')
plt.colorbar(label='能量 (dB)')
plt.title('转矩信号时频图')
plt.ylim(0, 100)  # 只显示0-100Hz范围

你看这张图,颜色亮的地方就是能量集中的区域。如果某个频率成分随时间变化,在图上就是一条斜线或曲线。我遇到过一个问题:某车型在某个车速区间,方向盘有「嗡嗡」声。时频图上一看,40Hz左右的能量带在特定时间点突然变亮——对应的是电机控制器在某个转速下进入了谐振区。调整了PWM频率就解决了。

小结一下:均值滤波和滑动窗口是「预处理」工具,帮你把信号洗干净。FFT是「诊断」工具,告诉你问题出在哪个频率。时频图是「定位」工具,告诉你问题在什么时间出现。四者配合使用,基本能搞定90%的转矩波动分析。

嗯,这一节的内容就到这儿。下一节我们聊频域分析方法,到时候会用到今天讲的FFT基础。你先消化一下,有问题随时找我。


公众号:蓝海资料掘金营,微信deep3321