[Matlab科学计算] 频谱分析和FFT算法总结

       频谱分析是一种非常重要的信号处理方法,在机械设备故障诊断、振动系统分析、电力系统、无线电通信、信息图像处理和自动控制等学科中都有重要应用。频谱分析的核心是1965年Cooely-Tukey发表的快速傅里叶变换算法(简称FFT),它是离散傅里叶变换(DFT)的快速算法。FFT算法的各种语言实现包已经相当成熟,不需要自己来重新写源代码,本文使用matlab自带的fft函数来实现频谱分析。实际信号可能是连续无限长时间的,但是计算机在处理的时候只能是有限长度的离散信号,所以需要对信号进行采样。FFT用的时候是对有限长度的信号进行计算的。一段采集的实际信号给你的时候,往往只知道数据和采样频率,根据数据和采样频率就可以进行频谱分析了。当然,如果你有一段10000个点的数据,你也可以只截取其中感兴趣的N个点进行频谱分析,这里就等效为采集了N个点进行分析。

基本名词解释:f_{s} 为信号的采样频率,N为采样点数,也是频谱分析和FFT计算的点数, \Delta t为采样时间间隔,T 为时域截断窗长, \Delta f为频率分辨率,也就是频谱中相邻两条谱线间的频率间隔。这些变量有如下关系:

                                                                       f_{s}=\frac{1}{\Delta t},         \Delta f=\frac{f_{s}}{N}=\frac{f_{s}}{Tf_{s}}=\frac{1}{T}

理论部分请看这篇博客:频谱分析和FFT算法总结—理论基础

FFT的实现过程,这里不做介绍,直接看matlab中的计算结果。

1.首先来看一下常规的FFT计算

指定信号的参数,采样频率为 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所示:

[Matlab科学计算] 频谱分析和FFT算法总结_第1张图片 图1

 从图1可以看出,计算的频谱(幅值和频率)与理论相同,没有误差。有时候我们也会遇到对数据进行补零以达到数据点数为2的n次幂的情况。

2.如果原始数据插值(重采样)或者补零到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所示:

[Matlab科学计算] 频谱分析和FFT算法总结_第2张图片 图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所示:

[Matlab科学计算] 频谱分析和FFT算法总结_第3张图片 图3

从图3可以看出,插值后信号波形变得光滑了。重采样函数resample用法见matlab文档说明。

计算结果如图4所示:

[Matlab科学计算] 频谱分析和FFT算法总结_第4张图片 图4

从图4来看,插值计算的结果频率是正确的,幅值有一点点误差,但要比补零好很多。

综上,在用matlab进行频谱分析的时候,一般情况下不需要进行补零和插值操作,但需要进行对数据进行去趋势项操作。

3.趋势项对频谱分析的影响 

这里考虑最简单的情况,在原始信号上加上直流分量,结果对比如图5所示:

[Matlab科学计算] 频谱分析和FFT算法总结_第5张图片 图5

可以看出,如果不去直流分量,频谱分析中会在0Hz出产生很大的一个值,影响结果。 

4.噪声对频谱分析的影响

在原始信号中加入均值为零、方差为 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所示:

[Matlab科学计算] 频谱分析和FFT算法总结_第6张图片 图6

从图6中可以看出,加入噪声之后,会对频谱分析的幅值有一定影响,对频率成分基本没有影响。

后面会继续写一写频谱分析误差校正等方面的知识,欢迎大家留言交流!

参考文献: 

matlab官方关于fft函数的教程:https://ww2.mathworks.cn/help/matlab/ref/fft.html

你可能感兴趣的:(信号处理,Matlab科学计算与绘图)