|
数据通过FFT转换成频域信号,对频域信号进行分析,再通过IFFT转换成时域信号。
[Python] 纯文本查看 复制代码
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
import numpy as np
import pylab as pl
import matplotlib as mpl
mpl.rcParams[‘font.sans-serif‘] = [‘KaiTi‘]
mpl.rcParams[‘font.serif‘] = [‘KaiTi‘]
mpl.rcParams[‘axes.unicode_minus‘]=False
sampling_rate = 8000
fft_size = 512
t = np.arange(0, 1.0, 1.0/sampling_rate)
x = np.sin(2*np.pi*156.25*t) + 2*np.sin(2*np.pi*234.375*t)
x = np.sin(2*np.pi*200*t) + 2*np.sin(2*np.pi*300*t)
xs = x[:fft_size]
xf = np.fft.rfft(xs)/fft_size
freqs = np.linspace(0, sampling_rate/2, fft_size/2+1)
xfp = 20*np.log10(np.clip(np.abs(xf), 1e-20, 1e100))
pl.figure(figsize=(8,4))
pl.subplot(211)
pl.plot(t[:fft_size], xs)
pl.xlabel(u"时间(秒)")
pl.title(u"156.25Hz和234.375Hz的波形和频谱")
pl.subplot(212)
pl.plot(freqs, xfp)
pl.xlabel(u"频率(Hz)")
pl.subplots_adjust(hspace=0.4)
pl.show()
|
运行结果如下图所示:
整数周期情况:
非整数周期情况:
如上两图可看出非整数周期情况下,第二种情况可能会发生信号泄露状况。由于FFT的假设前提是测量之外的信号是所测量信号的不断重复,如代码中那样,取8k个样例,从信号中取出的512个数据就是FFT的测量范围,也就是说每个波形周期是15.625Hz,它计算的是这512个数据一直重复的波形的频谱。显然如果512个数据包含整数个周期的话,那么得到的结果就是原始信号的频谱,而如果不是整数周期的话,得到的频谱就是如下波形的频谱,这里假设对50Hz的正弦波进行512点FFT,这种波形会发生跳变。
为了减少FFT所截取的数据段前后的跳变,可以对数据先乘以一个窗函数,使得其前后数据能平滑过渡。例如常用的hann窗函数的定义如下:
 hann窗的曲线如下所示:
 可看到,最前和最后的值都是0,如果直接去乘的前后会出现两个0,因此可考虑将这个波形用N+1个点表示,而取前N个点,这样第N+1个点就是下一个波形的第一个点,也就是0,通过设置sym参数解决。这与调用linspace时指定endpoint=False类似,丢掉最后一个点。
[Python] 纯文本查看 复制代码
|
1
2
3
4
5
6
|
signal.hann(8)
signal.hann(8, sym=0)
|
将hann窗与50hz相乘,它的曲线会更加平滑。
 之前的非整数周期加了hann窗之后的结果如下图所示:
对于频谱特性不随时间变化的信号,例如引擎、压缩机等机器噪声,可以对其进行长时间的采样,然后分段进行FFT计算,最后对每个频率分量的幅值求其平均值可以准确地测量信号的频谱,测试随机数序列频谱如下所示
[Python] 纯文本查看 复制代码
|
01
02
03
04
05
06
07
08
09
10
11
12
|
def average_fft(x, fft_size):[/align][align=left] n = len(x) // fft_size * fft_size
tmp = x[:n].reshape(-1, fft_size)
tmp *= signal.hann(fft_size, sym=0)
xf = np.abs(np.fft.rfft(tmp)/fft_size)
avgf = np.average(xf, axis=0)
return 20*np.log10(avgf)
[/align][align=left] 结果为[/align][align=left][img=279,100]https://img2018.cnblogs.com/blog/1800229/201909/1800229-20190919145733755-125985719.png[/img]这个频谱的能量趋近于一条直线,每个窗的能量相差不大,被称为白色噪声。[/align][list]
[*][size=18pt] 快速卷积[/size]
[/list][align=left] 信息处理可看作是将原始信号与一个信号进行卷积,也就需要考虑运算效率。这部分主要是比较FFT和直接卷积的运算效率[/align][align=left]x = np.random.rand(100000) - 0.5
xf = average_fft(x, 512)
pl.plot(xf)
pl.show()
|
[Python] 纯文本查看 复制代码
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
def fft_convolve(a,b):
n = len(a)+len(b)-1
N = 2**(int(np.log2(n))+1)
A = np.fft.fft(a, N)
B = np.fft.fft(b, N)
return np.fft.ifft(A*B)[:n]
if __name__ == "__main__":
a = np.random.rand(128)
b = np.random.rand(128)
c = np.convolve(a,b)
print (np.sum(np.abs(c - fft_convolve(a,b))))
|
结果为1.865645261656436e-12,也就是说FFT和普通卷积的结果相差很小,但速度却快很多。
对于输入信号 x 和系统向量(eg:FIR滤波器)h而言,x的长度不固定,h的长度固定。为了加快卷积效率, 我们需要x和h的长度相当,也就是说对x进行分段处理,这种分段算法被称为overlap-add运算。但是由于FFT在两个数组的分段长度相当时最为有效,因此在实时性要求很强的系统中,采用直接卷积会更好一些。
[Python] 纯文本查看 复制代码
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
import numpy as np[/align]x = np.random.rand(1000)
h = np.random.rand(101)
y = np.convolve(x, h)
N = 50
M = len(h)
output = []
buffer = np.zeros(M+N-1,dtype=np.float64)
for i in range(int(len(x)/N)):
xslice = x[i*N:(i+1)*N]
yslice = np.convolve(xslice, h)
pl.cla()
pl.plot(yslice)
buffer += yslice
output.append( buffer[:N].copy() )
buffer[0:M-1] = buffer[N:]
buffer[M-1:] = 0
y2 = np.hstack(output)
print (np.sum(np.abs( y2 - y[:len(x)] ) ))
|
Hilbert变换能在振幅保持不变的情况下将输入信号的相角偏移90度,简单地说就是能将正弦波形转换为余弦波形。
[Python] 纯文本查看 复制代码
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
from scipy import fftpack
import numpy as np
import matplotlib.pyplot as pl
t = np.linspace(0, 8*np.pi, 1024, endpoint=False)
x = np.sin(t)
y = fftpack.hilbert(x)
pl.plot(x, label=u"原始波形")
pl.plot(y, label=u"Hilbert转换后的波形")
pl.legend()
pl.show()
|
结果如下所示:
 Hilbert变换后可将直流分量变为0,正频率成分偏移+90度,负频率成分偏移-90度。它也可用来进行包络检波。
[Python] 纯文本查看 复制代码
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
import numpy as np
import pylab as pl
from scipy import fftpack
t = np.arange(0, 0.3, 1/20000.0)
x = np.sin(2*np.pi*1000*t) * (np.sin(2*np.pi*10*t) + np.sin(2*np.pi*7*t) + 3.0)
hx = fftpack.hilbert(x)
pl.plot(x, label=u"载波信号")
pl.plot(np.sqrt(x**2 + hx**2), "r", linewidth=2, label=u"检出的包络信号")
pl.title(u"使用Hilbert变换进行包络检波")
pl.legend()
pl.show()
|
|