如果信号的频率位于两个bin中心之间,则获得其峰值


12

请假设以下内容:

  • 信号基频的频率已使用FFT和某些频率估计方法进行了估计,位于两个bin中心之间
  • 采样频率是固定的
  • 计算工作不是问题

知道了频率,估计信号基波相应峰值的最准确方法是什么?

一种方法可能是对时间信号进行零填充以提高FFT分辨率,从而使bin中心更接近估计的频率。在这种情况下,我不确定的一点是我是否可以根据需要进行零填充,或者这样做是否有缺点。另一个是在零填充后我应该选择哪个bin中心作为我从中获取峰值的那个(因为即使在零填充之后,也可能不会精确地达到目标频率)。

但是,我也想知道是否存在另一种方法可以提供更好的结果,例如一种估计器,该估计器使用周围两个bin中心的峰值来估计感兴趣频率处的峰值。


2
FFT之前的零填充是一种方法。另一种方法是应用适合您需要的窗口功能。平顶窗正是为此目的而设计的。当然,如果您已经确切地知道了频率,并且只对一种amplutide感兴趣,那么可能有比FFT便宜的方法。
sellibitze 2013年

1
无需零填充:简单的抛物线插值(3个点:imax-1,imax,imax + 1,其中imaxFFT峰值在哪里)将为您提供准确的结果
Basj 2013年

确保插值功能与窗口功能匹配。平顶是微不足道的,否则您想要一个匹配对(例如,矩形窗口+ sinc插值,高斯窗口+高斯插值等)
finnw

@CedronDawg这个问题及其答案与您的确切频率公式相关(但不相同)。也许您会发现它很有趣。
Fat32

Answers:


5

首先想到的算法Goertzel算法。该算法通常假定感兴趣的频率是基本频率的整数倍。但是,本文将(通用)算法应用于您感兴趣的情况。


另一个问题是信号模型不正确。它使用2*%pi*(1:siglen)*(Fc/siglen)。它应该2*%pi*(0:siglen-1)*(Fc/siglen)用于阶段正确出来。

我也认为频率Fc=21.3很低是有问题的。当涉及相位/频率估计问题时,低频实值信号往往会出现偏差。

我还尝试了粗略的网格搜索来估算相位,它给出的答案与Goertzel算法相同。

下图显示了两个估算频率Fc=21.3(实心)和Fc=210.3(虚线)的两种估算值(Goertzel:蓝色,粗略:红色)的偏差。如您所见,较高频率的偏置要小得多。

积 -轴是初始相位改变从0到。2 πX2π

在此处输入图片说明


刚刚基于该论文测试了Goerzel算法的代码。使用输出的DTFT值,可以非常准确地获得峰值。但是,比例因子恰好为1000。因此,如果原始峰为1,234,则在Goerzel之后为1234。有人知道这可能来自哪里吗?
lR8n6i

同时做了一些研究。可能与幅度缩放有关:缩放时域幅度=频域系数* 2 / N,其中N是信号的长度。这个假设正确吗?
lR8n6i

1
是的,通常就是这种情况:信号的长度通常以刻度线的形式出现。
彼得·K。

嗨!我刚刚发现,使用Goertzel算法,所得复数系数的幅度非常准确,但是相位完全错误。有人知道这可能来自哪里吗?“相位”是指原始信号的基波中指定的相位滞后。
lR8n6i

1
ω0Ť+ϕĴ2[Ë-Ĵϕδω+ω0+2πķ-Ë+Ĵϕδω-ω0+2πķ]π/2

4

如果您愿意使用多个相邻的FFT分档,而不仅仅是2个,那么根据窗口的宽度,复数分档结果之间的加窗Sinc插值可以产生非常准确的估计。

窗口Sinc插值通常在高质量音频上采样器中找到,因此关于该主题的论文将具有合适的插值公式,并进行误差分析。


感谢您的评论。我也会尝试这种方法。
lR8n6i

4

πXπX

[1] JL Flanagan和RM Golden,“相位声码器”,《贝尔系统技术期刊》,第1卷。45,第1493-1509页,1966年。

[2] K. Dressler,Proc中的“使用多分辨率FFT的有效实现进行正弦提取”。9th Int。Conf。关于数字音频效果(DAFx-06),加拿大蒙特利尔,2006年9月,第247-252页。


嗨!非常感谢您的所有评论。我扩展了代码(见下文),以将Goertzel滤波器与抛物线形峰值插值结合起来以获得相位。但是,结果仍然不准确(±3-4度)。这是尽可能接近还是在理解或编码方面存在错误?
lR8n6i


3

几年前,我在解决这个确切问题上遇到了很多困难。

我发布了这个问题:

/programming/4633203/extracting-precise-frequencies-from-fft-bins-using-phase-change-between-frames

我最终从头开始进行计算,并发布了我自己问题的答案。

令我惊讶的是,我在互联网上找不到任何类似的展览。

我将在这里再次发布答案;请注意,该代码是针对我的FFT窗口重叠4倍的情况而设计的。

π


这个难题需要两个钥匙才能解锁。

图3.3:

在此处输入图片说明

图3.4:

在此处输入图片说明

码:

for (int k = 0; k <= fftFrameSize/2; k++) 
{
    // compute magnitude and phase 
    bins[k].mag = 2.*sqrt(fftBins[k].real*fftBins[k].real + fftBins[k].imag*fftBins[k].imag);
    bins[k].phase = atan2(fftBins[k].imag, fftBins[k].real);

    // Compute phase difference Δϕ fo bin[k]
    double deltaPhase;
    {
        double measuredPhaseDiff = bins[k].phase - gLastPhase[k];
        gLastPhase[k] = bins[k].phase;

        // Subtract expected phase difference <-- FIRST KEY
        // Think of a single wave in a 1024 float frame, with osamp = 4
        //   if the first sample catches it at phase = 0, the next will 
        //   catch it at pi/2 ie 1/4 * 2pi
        double binPhaseExpectedDiscrepancy = M_TWOPI * (double)k / (double)osamp;
        deltaPhase = measuredPhaseDiff - binPhaseExpectedDiscrepancy;

        // Wrap delta phase into [-Pi, Pi) interval 
        deltaPhase -= M_TWOPI * floor(deltaPhase / M_TWOPI + .5);
    }

    // say sampleRate = 40K samps/sec, fftFrameSize = 1024 samps in FFT giving bin[0] thru bin[512]
    // then bin[1] holds one whole wave in the frame, ie 44 waves in 1s ie 44Hz ie sampleRate / fftFrameSize
    double bin1Freq = (double)sampleRate / (double)fftFrameSize;
    bins[k].idealFreq = (double)k * bin1Freq;

    // Consider Δϕ for bin[k] between hops.
    // write as 2π / m.
    // so after m hops, Δϕ = 2π, ie 1 extra cycle has occurred   <-- SECOND KEY
    double m = M_TWOPI / deltaPhase;

    // so, m hops should have bin[k].idealFreq * t_mHops cycles.  plus this extra 1.
    // 
    // bin[k].idealFreq * t_mHops + 1 cycles in t_mHops seconds 
    //   => bins[k].actualFreq = bin[k].idealFreq + 1 / t_mHops
    double tFrame = fftFrameSize / sampleRate;
    double tHop = tFrame / osamp;
    double t_mHops = m * tHop;

    bins[k].freq = bins[k].idealFreq + 1. / t_mHops;
}

您正在插值频率,而OP知道频率并想插值振幅。
finnw

2

此python代码通过抛物线插值(McAulay Quatieri,Serra等成功使用的方法以谐波+余数)为您提供了非常准确的结果(我将其用于许多音符并且获得的误差小于半音的0.01%)分离技术)

import matplotlib.pyplot as plt
import numpy as np
from scipy.io.wavfile import read
from scipy.fftpack import fft, ifft
import math

(fs, x) = read('test.wav')
if (len(x.shape) == 2):    # if stereo we keep left channel only
 x = x[:,1]

n=x.size
freq = np.arange(n)*1.0/n*fs 
xfft = abs(fft(x))

imax=np.argmax(xfft)  
p=1.0/2*(xfft[imax-1]/xfft[imax]-xfft[imax+1]/xfft[imax])/(xfft[imax-1]/xfft[imax]-2+xfft[imax+1]/xfft[imax])   # parabolic interpolation 
print 'Frequence detectee avec interpolation parabolique :',(imax+p)*1.0/n*fs, 'Hz'

1
clear all
clc

for phase_orig = 0:pi/18:pi,

%% Specify and generate signal
Amp = 1;                     % Amplitude of signal
Fs = 8000;                   % samples per second
dt = 1/Fs;                   % seconds per sample
Fc = 21.3;                   % Hz
StopTime = 0.25;             % seconds
t = (0:dt:StopTime-dt)';     % seconds

siglen = length(t);
sig = Amp * 1.5 * sin(2*pi*(0:siglen-1)*(Fc/siglen) + phase_orig) + 1.5 * Amp * sin(2*pi*(0:siglen-1)*(Fc/siglen) * 3) ...
  + 1.5 * Amp * sin(2*pi*(0:siglen-1)*(Fc/siglen) * 5)+ 0.3 * Amp * sin(2*pi*(0:siglen-1)*(Fc/siglen) * 7) ...
  + 1.3 * Amp * sin(2*pi*(0:siglen-1)*(Fc/siglen) * 9)+ 1.4 * Amp * sin(2*pi*(0:siglen-1)*(Fc/siglen) * 11);

%% Estimate the peak value of the signals fundamental using Goertzel algorithm
peak = 0;
indvec = [Fc-1 Fc Fc+1];

% Check the input data
if ~isvector(sig) || isempty(sig)
  error('X must be a nonempty vector')
end

if ~isvector(indvec) || isempty(indvec)
  error('INDVEC must be a nonempty vector')
end
if ~isreal(indvec)
  error('INDVEC must contain real numbers')
end

% forcing x to be column
sig = reshape(sig,siglen,1);

% initialization
no_freq = length(indvec); %number of frequencies to compute
y = zeros(no_freq,1); %memory allocation for the output coefficients

% Computation via second-order system
% loop over the particular frequencies
for cnt_freq = 1:no_freq
  %for a single frequency:
  %a/ precompute the constants
  pik_term = 2*pi*(indvec(cnt_freq))/(siglen);
  cos_pik_term2 = cos(pik_term) * 2;
  cc = exp(-1i*pik_term); % complex constant
  %b/ state variables
  s0 = 0;
  s1 = 0;
  s2 = 0;
  %c/ 'main' loop
  for ind = 1:siglen-1 %number of iterations is (by one) less than the length of signal
    %new state
    s0 = sig(ind) + cos_pik_term2 * s1 - s2;  % (*)
    %shifting the state variables
    s2 = s1;
    s1 = s0;
  end
  %d/ final computations
  s0 = sig(siglen) + cos_pik_term2 * s1 - s2; %correspond to one extra performing of (*)
  y(cnt_freq) = s0 - s1*cc; %resultant complex coefficient

  %complex multiplication substituting the last iterationA
  %and correcting the phase for (potentially) non-integer valued
  %frequencies at the same time
  y(cnt_freq) = y(cnt_freq) * exp(-1i*pik_term*(siglen-1));
end

  % perfom amplitude scaling
  peak = abs(y(2)) * 2 / siglen

% perform parabolic interpolation to get the phase estimate
phase_orig=phase_orig*180/pi
ym1 = angle(unwrap(y(1)));
y0 = angle(unwrap(y(2)));
yp1 = angle(unwrap(y(3)));

p = (yp1 - ym1)/(2*(2*y0 - yp1 - ym1)); 
phase = y0 - 0.25*(ym1-yp1)*p;
phase_est = phase * 180/pi + 90;
phase_est = mod(phase_est+180,360)-180
end

您正在处理的频率(以8kHz采样的21.3Hz)非常低。由于这些是实值信号,因此它们在任何**频率的相位估计中都会出现偏差。

此图为偏压(的曲线phase_est - phase_orig),用于Fc = 210.3;相对于偏压为(红色)Fc = 21.3;。如您所见,这种21.3情况下的偏移量要重要得多。

另一种选择是降低采样率。绿色曲线显示了偏见Fs = 800代替8000

在此处输入图片说明


1
感谢更新!看我的情节;我仍然认为,任何相位估计器都将在如此低的频率上产生偏差。解决该问题的一种方法是使用已知频率(如果已知!)通过查找表校正相位估计偏差。但是您需要注意:偏差会随频率而变化。另一种方法是降低采样率。
Peter K.

1
也谢谢你!但是,如果您使用的是Fs = 8000 Hz,而Fc = 210而不是210.3,则偏置看起来会更糟。知道这可能来自哪里吗?
lR8n6i 2013年

1
rk!不知道。FWIW,Geortzel估计量没有问题:goertzel = atan(imag(y(2)),real(y(2)))*180/%pi + 90;。:-)会多挖一点。关注此空间。
Peter K.

1
抛物线插值法没有按照您认为的那样做。特别是,如果您将的计算替换为pp2 = (abs(y(3)) - abs(y(1)))/(2*(2*abs(y(2)) - abs(y(3)) - abs(y(1)))); phase2 = y0 - 0.25*(ym1-yp1)*p2;则即使对于,您也可以获得更好的答案Fc=210。我完全不确定当前版本是否p会为您提供任何明智的选择。插值公式用于插值抛物线的AMPLITUDE,但用于插值正...奇数p相位
彼得·K.

1
所有这些都可以,p = (yp1 - ym1)/(2*(2*y0 - yp1 - ym1))但是如果您使用PHASES而不是振幅,则在某些时候峰值位置()将不正确。这是因为相位可能会在+/- 180度边界附近跳跃。修复此问题所需要做的就是将该行更改为我p2上面的计算。
Peter K.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.