
hann窗的曲线如下所示:

可看到,最前和最后的值都是0,如果直接去乘的前后会出现两个0,因此可考虑将这个波形用N+1个点表示,而取前N个点,这样第N+1个点就是下一个波形的第一个点,也就是0,通过设置sym参数解决。这与调用linspace时指定endpoint=False类似,丢掉最后一个点。
[Python] 纯文本查看 复制代码
signal.hann(8)
#array([0. , 0.1882551 , 0.61126047, 0.95048443, 0.95048443,
# 0.61126047, 0.1882551 , 0. ])
signal.hann(8, sym=0)
#array([0. , 0.14644661, 0.5 , 0.85355339, 1. ,
# 0.85355339, 0.5 , 0.14644661])
将hann窗与50hz相乘,它的曲线会更加平滑。

之前的非整数周期加了hann窗之后的结果如下图所示:
对于频谱特性不随时间变化的信号,例如引擎、压缩机等机器噪声,可以对其进行长时间的采样,然后分段进行FFT计算,最后对每个频率分量的幅值求其平均值可以准确地测量信号的频谱,测试随机数序列频谱如下所示
[Python] 纯文本查看 复制代码
def average_fft(x, fft_size):
n = len(x) // fft_size * fft_size
tmp = x[:n].reshape(-1, fft_size) #将n行转成n列,转为一个二维数组
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)
结果为

这个频谱的能量趋近于一条直线,每个窗的能量相差不大,被称为白色噪声。
信息处理可看作是将原始信号与一个信号进行卷积,也就需要考虑运算效率。这部分主要是比较FFT和直接卷积的运算效率
x = np.random.rand(100000) - 0.5
xf = average_fft(x, 512)
pl.plot(xf)
pl.show()
[Python] 纯文本查看 复制代码
def fft_convolve(a,b):
n = len(a)+len(b)-1
N = 2**(int(np.log2(n))+1) #FFT的长度:大于n的最小的2的整数次幂
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] 纯文本查看 复制代码
import numpy as np
x = np.random.rand(1000)
h = np.random.rand(101)
y = np.convolve(x, h)
N = 50 # 分段大小
M = len(h) # 滤波器长度
output = []
#缓存初始化为0
buffer = np.zeros(M+N-1,dtype=np.float64)
for i in range(int(len(x)/N)):
#从输入信号中读取N个数据
xslice = x[i*N:(i+1)*N]
#计算卷积
yslice = np.convolve(xslice, h)
pl.cla()
pl.plot(yslice)
#将卷积的结果加入到缓冲中
buffer += yslice
#输出缓存中的前N个数据,注意使用copy,否则输出的是buffer的一个视图
output.append( buffer[:N].copy() )
#缓存中的数据左移动N个元素
buffer[0:M-1] = buffer[N:]
#后面的补0
buffer[M-1:] = 0
#将输出的数据组合为数组
y2 = np.hstack(output)
#计算和直接卷积的结果之间的误差
print (np.sum(np.abs( y2 - y[:len(x)] ) ))