1. 时频分析:信号处理的瑞士军刀
第一次接触时频分析是在研究生课题中处理一组非平稳振动信号。当时盯着满屏跳动的波形束手无策,直到导师扔过来一句"试试STFT"——那感觉就像在黑暗里突然摸到了电灯开关。时频分析之所以被称为信号处理的瑞士军刀,正是因为它能同时揭示信号在时间和频率维度的特征,这对分析时变信号(比如雷达回波、语音波形或机械振动)至关重要。
Matlab在这个领域简直就是天选之子。不仅内置了完整的时频分析工具箱,其矩阵运算优势更能让算法跑得飞起。今天我们就用几个典型场景,带你玩转四种核心工具:STFT(短时傅里叶变换)、小波变换、WVD(维格纳分布)和HHT(希尔伯特-黄变换)。每种方法我都会配上可直接运行的代码块,连绘图配色都给你调好了,复制粘贴就能出论文级结果图。
重要提示:本文所有代码基于Matlab R2023a编写,但兼容2016b及以上版本。如果遇到函数报错,大概率是工具箱未安装,到"主页→附加功能"里搜索添加即可。
2. 工具选型指南:四种方法对比
2.1 方法特性矩阵
| 方法 | 时间分辨率 | 频率分辨率 | 交叉项干扰 | 适用场景 |
|---|---|---|---|---|
| STFT | 中等 | 中等 | 无 | 语音分析,平稳信号 |
| 小波变换 | 可变 | 可变 | 无 | 瞬态检测,非平稳信号 |
| WVD | 高 | 高 | 有 | 线性调频信号,能量分析 |
| HHT | 高 | 高 | 无 | 非线性/非平稳信号 |
2.2 选择决策树
- 信号是否平稳?
- 是 → STFT
- 否 → 进入下一层判断
- 需要量化瞬时频率吗?
- 是 → HHT
- 否 → 进入下一层判断
- 关注瞬态特征还是能量分布?
- 瞬态 → 小波变换
- 能量 → WVD
3. STFT实战:语音信号分析
3.1 基础实现
matlab复制% 读取语音样本
[voice, Fs] = audioread('speech.wav');
% 设置参数
window = hamming(256); % 汉明窗
noverlap = 128; % 重叠点数
nfft = 1024; % FFT点数
% 计算STFT
[S, f, t] = spectrogram(voice, window, noverlap, nfft, Fs);
% 绘制时频谱
figure;
surf(t, f, 10*log10(abs(S)), 'EdgeColor', 'none');
view(0, 90);
axis tight;
xlabel('Time (s)');
ylabel('Frequency (Hz)');
title('STFT时频分析');
colormap jet;
colorbar;
3.2 参数调优经验
-
窗函数选择:
- 汉明窗:通用场景(默认推荐)
- 矩形窗:需要高时间分辨率时
- 布莱克曼窗:需要抑制频谱泄漏时
-
窗长黄金法则:
matlab复制% 自适应窗长计算(根据信号主频) main_freq = 1000; % 预估的主频(Hz) window_length = round(3*Fs/main_freq); % 每个周期采样3个窗
踩坑提醒:noverlap建议取窗长的50%-75%,超过90%会导致计算量剧增但分辨率提升有限。我曾傻乎乎设了95%的重叠,跑一个10分钟的信号让电脑风扇狂转了半小时...
4. 小波变换:轴承故障诊断
4.1 连续小波变换(CWT)实现
matlab复制% 生成故障轴承信号
load('bearing.mat'); % 包含振动数据vib_data
Fs = 20e3; % 采样率20kHz
% 小波参数
wavelet_name = 'amor'; % 复Morlet小波
frequencies = linspace(1e3, 10e3, 100); % 分析1k-10kHz
% 计算CWT
[cwt_coef, ~] = cwt(vib_data, frequencies, Fs, ...
'Wavelet', wavelet_name, ...
'VoicesPerOctave', 12);
% 绘制尺度图
figure;
pcolor(1:length(vib_data)/Fs, frequencies, abs(cwt_coef));
shading interp;
xlabel('Time (s)');
ylabel('Frequency (Hz)');
title('轴承振动信号CWT分析');
colormap hot;
4.2 小波选择秘籍
- 复Morlet小波('amor'):最佳时频聚焦性(默认推荐)
- Daubechies小波('dbN'):N=4时适合瞬态检测
- Meyer小波:需要严格正交性时使用
4.3 故障特征提取技巧
matlab复制% 提取特定频带能量(以5kHz为中心)
target_band = [4500, 5500];
[~, idx1] = min(abs(frequencies - target_band(1)));
[~, idx2] = min(abs(frequencies - target_band(2)));
band_energy = sum(abs(cwt_coef(idx1:idx2, :)).^2, 1);
% 绘制能量趋势
figure;
plot(linspace(0, length(vib_data)/Fs, length(band_energy)), band_energy);
xlabel('Time (s)');
ylabel('Energy at 5kHz band');
title('故障特征能量演化');
5. WVD与HHT:进阶应用
5.1 维格纳分布(WVD)实现
matlab复制% 生成线性调频信号
t = 0:1/Fs:1;
f0 = 10; f1 = 200;
chirp_signal = chirp(t, f0, 1, f1);
% 计算WVD
[wvd, ~, ~] = wvd(chirp_signal, Fs);
% 抑制交叉项(采用平滑伪WVD)
window = hamming(65);
[swvd, f, t] = swvd(chirp_signal, Fs, 'Window', window);
% 对比绘图
figure;
subplot(1,2,1);
imagesc(t, f, abs(wvd));
title('经典WVD');
subplot(1,2,2);
imagesc(t, f, abs(swvd));
title('平滑伪WVD');
colormap parula;
5.2 希尔伯特-黄变换(HHT)实战
matlab复制% 生成非线性信号
t = linspace(0, 1, 1000);
x = sin(2*pi*10*t + 0.5*sin(2*pi*3*t)) + 0.3*randn(size(t));
% EMD分解
[imf, residual] = emd(x, 'Interpolation', 'pchip');
% 希尔伯特谱分析
[hs, f, t] = hilbertspectrum(imf(1:end-1,:), Fs); % 忽略残差
% 绘制Hilbert谱
figure;
contourf(t, f, hs, 20, 'LineColor', 'none');
xlabel('Time (s)');
ylabel('Frequency (Hz)');
title('HHT时频谱');
colormap turbo;
6. 性能优化与调试技巧
6.1 加速计算三大招
-
降采样预处理:
matlab复制% 安全降采样准则 new_Fs = Fs / dec_factor; % dec_factor≤4 if new_Fs < 2*max_freq error('新采样率不满足奈奎斯特准则!'); end -
使用GPU加速:
matlab复制if gpuDeviceCount > 0 voice_gpu = gpuArray(voice); S = spectrogram(voice_gpu, window, noverlap, nfft); S = gather(S); % 取回CPU end -
并行计算:
matlab复制parpool(4); % 启动4个工作进程 parfor i = 1:numel(wavelets) cwt_coef{i} = cwt(data, wavelets{i}); end
6.2 常见报错解决方案
-
"Function not found":
- 检查是否安装对应工具箱(Signal Processing/Wavelet)
- 函数名区分大小写(spectrogram不是Spectrogram)
-
"Out of memory":
- 对长信号分帧处理
- 使用single精度替代double:
matlab复制
voice = single(voice);
-
时频谱出现异常条纹:
- 检查信号是否含NaN/Inf
- 尝试更换窗函数类型
- 调整重叠点数(通常50%-75%最佳)
7. 结果可视化进阶技巧
7.1 专业级时频图优化
matlab复制% 创建带等高线的时频图
figure;
[cont, h] = contourf(t, f, 10*log10(abs(S)), 30, 'LineColor', 'none');
colormap(flipud(pink)); % 避免jet的视觉误导
caxis([-80, 0]); % 统一色标范围
hcb = colorbar;
hcb.Label.String = 'Power/frequency (dB/Hz)';
% 添加主频跟踪线
[~, idx] = max(abs(S), [], 1);
hold on;
plot(t, f(idx), 'w--', 'LineWidth', 1.5);
7.2 动态可视化方案
matlab复制% 创建时频分析动画
writerObj = VideoWriter('tf_analysis.mp4', 'MPEG-4');
open(writerObj);
figure;
for k = 1:10:length(t)
surf(t(1:k), f, 10*log10(abs(S(:,1:k))), 'EdgeColor', 'none');
view(0, 90);
zlim([-80 0]);
title(sprintf('Time = %.2fs', t(k)));
frame = getframe(gcf);
writeVideo(writerObj, frame);
end
close(writerObj);
8. 工程应用案例
8.1 案例一:ECG信号分析
matlab复制% 读取MIT-BIH心律失常数据库
[ecg, Fs] = rdsamp('mitdb/100', 1);
% 带通滤波(0.5-40Hz)
[b,a] = butter(4, [0.5 40]/(Fs/2));
ecg_filt = filtfilt(b, a, ecg);
% STFT参数
window = kaiser(256, 5);
[S, f, t] = spectrogram(ecg_filt, window, 192, 512, Fs);
% R峰检测(结合时频能量)
hr_band = [5, 15]; % 对应0.75-2.5Hz
[~, idx1] = min(abs(f - hr_band(1)));
[~, idx2] = min(abs(f - hr_band(2)));
hr_energy = sum(abs(S(idx1:idx2, :)).^2, 1);
[~, r_peaks] = findpeaks(hr_energy, 'MinPeakDistance', Fs*0.6);
8.2 案例二:机械振动监测
matlab复制% 轴承故障特征频率计算
rpm = 1800; % 转速(rpm)
bearing_d = 0.25; % 滚珠直径(inch)
pitch_d = 1.5; % 节径(inch)
contact_angle = 15; % 接触角(度)
bpfo = rpm/60 * 0.5 * (1 - bearing_d/pitch_d*cosd(contact_angle)); % 外圈故障频率
bpfi = rpm/60 * 0.5 * (1 + bearing_d/pitch_d*cosd(contact_angle)); % 内圈故障频率
% 小波包分解提取特征频带
wpt = wpdec(vibration, 5, 'dmey');
nodes = [7,8,9,10]; % 选择包含故障频率的节点
features = zeros(length(nodes), size(vibration,2));
for i = 1:length(nodes)
features(i,:) = wprcoef(wpt, nodes(i));
end
9. 工具箱函数速查表
9.1 时频分析函数库
| 函数 | 功能描述 | 关键参数 |
|---|---|---|
| spectrogram | STFT计算 | window, noverlap, nfft |
| cwt | 连续小波变换 | Wavelet, VoicesPerOctave |
| wvd | 维格纳分布 | FilterBank, FrequencyRange |
| emd | 经验模态分解 | Interpolation, SiftRelativeTolerance |
| hilbertspectrum | 希尔伯特谱计算 | FrequencyResolution |
9.2 实用辅助函数
matlab复制% 时频分辨率评估函数
function [t_res, f_res] = tf_resolution(window, Fs, nfft)
t_res = length(window)/Fs; % 时间分辨率(s)
f_res = Fs/nfft; % 频率分辨率(Hz)
end
% 自适应窗长计算
function win_len = auto_window(signal, Fs, target_cycles)
[pxx, f] = pwelch(signal, [], [], [], Fs);
[~, idx] = max(pxx);
main_freq = f(idx);
win_len = round(target_cycles*Fs/main_freq);
end
10. 从实验室到产线的经验
在工业现场实施时频分析时,有几点血泪教训:
-
采样率设置:曾因采样率不足导致5kHz以上的轴承故障特征完全丢失,现在坚持用"2.56法则":采样率=2.56×最高分析频率×安全系数(通常取4)
-
抗干扰处理:车间电磁干扰会导致时频谱出现50Hz工频谐波,必须加装硬件滤波器或软件陷波:
matlab复制% 数字陷波器设计 wo = 50/(Fs/2); % 工频归一化频率 bw = wo/10; % 带宽 [b,a] = iirnotch(wo, bw); -
特征量化:时频分析不能只靠肉眼看图,必须提取量化指标。我常用的三个关键指标:
matlab复制% 1. 频带能量比 band_ratio = sum(S(f>1000,:))/sum(S(:)); % 2. 时频熵 P = abs(S).^2/sum(abs(S(:)).^2); tf_entropy = -sum(P.*log2(P+eps), 'all'); % 3. 瞬时频率标准差 [~,idx] = max(abs(S)); inst_freq = f(idx); freq_std = std(inst_freq);
最后分享一个调试技巧:当算法在测试数据表现良好但现场失效时,用这段代码生成带噪声的测试信号,能模拟90%的工业场景:
matlab复制clean_signal = chirp(t, 20, 1, 200);
noise = 0.2*randn(size(t)) + 0.1*sin(2*pi*50*t);
disturbance = 0.5*(rand(1,10)>0.9); % 随机脉冲
test_signal = clean_signal + noise + disturbance;
