質問

私が使用している fft (高速フーリエ変換) ルーチンを理解しようとしています (盗用) (リサイクル)

入力は、サンプル波形である 512 データ ポイントの配列です。テスト データはこの配列に生成されます。fft は、この配列を周波数領域に変換します。周波数、周期、サンプルレート、FFT配列の位置の関係を理解し​​ようとしています。例を挙げて説明します。

========================================

サンプルレートは 1000 サンプル/秒です。10Hz でサンプルのセットを生成します。

入力配列には、arr(28)、arr(128)、arr(228) ... にピーク値があります。期間 = 100 サンプルポイント

FFT 配列のピーク値はインデックス 6 にあります (0 の巨大な値を除く)

========================================

サンプルレートは8000サンプル/s 440Hzでサンプルのセットを生成します

入力配列のピーク値には、arr(7)、arr(25)、arr(43)、arr(61) ... が含まれます。周期 = 18 サンプルポイント

FFT 配列のピーク値はインデックス 29 にあります (0 の巨大な値を除く)

========================================

FFT 配列のピークのインデックスを周波数に関連付けるにはどうすればよいですか?

役に立ちましたか?

解決

虚数部を無視すると、度数分布はビン全体で線形になります。

周波数@i = (サンプリングレート/2)*(i/Nbins)。

したがって、最初の例では、256 個のビンがあると仮定すると、最大のビンは 1000/2 * 6/256 = 11.7 Hz の周波数に対応します。入力が 10Hz だったので、ビン 5 (9.7Hz) にも大きな成分があったと思います。より高い精度を得るには、より多くのサンプルを取得し、より小さなビンを取得する必要があります。

2 番目の例では、8000/2*29/256 = 453Hz となります。もう一度閉じますが、さらに多くのビンが必要です。ここでの解像度は 4000/256 = 15.6Hz のみです。

他のヒント

サンプル データセットを提供していただけると助かります。

私の推測では、いわゆるサンプリングアーチファクトがあると思います。DC (周波数 0 ) での強い信号は、これが事実であることを示唆しています。

入力データの平均値がゼロであることを常に確認する必要があります。FFT を呼び出す前に、平均を見つけて各サンプル ポイントからそれを差し引くことをお勧めします。

同様に、サンプリング ウィンドウのアーティファクトにも注意する必要があります。最初と最後のデータ ポイントがゼロに近いことが重要です。そうしないと、サンプリング ウィンドウの外側から内側への「ステップ」が、さまざまな周波数で大量のエネルギーを注入する影響を及ぼします。

肝心なのは、fft 分析を行うには、どこかにある fft ルーチンを単に再利用するよりも多くの注意が必要であるということです。

以下は、質問で説明されている 10Hz 信号の最初の 100 サンプル ポイントです。サンプリング アーティファクトを避けるためにマッサージされています。

> sinx[1:100]
  [1]  0.000000e+00  6.279052e-02  1.253332e-01  1.873813e-01  2.486899e-01  3.090170e-01  3.681246e-01  4.257793e-01  4.817537e-01  5.358268e-01
 [11]  5.877853e-01  6.374240e-01  6.845471e-01  7.289686e-01  7.705132e-01  8.090170e-01  8.443279e-01  8.763067e-01  9.048271e-01  9.297765e-01
 [21]  9.510565e-01  9.685832e-01  9.822873e-01  9.921147e-01  9.980267e-01  1.000000e+00  9.980267e-01  9.921147e-01  9.822873e-01  9.685832e-01
 [31]  9.510565e-01  9.297765e-01  9.048271e-01  8.763067e-01  8.443279e-01  8.090170e-01  7.705132e-01  7.289686e-01  6.845471e-01  6.374240e-01
 [41]  5.877853e-01  5.358268e-01  4.817537e-01  4.257793e-01  3.681246e-01  3.090170e-01  2.486899e-01  1.873813e-01  1.253332e-01  6.279052e-02
 [51] -2.542075e-15 -6.279052e-02 -1.253332e-01 -1.873813e-01 -2.486899e-01 -3.090170e-01 -3.681246e-01 -4.257793e-01 -4.817537e-01 -5.358268e-01
 [61] -5.877853e-01 -6.374240e-01 -6.845471e-01 -7.289686e-01 -7.705132e-01 -8.090170e-01 -8.443279e-01 -8.763067e-01 -9.048271e-01 -9.297765e-01
 [71] -9.510565e-01 -9.685832e-01 -9.822873e-01 -9.921147e-01 -9.980267e-01 -1.000000e+00 -9.980267e-01 -9.921147e-01 -9.822873e-01 -9.685832e-01
 [81] -9.510565e-01 -9.297765e-01 -9.048271e-01 -8.763067e-01 -8.443279e-01 -8.090170e-01 -7.705132e-01 -7.289686e-01 -6.845471e-01 -6.374240e-01
 [91] -5.877853e-01 -5.358268e-01 -4.817537e-01 -4.257793e-01 -3.681246e-01 -3.090170e-01 -2.486899e-01 -1.873813e-01 -1.253332e-01 -6.279052e-02

そして、これが結果として得られる FFT 周波数領域の絶対値です。

 [1] 7.160038e-13 1.008741e-01 2.080408e-01 3.291725e-01 4.753899e-01 6.653660e-01 9.352601e-01 1.368212e+00 2.211653e+00 4.691243e+00 5.001674e+02
[12] 5.293086e+00 2.742218e+00 1.891330e+00 1.462830e+00 1.203175e+00 1.028079e+00 9.014559e-01 8.052577e-01 7.294489e-01

私も数学と信号処理には少し慣れていますが、追加情報があれば試してみることができます。

ビンごとの信号エネルギーを知りたい場合は、複素出力の大きさが必要です。したがって、実際の出力を見るだけでは十分ではありません。入力が実数のみの場合でも。すべてのビンの出力の大きさは、ピタゴラスと同じように sqrt(real^2 + imag^2) です:-)

ビン 0 ~ 449 は、0 Hz ~ 500 Hz の正の周波数です。ビン 500 ~ 1000 は負の周波数であり、実際の信号の正の周波数と同じである必要があります。1 つのバッファを 2 秒ごとに処理すると、周波数と配列のインデックスが適切に整列します。したがって、インデックス 6 のピークは 6Hz に対応するため、これは少し奇妙です。これは、実際の出力データのみを調べており、実数データと虚数データが​​組み合わされてインデックス 10 で予想されるピークが得られることが原因である可能性があります。周波数はビンに線形にマッピングされる必要があります。

0 のピークは DC オフセットを示します。

FFTをやってからしばらく経ちましたが、覚えていることは次のとおりです

FFT は通常、入力と出力として複素数を受け取ります。したがって、入力と出力の実数部と虚数部がどのように配列にマップされるのかはよくわかりません。

何をしているのかよくわかりません。最初の例では、サンプル レート 1000 Hz に対してサンプル バッファを 10 Hz で処理すると言いましたか?したがって、それぞれ 100 個のサンプルを持つ 1 秒あたり 10 個のバッファーが必要になります。入力配列がどのようにして少なくとも 228 サンプル長になるのか理解できません。

通常、出力バッファの前半は、0 周波数 (= DC オフセット) から 1/2 サンプル レートまでの周波数ビンです。後半は負の周波数です。入力が実数データのみで、虚数信号が 0 の場合、正と負の周波数は同じです。出力の実数/虚数信号の関係には、入力信号からの位相情報が含まれます。

ビン i の周波数は i * (サンプルレート / n) です。ここで、n は FFT の入力ウィンドウ内のサンプル数です。

オーディオを処理している場合、ピッチは周波数の対数に比例するため、ビンのピッチ解像度は周波数が増加するにつれて増加します。つまり、低周波数信号を正確に解決するのは困難です。これを行うには、より大きな FFT ウィンドウを使用する必要があるため、時間分解能が低下します。特定のサンプルレートでは、周波数と時間分解能のトレードオフが存在します。

値が 0 の大きなビンについて言及しました。これは頻度 0 のビン、つまりDC成分。これが大きい場合、値は一般に正であると考えられます。ビン n/2 (この場合は 256) は、サンプル レートの半分であるナイキスト周波数であり、このレートでサンプリングされた信号で分解できる最高の周波数です。

信号が実数の場合、ビン n/2+1 ~ n-1 には、それぞれビン n/2-1 ~ 1 の複素共役が含まれます。DC 値は 1 回だけ表示されます。

他の人が言ったように、サンプルは周波数領域で等間隔に配置されています(対数ではありません)。

例 1 の場合、次のようになります。

代替テキスト http://home.comcast.net/~kootsoop/images/SINE1.jpg

他の例については、取得する必要があります

代替テキスト http://home.comcast.net/~kootsoop/images/SINE2.jpg

したがって、ピークの位置に関しては両方の答えが正しいようです。

得られないのは、大きな DC 成分です。入力として正弦波を生成していますか?入力がマイナスになるんですか?正弦波の場合、十分なサイクルが得られれば DC はゼロに近くなるはずです。

別の方法は、 ゲルツェルのアルゴリズム 探している各ノートの中心周波数の。

アルゴリズムの 1 つの実装が機能するようになったら、中心周波数を設定するためにパラメーターを受け取るようにアルゴリズムを作成できます。これを使用すると、88 個のコレクションや必要なものを簡単に実行して、ピーク値をスキャンできます。

Goertzel アルゴリズムは基本的にシングル ビン FFT です。この方法を使用すると、音符の自然な流れに合わせて対数的にビンを配置できます。

Wikipedia からのいくつかの疑似コード:

s_prev = 0
s_prev2 = 0
coeff = 2*cos(2*PI*normalized_frequency);
for each sample, x[n],
  s = x[n] + coeff*s_prev - s_prev2;
  s_prev2 = s_prev;
  s_prev = s;
end
power = s_prev2*s_prev2 + s_prev*s_prev - coeff*s_prev2*s_prev;

前の 2 つのサンプルを表す 2 つの変数は、次の反復のために維持されます。これはストリーミング アプリケーションで使用できます。おそらく電力計算もループ内に含めるべきだと思います。(ただし、Wiki の記事ではそのようには描かれていません。)

トーン検出の場合、88 個の異なる係数、88 対の前のサンプルがあり、その周波数ビンの相対レベルを示す 88 個のパワー出力サンプルが得られます。

WaveyDavey は、コンピューターのオーディオ ハードウェアを介してマイクからサウンドをキャプチャしているが、その結果はゼロ中心ではないと述べています。これはハードウェアに問題があるように思えます。ゼロ中心であるべきです。

部屋が静かな場合、サウンド API からの値のストリームは、周囲の騒音に対してわずかな +- 変動はあるものの、振幅が 0 に非常に近くなるはずです。室内に振動音が存在する場合(例:ピアノ、フルート、声など)データ ストリームは、正と負の両方に進み、平均がゼロに近い基本的に正弦波ベースの波を示す必要があります。そうでない場合は、システムで何らかの異常が発生しています。

-リック

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top