3D传感器信号的FFT

时间:2019-04-16 16:35:25

标签: python fft sensor

我有加速器信号数据的3d阵列,以50 Hz采样,这意味着时间步长是1/50 = .02。我的目标是使用Numpy或Scipy计算该传感器的主频率。我的问题是,我应该使用多维fft或计算单个Vector分别计算每列的频率,然后计算fft。

我使用以下函数来计算主频率。


from scipy import fftpack
import numpy as np
def fourier(signal, timestep):
    data = signal - np.mean(signal)
    N = len(data) // 2  # we need half of data
    freq = fftpack.fftfreq(len(data), d=timestep)[:N]
    fft = fftpack.fft(data)[:N]
    amp = np.abs(fft) / N
    order = np.argsort(amp)[::-1]  ## sort based on the importance
    return freq[order][0]

1 个答案:

答案 0 :(得分:0)

3D加速度传感器阵列产生5个维度的阵列:空间坐标,时间和加速度分量。

time维度上进行DFT相当于一次分析一个传感器:每个传感器会产生一个主频率,可能每个传感器之间的频率略有不同,就好像传感器解耦。

作为替代方案,让我们考虑在空间坐标和时间上进行DFT。它相当于将复合信号写为sinusoidal plane waves的总和:

Image description

其中Ǹ是通过将点数乘以时间样本数获得的比例因子。在续篇中,我将放弃与x,y,z,t,k_x,k_y,k_z和w无关的全局缩放。

在这一点上,对产生这种加速度的物理场进行建模将是一项重要的资产。实际上,如果现象是分散的,则使用此DFT几乎没有意义。但是,均匀材料中的扩散,弹性或声学是非扩散性的:每个频率与另一个频率独立存在。此外,知道物理原理对于定义能量是有用的。例如,与波k_x,k_y,k_z,w相关的动能写道:

xxx

因此,与给定频率w相关的动能写道:

因此,该推理提供了一种基于物理的方式来合并时间上的逐点DFT。确实,根据Parseval的身份:

关于实际考虑,像您一样减去平均值确实是一个好的开始。如果考虑通过乘以1 / w ^ 2来计算速度,则应将零频率(即平均值)清零,以避免出现无限或Nan。

此外,在计算时间DFT之前应用窗口可以帮助限制与spectral leakage相关的问题。 DFT设计用于周期与帧周期一致的周期信号。更具体地说,它通过一次又一次地重复帧来计算所构建信号的傅立叶变换。结果,人为的不连续性可能出现在边缘处,从而引起误导性的不存在频率。 Windows在靠近帧边缘的位置接近零,从而减少了不连续性及其影响。因此,建议将一个窗口也应用于空间尺寸,以保持与物理平面波分解的一致性。这样可以使3D阵列中心的加速器获得更多的权重。

平面波分解还要求传感器的空间间距必须比预期波长小大约两倍。否则,会发生另一种称为aliasing的现象。但是,功率谱W(w)可能比平面波分解对这个问题不敏感。相反,如果从加速度开始计算弹性应变能,则混叠可能成为一个实际问题,因为计算应变需要相对于空间坐标进行导数,即乘以k_x,k_y或k_z,并且空间混叠对应于使用错误的k_x。

一旦计算出W(w),就可以像Why are frequency values rounded in signal using FFT?一样,通过计算峰上相对于功率密度的平均频率来估算与每个峰相对应的频率。

这是一个示例代码,生成的某些平面波的频率与帧的大小(时间和空间)不一致。应用汉宁窗,计算动能,并求出与每个峰相对应的频率。

import matplotlib.pyplot as plt
import numpy as np
from scipy import signal
import scipy



spacingx=1.
spacingy=1.
spacingz=1.
spacingt=1./50.
Nx=5
Ny=5
Nz=5
Nt=512

frequency1=9.5
frequency2=13.7
frequency3=22.3
#building a signal
acc=np.zeros((Nx,Ny,Nz,Nt,3))
for i in range(Nx):
    for j in range(Ny):
        for k in range(Nz):
            for l in range(Nt):

                acc[i,j,k,l,0]=np.sin(i*spacingx+j*spacingy-2*np.pi*frequency1*l*spacingt)
                acc[i,j,k,l,1]=np.sin(i*spacingx+1.5*k*spacingz-2*np.pi*frequency2*l*spacingt)
                acc[i,j,k,l,2]=np.sin(1.5*i*spacingx+k*spacingz-2*np.pi*frequency3*l*spacingt)

#applying a window both in time and space
hanningx=np.hanning(Nx)
hanningy=np.hanning(Ny)
hanningz=np.hanning(Nz)
hanningt=np.hanning(Nt)

for i in range(Nx):
    hx=hanningx[i]
    for j in range(Ny):
        hy=hanningy[j]
        for k in range(Nz):
            hz=hanningx[k]
            for l in range(Nt):
                ht=hanningt[l]
                acc[i,j,k,l,0]*=hx*hy*hz*ht
                acc[i,j,k,l,1]*=hx*hy*hz*ht
                acc[i,j,k,l,2]*=hx*hy*hz*ht


#computing the DFT over time.
acctilde=np.fft.fft(acc,axis=3)


#kinetic energy
print acctilde.shape[3]
kineticW=np.zeros(acctilde.shape[3])
frequencies=np.fft.fftfreq(Nt, spacingt)

for l in range(Nt):
    oneonomegasquared=0.
    if l>0:
        oneonomegasquared=1.0/(frequencies[l]*frequencies[l])
    for i in range(Nx):
        for j in range(Ny):
            for k in range(Nz):
                kineticW[l]+= oneonomegasquared*(np.real(np.vdot(acctilde[i,j,k,l,:],acctilde[i,j,k,l,:])))



plt.plot(frequencies[0:acctilde.shape[3]],kineticW,'k-',label=r'$W(f)$')
#plt.plot(xi,np.real(fourier),'k-', lw=3, color='red', label=r'$f$, Hz')
plt.legend()
plt.show()

# see https://stackoverflow.com/questions/54714169/why-are-frequency-values-rounded-in-signal-using-fft/54775867#54775867
peaks, _= signal.find_peaks(kineticW, height=np.max(kineticW)*0.1)
print "potential frequencies index", peaks

#compute the mean frequency of the peak with respect to power density
powerpeak=np.zeros(len(peaks))
powerpeaktimefrequency=np.zeros(len(peaks))
for i in range(len(kineticW)):
    dist=1000
    jnear=0
    for j in range(len(peaks)):
        if dist>np.abs(i-peaks[j]):
             dist=np.abs(i-peaks[j])
             jnear=j
    powerpeak[jnear]+=kineticW[i]
    powerpeaktimefrequency[jnear]+=kineticW[i]*frequencies[i]


powerpeaktimefrequency=np.divide(powerpeaktimefrequency,powerpeak)
print 'corrected frequencies', powerpeaktimefrequency

enter image description here