web-dev-qa-db-ja.com

2つの類似した波形間の時間シフトを見つける

2つの時間対電圧の波形を比較する必要があります。これらの波形のソースの特殊性のために、それらの1つは他のタイムシフトバージョンである可能性があります。

タイムシフトがあるかどうかを確認するにはどうすればよいですか?はいの場合、いくらですか。

私はこれをPythonで行っており、numpy/scipyライブラリを使用したいと思っています。

21
Vishal

scipyは、小さな入力に対して、また信号がラップアラウンドしないことを意味する非循環相関が必要な場合にも正常に機能する相関関数を提供します。 _mode='full'_では、signal.correlationによって返される配列のサイズは、シグナルサイズの合計から1を引いたもの(つまり、len(a) + len(b) - 1)であるため、argmaxは(信号サイズ-1 = 20)あなたが期待しているように見えるものからずれています

_from scipy import signal, fftpack
import numpy
a = numpy.array([0, 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1, 0, 0, 0, 0, 0])
b = numpy.array([0, 0, 0, 0, 0, 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1, 0])
numpy.argmax(signal.correlate(a,b)) -> 16
numpy.argmax(signal.correlate(b,a)) -> 24
_

2つの異なる値は、シフトがaまたはbのどちらにあるかに対応します。

循環相関が必要で、信号サイズが大きい場合は、畳み込み/フーリエ変換定理を使用できますが、相関は畳み込みと非常に似ていますが、同一ではないという警告があります。

_A = fftpack.fft(a)
B = fftpack.fft(b)
Ar = -A.conjugate()
Br = -B.conjugate()
numpy.argmax(numpy.abs(fftpack.ifft(Ar*B))) -> 4
numpy.argmax(numpy.abs(fftpack.ifft(A*Br))) -> 17
_

この場合も、2つの値は、aのシフトを解釈するかbのシフトを解釈するかに対応します。

負の活用は、関数の1つを畳み込み反転することによるものですが、相関関係では反転はありません。信号の1つを反転してからFFTを取得するか、信号のFFTを取得してから負の共役を取得することにより、反転を元に戻すことができます。つまり、次のことが当てはまります:Ar = -A.conjugate() = fft(a[::-1])

37
Gus

一方が他方によってタイムシフトされている場合、相関のピークが見られます。相関の計算にはコストがかかるため、FFTを使用することをお勧めします。したがって、次のようなものが機能するはずです。

af = scipy.fft(a)
bf = scipy.fft(b)
c = scipy.ifft(af * scipy.conj(bf))

time_shift = argmax(abs(c))
10
highBandWidth

この関数は、実数値の信号に対しておそらくより効率的です。 rfftとゼロパッドを使用して、線形(つまり非円形)相関を保証するのに十分な大きさの2の累乗の入力を使用します。

_def rfft_xcorr(x, y):
    M = len(x) + len(y) - 1
    N = 2 ** int(np.ceil(np.log2(M)))
    X = np.fft.rfft(x, N)
    Y = np.fft.rfft(y, N)
    cxy = np.fft.irfft(X * np.conj(Y))
    cxy = np.hstack((cxy[:len(x)], cxy[N-len(y)+1:]))
    return cxy
_

戻り値は長さM = len(x) + len(y) - 1です(2の累乗に切り上げられることから余分なゼロを削除するためにhstackと一緒にハッキングされます)。非負のラグはcxy[0], cxy[1], ..., cxy[len(x)-1]であり、負のラグはcxy[-1], cxy[-2], ..., cxy[-len(y)+1]です。

基準信号を一致させるために、rfft_xcorr(x, ref)を計算し、ピークを探します。例えば:

_def match(x, ref):
    cxy = rfft_xcorr(x, ref)
    index = np.argmax(cxy)
    if index < len(x):
        return index
    else: # negative lag
        return index - len(cxy)   

In [1]: ref = np.array([1,2,3,4,5])
In [2]: x = np.hstack(([2,-3,9], 1.5 * ref, [0,3,8]))
In [3]: match(x, ref)
Out[3]: 3
In [4]: x = np.hstack((1.5 * ref, [0,3,8], [2,-3,-9]))
In [5]: match(x, ref)
Out[5]: 0
In [6]: x = np.hstack((1.5 * ref[1:], [0,3,8], [2,-3,-9,1]))
In [7]: match(x, ref)
Out[7]: -1
_

信号を照合するための堅牢な方法ではありませんが、すばやく簡単に行えます。

6
Eryk Sun

それはあなたが持っている信号の種類(周期的?…)、両方の信号が同じ振幅を持っているかどうか、そしてあなたが探している精度に依存します。

HighBandWidthで説明されている相関関数は、実際に機能する可能性があります。それはあなたがそれを試してみる必要があるほど簡単です。

もう1つのより正確なオプションは、高精度のスペクトル線フィッティングに使用するオプションです。スプラインを使用して「マスター」信号をモデル化し、タイムシフト信号を(必要に応じて信号をスケーリングしながら)フィッティングします。これにより、非常に正確なタイムシフトが得られます。このアプローチの利点の1つは、相関関数を調べる必要がないことです。たとえば、interpolate.UnivariateSpline()(SciPyから)を使用してスプラインを簡単に作成できます。 SciPyは関数を返し、その関数はoptimize.leastsq()で簡単に適合されます。

2
Eric O Lebigot

別のオプションは次のとおりです。

from scipy import signal, fftpack

def get_max_correlation(original, match):
    z = signal.fftconvolve(original, match[::-1])
    lags = np.arange(z.size) - (match.size - 1)
    return ( lags[np.argmax(np.abs(z))] )
2
FFT