40

我有一个包含 301 个值的数组,这些值是从 301 帧的影片剪辑中收集的。这意味着来自 1 帧的 1 个值。影片剪辑以 30 fps 运行,因此实际上是 10 秒长

现在我想得到这个“信号”的功率谱(用右轴)。我试过:

 X = fft(S_[:,2]);
 pl.plot(abs(X))
 pl.show()

我也试过:

 X = fft(S_[:,2]);
 pl.plot(abs(X)**2)
 pl.show()

虽然我不认为这是真正的光谱。

信号: 在此处输入图像描述

光谱:在此处输入图像描述

功率谱:

在此处输入图像描述

任何人都可以提供一些帮助吗?我想有一个以 Hz 为单位的情节

4

5 回答 5

60

Numpy 有一个方便的函数,np.fft.fftfreq用于计算与 FFT 分量相关的频率:

from __future__ import division
import numpy as np
import matplotlib.pyplot as plt

data = np.random.rand(301) - 0.5
ps = np.abs(np.fft.fft(data))**2

time_step = 1 / 30
freqs = np.fft.fftfreq(data.size, time_step)
idx = np.argsort(freqs)

plt.plot(freqs[idx], ps[idx])

在此处输入图像描述

请注意,您在案例中看到的最大频率不是 30 Hz,而是

In [7]: max(freqs)
Out[7]: 14.950166112956811

您永远不会在功率谱中看到采样频率。如果您有偶数个样本,那么您将达到奈奎斯特频率,在您的情况下为 15 Hz(尽管 numpy 会将其计算为 -15)。

于 2013-03-13T14:39:01.677 回答
21

如果rate是采样率(Hz),那么np.linspace(0, rate/2, n)是fft中每个点的频率数组。您可以使用rfftfft 来计算数据中的实际值:

import numpy as np
import pylab as pl
rate = 30.0
t = np.arange(0, 10, 1/rate)
x = np.sin(2*np.pi*4*t) + np.sin(2*np.pi*7*t) + np.random.randn(len(t))*0.2
p = 20*np.log10(np.abs(np.fft.rfft(x)))
f = np.linspace(0, rate/2, len(p))
plot(f, p)

在此处输入图像描述

信号 x 包含 4Hz 和 7Hz 正弦波,因此在 4Hz 和 7Hz 有两个峰值。

于 2013-03-13T12:37:53.100 回答
10

您还可以使用scipy.signal.welch使用 Welch 方法估计功率谱密度。这是 np.fft.fft 和 scipy.signal.welch 之间的比较:

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

fs = 10e3
N = 1e5
amp = 2*np.sqrt(2)
freq = 1234.0
noise_power = 0.001 * fs / 2
time = np.arange(N) / fs
x = amp*np.sin(2*np.pi*freq*time)
x += np.random.normal(scale=np.sqrt(noise_power), size=time.shape)

# np.fft.fft
freqs = np.fft.fftfreq(time.size, 1/fs)
idx = np.argsort(freqs)
ps = np.abs(np.fft.fft(x))**2
plt.figure()
plt.plot(freqs[idx], ps[idx])
plt.title('Power spectrum (np.fft.fft)')

# signal.welch
f, Pxx_spec = signal.welch(x, fs, 'flattop', 1024, scaling='spectrum')
plt.figure()
plt.semilogy(f, np.sqrt(Pxx_spec))
plt.xlabel('frequency [Hz]')
plt.ylabel('Linear spectrum [V RMS]')
plt.title('Power spectrum (scipy.signal.welch)')
plt.show()

[fft[2] 韦尔奇

于 2018-07-31T13:26:31.143 回答
6

从 numpy fft 页面http://docs.scipy.org/doc/numpy/reference/routines.fft.html

当输入 a 为时域信号且 A = fft(a) 时,np.abs(A) 为其幅度谱,np.abs(A)**2 为其功率谱。相位谱由 np.angle(A) 获得。

于 2013-03-13T10:06:00.320 回答
6

由于 FFT 在其中心上是对称的,因此一半的值就足够了。

import numpy as np
import matplotlib.pyplot as plt

fs = 30.0
t = np.arange(0,10,1/fs)
x = np.cos(2*np.pi*10*t)

xF = np.fft.fft(x)
N = len(xF)
xF = xF[0:N/2]
fr = np.linspace(0,fs/2,N/2)

plt.ion()
plt.plot(fr,abs(xF)**2)
于 2013-11-24T06:56:48.253 回答