Python Can';使用scipy.signal.welch无法找到正确的能量

Python Can';使用scipy.signal.welch无法找到正确的能量,python,numpy,signal-processing,fft,discrete-mathematics,Python,Numpy,Signal Processing,Fft,Discrete Mathematics,对于给定的离散时间信号x(t),间隔dt(等于1/fs,fs为采样率),能量为: E[x(t)] = sum(abs(x)**2.0)/fs 然后我做一个x(t)的DFT: 再次计算能量: E[x_tf] = sum( abs( x_tf ) ** 2.0 ) * fs * 2 * np.pi / N (这里的系数fs*2*np.pi/N=脉动间隔dk,fftfreq的文档提供了关于频域间隔的更多细节),我具有相同的能量: E[x(t)] = E[x_tf] 但是。。。当我使用scipy.

对于给定的离散时间信号
x(t)
,间隔
dt
(等于
1/fs
fs
为采样率),能量为:

E[x(t)] = sum(abs(x)**2.0)/fs
然后我做一个
x(t)
的DFT:

再次计算能量:

E[x_tf] = sum( abs( x_tf ) ** 2.0 ) * fs * 2 * np.pi / N
(这里的系数
fs*2*np.pi/N
=脉动间隔
dk
fftfreq
的文档提供了关于频域间隔的更多细节),我具有相同的能量:

E[x(t)] = E[x_tf]
但是。。。当我使用
scipy.signal.welch
计算
x(t)
的功率谱密度时,我找不到正确的能量
scipy.signal.welch
返回频率
f
和能量
Pxx
(或每个频率的能量,取决于我们在
scipy.signal.welch
的参数中输入的
scaling

如何使用
Pxx
找到与
E[x(t)]
E[x_tf]
相同的能量?我试图计算:

E_psd = sum(Pxx_den) / nperseg
其中,
nperseg
是韦尔奇算法每段的长度,
fs
np.sqrt(2*np.pi)
等因子被抵消,并用
nperseg
重新缩放E[x(t)],但没有任何成功(数量级小于
E[x(t)]

我使用以下代码生成信号:

#Generate a test signal, a 2 Vrms sine wave at 1234 Hz, corrupted by 0.001 V**2/Hz of white noise sampled at 10 kHz.

fs = 10e3   #sampling rate, dt = 1/fs
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)
为了得到功率谱密度,我做了以下工作:

f, Pxx_den = signal.welch(x, fs )

解决这一明显差异的办法在于认真理解和应用

  • 连续与离散傅里叶变换,以及
  • 给定信号的能量、功率和功率谱密度
我也一直在为这个问题苦苦挣扎,所以我将在下面的讨论中尽量明确

离散傅里叶变换(DFT) 满足某些可积条件的连续信号x(t)具有傅里叶变换x(f)。然而,当使用离散信号x[n]时,通常使用离散时间傅里叶变换(DTFT)。我将DTFT表示为X{dt}(f),其中
dt
等于相邻样本之间的时间间隔。回答您的问题的关键要求您认识到DTFT不等于相应的傅里叶变换!事实上,这两者是相互关联的

X{dt}(f)=(1/dt)*X(f)

此外,离散傅里叶变换(DFT)只是DTFT的离散样本。当然,DFT是Python在使用
np.fft.fft(…)
时返回的。因此,计算出的DFT不等于傅里叶变换

功率谱密度
scipy.signal.welch(…,scaling='density',…)
返回离散信号x[n]的估计值。对PSD的全面讨论有点超出了本文的范围,但是对于简单的周期信号(例如在您的示例中),PSD S_{xx}(f)给出如下

S{xx}=|X(f)^2/T

式中,| X(f)|是信号的傅里叶变换,T是信号的总持续时间(时间)(如果你的信号X(T)是一个随机过程,我们必须对系统的许多实现进行集合平均…)。信号中的总功率只是系统频带上S_{xx}的积分。使用上面的代码,我们可以编写

import scipy.signal

# Estimate PSD `S_xx_welch` at discrete frequencies `f_welch`
f_welch, S_xx_welch = scipy.signal.welch(x, fs=fs)

# Integrate PSD over spectral bandwidth
# to obtain signal power `P_welch`
df_welch = f_welch[1] - f_welch[0]
P_welch = np.sum(S_xx_welch) * df_welch
要联系您的
np.fft.fft(…)
计算(返回DFT),我们必须使用上一节中的信息,即

X[k]=X_{dt}(f_k)=(1/dt)*X(f_k)

因此,要从FFT计算中计算功率谱密度(或总功率),我们需要认识到

S{xx}=|X[k]^2*(dt^2)/T

p_welch
p_fft
的值应该非常接近,并且接近信号中的预期功率,可以计算为

# Power in sinusoidal signal is simply squared RMS, and
# the RMS of a sinusoid is the amplitude divided by sqrt(2).
# Thus, the sinusoidal contribution to expected power is
P_exp = (amp / np.sqrt(2)) ** 2 

# For white noise, as is considered in this example,
# the noise is simply the noise PSD (a constant)
# times the system bandwidth. This was already
# computed in the problem statement and is given
# as `noise_power`. Simply add to `P_exp` to get
# total expected signal power.
P_exp += noise_power
注意:
p_welch
p_fft
将不完全相等,甚至在数值精度范围内也可能不相等。这是由于功率谱密度估计存在随机误差。为了减少此类错误,Welch的方法将信号分成若干段(其大小由
nperseg
关键字控制),计算每个段的PSD,并对PSD进行平均,以获得更好的信号PSD估计值(平均的段越多,产生的随机误差越小)。实际上,FFT方法相当于只对一个大段进行计算和平均。因此,我们期望
P_-welch
P_-fft
之间存在一些差异,但我们应该期望
P_-welch
更准确

信号能量 正如你所说的,信号能量可以从帕塞瓦尔定理的离散版本中获得

# Energy obtained via "integrating" over time
E = np.sum(x ** 2)

# Energy obtained via "integrating" DFT components over frequency.
# The fact that `E` = `E_fft` is the statement of 
# the discrete version of Parseval's theorem.
N = len(x)
E_fft = np.sum(np.abs(Xk) ** 2) / N
我们现在想了解上面通过
scipy.signal.welch(…)
计算的
sxx_welch
与信号中总能量
E
的关系。从上面看,
S_xx_fft=((np.abs(Xk)**dt)**2)/T
。重新排列这个表达式中的术语,我们可以看到
np.abs(Xk)**2=(T/(dt**2))*S_xx_fft
。此外

从上面我们知道,
np.sum(S_xx_fft)=p_fft/df_fft
,并且
p_fft
p_welch
近似相等。此外,
P_-welch=np.sum(S_xx_-welch)/df_-welch
,因此我们得到

np.sum(S_xx_fft)=(df_welch/df_fft)*np.sum(S_xx_welch)

此外,
S_xx_fft=((np.abs(Xk)**dt)**2)/T
。将
sxx_fft
代入上述方程并重新排列项,我们得到

np.sum(np.abs(Xk)**2)=(T/(dt**2))*(df_-welch/df_-fft)*np.sum(S_-xx_-welch)

上述方程式中的左侧(LHS)现在看起来应该非常接近于计算出的fr信号中总能量的表达式
# Power in sinusoidal signal is simply squared RMS, and
# the RMS of a sinusoid is the amplitude divided by sqrt(2).
# Thus, the sinusoidal contribution to expected power is
P_exp = (amp / np.sqrt(2)) ** 2 

# For white noise, as is considered in this example,
# the noise is simply the noise PSD (a constant)
# times the system bandwidth. This was already
# computed in the problem statement and is given
# as `noise_power`. Simply add to `P_exp` to get
# total expected signal power.
P_exp += noise_power
# Energy obtained via "integrating" over time
E = np.sum(x ** 2)

# Energy obtained via "integrating" DFT components over frequency.
# The fact that `E` = `E_fft` is the statement of 
# the discrete version of Parseval's theorem.
N = len(x)
E_fft = np.sum(np.abs(Xk) ** 2) / N
# Signal energy from Welch's PSD
E_welch = (1. / dt) * (df_welch / df_fft) * np.sum(S_xx_welch)