Python 强噪声信号的Scipy-FFT频率分析

Python 强噪声信号的Scipy-FFT频率分析,python,numpy,pandas,fft,Python,Numpy,Pandas,Fft,我有噪声,我想计算频率和振幅。每100秒采集一次样本。从趋势来看,我认为频率约为0.3 当我使用numpyfft模块时,我得到的频率非常高(36.32/秒),这显然是不正确的。我试图在fft之前用pandasrolling\u mean过滤数据,以去除噪声,但这也不起作用 import pandas as pd from numpy import fft import numpy as np import matplotlib.pyplot as plt Moisture_mean_x =

我有噪声,我想计算频率和振幅。每100秒采集一次样本。从趋势来看,我认为频率约为0.3

当我使用
numpy
fft
模块时,我得到的频率非常高(36.32/秒),这显然是不正确的。我试图在fft之前用
pandas
rolling\u mean
过滤数据,以去除噪声,但这也不起作用

import pandas as pd
from numpy import fft
import numpy as np
import matplotlib.pyplot as plt

Moisture_mean_x = pd.read_excel("signal.xlsx", header = None)
Moisture_mean_x = pd.rolling_mean(Moisture_mean_x, 10) # doesn't helps
Moisture_mean_x = Moisture_mean_x.dropna()
Moisture_mean_x = Moisture_mean_x -Moisture_mean_x.mean()
frate = 100. #/sec           
Hn = fft.fft(Moisture_mean_x)
freqs = fft.fftfreq(len(Hn), 1/frate)
idx = np.argmax(np.abs(Hn))
freq_in_hertz = freqs[idx]

有人能指导我如何解决这个问题吗?

我希望这能对你有所帮助

在应用FFT之前,应仅过滤预期频率附近的频带,并提高信噪比

编辑:


MarkRansom给出了一个更聪明的答案,如果你必须做FFT,你可以在变换后切断噪声。它不会产生比滤波器更差的结果。

您应该使用低通滤波器,它应该保持较大的周期性变化,并首先平滑一些较高频率的东西。之后,再进行FFT运算得到峰值。这是一个典型的用于这类事情的例子

FFT是一个滤波器组。只需在FFT结果(而不是整个结果向量)的预期频率范围内查找幅值峰值,其他大部分频谱基本上都会被过滤掉。

因为FFT是一个滤波器,所以无需事先过滤信号。只需跳过FFT中那些与你知道的包含大量噪声的频率相对应的部分-将它们归零,或以其他方式排除它们。

你是对的,有问题。需要解释的是,请pandas填写第0列:

Hn = np.fft.fft(Moisture_mean_x[0])
否则会发生错误,您可以通过FFT结果不是对称的这一事实看到,这应该是真实输入的情况

似乎@tilsten已经回答了您的问题,但这里有一些额外的确认。第一个绘图是您的数据(零均值,我将其更改为csv)。第二个是功率谱密度,你可以看到一个脂肪团,峰值约为0.3 Hz。我放大了第三个图,看看是否有第二个隐藏频率接近主频率

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

x = pd.read_csv("signal.csv")
x = np.array(x, dtype=float)[:,0]
x = x - np.mean(x)
fs = 1e2

f, Pxx = signal.welch(x, fs, nperseg=1024)
f_res, Pxx_res = signal.welch(x, fs, nperseg=2048)

plt.subplot(3,1,1)
plt.plot(x)

plt.subplot(3,1,2)
plt.plot(f, Pxx)
plt.xlim([0, 1])
plt.xlabel('frequency [Hz]')
plt.ylabel('PSD')

plt.subplot(3,1,3)
plt.plot(f_res, Pxx_res)
plt.xlim([0, 1])
plt.xlabel('frequency [Hz]')
plt.ylabel('PSD')

plt.show()

Hn = fft.fft(x)
freqs = fft.fftfreq(len(Hn), 1/fs)
idx = np.argmax(np.abs(Hn))
freq_in_hertz = freqs[idx]
print 'Main freq:', freq_in_hertz
print 'RMS amp:', np.sqrt(Pxx.max())
这张照片是:

Main freq: 0.32012805122
RMS amp: 0.0556044913489

1)代码正常。2) 你为什么期望0.3赫兹?我计算了2500秒内的6/8峰值,估计主导低频约为0.0024至0.0032 Hz。3) 36.32 Hz有什么问题,即为什么您确定这是错误的?4) 如果确实要使用滚动平均值对信号进行预滤波,请选择不同于1的窗口大小。现在,对
rolling_mean()
的调用没有任何作用。@gg349我假设图片显示的是2500个样本,而不是2500秒。这使你的频率范围从0.24到0.32赫兹,其中当然包含0.3。@MarkRansom很好地识别了抱歉,这是
rolling\u mean
的打字错误,现在已修复。滤波是完全冗余的,FFT已经在按频率分割内容。只需要忽略FFT结果中与感兴趣范围之外的频率相对应的部分。不,不必。信号是实数,因此研究负频率分量也是多余的,并将导致相同的结果。@gg349绝对正确,我没有意识到FFT返回的是复数值不是实数。我将删除这个答案中不准确的部分,这应该是其中的大部分。@Markransem我同意,这似乎是目前为止唯一的解决方案。但是,我想知道,即使我完全用噪声来计算频率,结果仍然是34.8赫兹!这让我觉得我在代码中遗漏了一些步骤。为什么要过滤?标记Ransom对其他类似帖子的评论。为什么值为10会导致几何平均数?对不起,这是打字错误。我试过
rolling\u意思是(…,10)
得到了36.4赫兹。啊,你说得对,马克的答案是最聪明的。我误解了请求中的数据。事实上我不明白为什么他的答案被否决了+1谢谢。我也想到了这一点,但我只给出了一个问题的样本,而我有数百个这样的信号要分析。手动选择预期的频率范围将非常繁琐。有没有更好的方法来自行计算这个频率范围?如果不知道要删除哪些频率(以及剩下的频率),就无法进行过滤。即使是移动平均线也是一个(低质量)过滤器。因此,你必须知道预期的范围,或者不过滤噪声结果,然后接受你得到的结果。我知道移动平均是一个低质量的过滤器,但我希望它会使我更接近预期的频率。但是,即使我使用了
滚动平均值(…,100)
这给了我非常平滑的信号[1],结果仍然是34.8赫兹!这让我觉得我错过了什么。[1] 的确如此。而且,现在也不需要过滤。谢谢是的,我也注意到了隐藏的频率。因此,我将平均频率计算为:
freqs=fft.fftfreq(len(Hn),1/frate);ind=np.arange(1,len(Hn)/2+1);psd=np.abs(Hn[ind])**2+np.abs(Hn[-ind])**2;切割=0.7*psd.max();ind1=np,其中(psd>切割);freq_in_hertz=freqs[ind[ind1]].mean()
。这很有效。您还可以通过
idx=np.argmax(np.abs(Hn)[:5])
找到第二个峰值,结果是0.16006402561(正好是0.32012的一半…)。