频谱分析是一种非常重要的信号处理方法,在机械设备故障诊断、振动系统分析、电力系统、无线电通信、信息图像处理和自动控制等学科中都有重要应用。频谱分析的核心是1965年Cooely-Tukey发表的快速傅里叶变换算法(简称FFT),它是离散傅里叶变换(DFT)的快速算法。FFT算法的各种语言实现包已经相当成熟,不需要自己来重新写源代码,本文使用matlab自带的fft函数来实现频谱分析。实际信号可能是连续无限长时间的,但是计算机在处理的时候只能是有限长度的离散信号,所以需要对信号进行采样。FFT用的时候是对有限长度的信号进行计算的。一段采集的实际信号给你的时候,往往只知道数据和采样频率,根据数据和采样频率就可以进行频谱分析了。当然,如果你有一段10000个点的数据,你也可以只截取其中感兴趣的N个点进行频谱分析,这里就等效为采集了N个点进行分析。
基本名词解释: 为信号的采样频率,为采样点数,也是频谱分析和FFT计算的点数, 为采样时间间隔, 为时域截断窗长, 为频率分辨率,也就是频谱中相邻两条谱线间的频率间隔。这些变量有如下关系:
理论部分请看这篇博客:频谱分析和FFT算法总结—理论基础
FFT的实现过程,这里不做介绍,直接看matlab中的计算结果。
指定信号的参数,采样频率为 1 kHz,信号持续时间为 1.5 秒。
matlab代码如下:
clear;close all
Fs = 1000; % 采样频率
T = 1/Fs; % 采样周期
L = 1500; % 截取的信号长度
t = (0:L-1)*T; % 时间矢量
%构造一个信号,其中包含幅值为 0.7 的 50 Hz 正弦量和幅值为 1 的 120 Hz 正弦量
X = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
% X = S + 2*randn(size(t));%用均值为零、方差为 4 的白噪声扰乱该信号。
%在时域中绘制原始信号
subplot(2,1,1),plot(1000*t(1:100),X(1:100))
title('部分原始信号X(t)')
xlabel('t (milliseconds)')
ylabel('X(t)')
%计算信号的傅里叶变换
Y = fft(X);
%计算双侧频谱 P2。然后基于 P2 和偶数信号长度 L 计算单侧频谱 P1。
P2 = abs(Y/L);
P1 = P2(1:L/2+1);
P1(2:end-1) = 2*P1(2:end-1);
%定义频域 f 并绘制单侧幅值频谱 P1。与预期相符,频率和幅值与理论相同
f = Fs*(0:(L/2))/L;
subplot(2,1,2),plot(f,P1)
title('X(t)的单边幅值谱')
xlabel('f (Hz)')
ylabel('|P1(f)|')
计算结果如图1所示:
从图1可以看出,计算的频谱(幅值和频率)与理论相同,没有误差。有时候我们也会遇到对数据进行补零以达到数据点数为2的n次幂的情况。
我们知道FFT分析的时候一般要求数据点数为2的n次幂,所以matlab中提供了Y=fft(x,N)这个函数,如果数据x的长度小于N,则为x补上尾零以达到长度N;如果x的长度大于N,则对x进行截断以达到长度N。或者通过插值使原始信号达到2的n次幂个数据。分别来看这两种情况。
1)补零的matlab程序如下:
clear;close all
Fs = 1000; % 采样频率
T = 1/Fs; % 采样周期
L = 1500; % 截取的信号长度
t = (0:L-1)*T; % 时间矢量
%构造一个信号,其中包含幅值为 0.7 的 50 Hz 正弦量和幅值为 1 的 120 Hz 正弦量
X = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
% X = S + 2*randn(size(t));%用均值为零、方差为 4 的白噪声扰乱该信号。
%在时域中绘制原始信号
subplot(2,1,1),plot(1000*t(1:100),X(1:100))
title('部分原始信号X(t)')
xlabel('t (milliseconds)')
ylabel('X(t)')
%计算信号的傅里叶变换
n = length(X); %获取数据点数
N = 2^nextpow2(n); %nextpow2(n)获得大于等于n的最大整数次幂
Y = fft(X,N);
%计算双侧频谱 P2。然后基于 P2 和偶数信号长度 N 计算单侧频谱 P1。
P2 = abs(Y/N);
P1 = P2(1:N/2+1);
P1(2:end-1) = 2*P1(2:end-1);
%定义频域 f 并绘制单侧幅值频谱 P1。
f = Fs*(0:(N/2))/N;
subplot(2,1,2),plot(f,P1)
title('X(t)的单边幅值谱')
xlabel('f (Hz)')
ylabel('|P1(f)|')
计算结果如图2所示:
从图2可以看出,本来是1500个数据,补零以达到2048个数据点之后,计算的频谱在频率和幅值方面都产生了误差,出现频谱泄露,原因是补零之后,数据相对原始信号发生了改变,已不是初始信号了。
2)插值的matlab程序如下:
clear;close all
Fs = 1000; % 采样频率
T = 1/Fs; % 采样周期
L = 1500; % 截取的信号长度
t = (0:L-1)*T; % 时间矢量
%构造一个信号,其中包含幅值为 0.7 的 50 Hz 正弦量和幅值为 1 的 120 Hz 正弦量
X = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
% X = S + 2*randn(size(t));%用均值为零、方差为 4 的白噪声扰乱该信号。
%在时域中绘制原始信号
subplot(2,1,1),plot(1000*t(1:100),X(1:100))
title('部分原始信号X(t)')
xlabel('t (milliseconds)')
ylabel('X(t)')
%计算信号的傅里叶变换
n = length(X); %获取数据点数
N = 2^nextpow2(n); %nextpow2(n)获得大于等于n的最大整数次幂
X1 = resample(X,N,n);%升采样到2048个数据
Y = fft(X1);
%计算双侧频谱 P2。然后基于 P2 和偶数信号长度 N 计算单侧频谱 P1。
P2 = abs(Y/N);
P1 = P2(1:N/2+1);
P1(2:end-1) = 2*P1(2:end-1);
%定义频域 f 并绘制单侧幅值频谱 P1。
f = Fs*2048/1500*(0:(N/2))/N;
subplot(2,1,2),plot(f,P1)
title('X(t)的单边幅值谱')
xlabel('f (Hz)')
ylabel('|P1(f)|')
插值前后的信号波形对比如图3所示:
从图3可以看出,插值后信号波形变得光滑了。重采样函数resample用法见matlab文档说明。
计算结果如图4所示:
从图4来看,插值计算的结果频率是正确的,幅值有一点点误差,但要比补零好很多。
综上,在用matlab进行频谱分析的时候,一般情况下不需要进行补零和插值操作,但需要进行对数据进行去趋势项操作。
这里考虑最简单的情况,在原始信号上加上直流分量,结果对比如图5所示:
可以看出,如果不去直流分量,频谱分析中会在0Hz出产生很大的一个值,影响结果。
在原始信号中加入均值为零、方差为 4 的白噪声来扰乱该信号
matlab代码如下:
clear;close all
Fs = 1000; % 采样频率
T = 1/Fs; % 采样周期
L = 1500; % 截取的信号长度
t = (0:L-1)*T; % 时间矢量
%构造一个信号,其中包含幅值为 0.7 的 50 Hz 正弦量和幅值为 1 的 120 Hz 正弦量
S = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
X = S + 2*randn(size(t));%用均值为零、方差为 4 的白噪声扰乱该信号。
%在时域中绘制原始信号
subplot(2,1,1),plot(1000*t(1:100),X(1:100))
title('部分原始信号X(t)')
xlabel('t (milliseconds)')
ylabel('X(t)')
%计算信号的傅里叶变换
Y = fft(X);
%计算双侧频谱 P2。然后基于 P2 和偶数信号长度 L 计算单侧频谱 P1。
P2 = abs(Y/L);
P1 = P2(1:L/2+1);
P1(2:end-1) = 2*P1(2:end-1);
%定义频域 f 并绘制单侧幅值频谱 P1。与预期相符,频率和幅值与理论相同
f = Fs*(0:(L/2))/L;
subplot(2,1,2),plot(f,P1)
title('X(t)的单边幅值谱(去均值)')
xlabel('f (Hz)')
ylabel('|P1(f)|')
结果如图6所示:
从图6中可以看出,加入噪声之后,会对频谱分析的幅值有一定影响,对频率成分基本没有影响。
后面会继续写一写频谱分析误差校正等方面的知识,欢迎大家留言交流!
matlab官方关于fft函数的教程:https://ww2.mathworks.cn/help/matlab/ref/fft.html