Подтвердить что ты не робот

Найти временной сдвиг между двумя подобными сигналами

Мне нужно сравнить два сигнала с временным напряжением. Из-за особенностей источников этих сигналов одна из них может быть измененной по времени версией другой.

Как я могу найти, есть ли временной сдвиг? и если да, то сколько это.

Я делаю это в Python и хочу использовать библиотеки numpy/scipy.

4b9b3361

Ответ 1

scipy предоставляет функцию корреляции, которая будет хорошо работать для небольших входных данных, а также, если вы хотите некруговую корреляцию, означающую, что сигнал не будет распространяться. обратите внимание, что в mode='full' размер массива, возвращаемого signal.correlation, является суммой размеров сигналов минус один (т.е. 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

Два разных значения соответствуют тому, находится ли сдвиг в 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

опять же, эти два значения соответствуют тому, интерпретируете ли вы сдвиг в a или сдвиг в b.

Отрицательное сопряжение связано со свертыванием одной из функций, но в корреляции это не происходит. Вы можете отменить переключение, изменив один из сигналов и затем взяв БПФ, или взяв БПФ сигнала, а затем взяв отрицательное сопряжение. то есть верно следующее: Ar = -A.conjugate() = fft(a[::-1])

Ответ 2

Если один сдвинут во времени другим, вы увидите пик корреляции. Поскольку вычисление корреляции является дорогостоящим, лучше использовать БПФ. Итак, что-то вроде этого должно работать:

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

time_shift = argmax(abs(c))

Ответ 3

Эта функция, вероятно, более эффективна для реальных сигналов. Он использует rfft и zero pads входы для мощности 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 (взломанная вместе с hstack, чтобы удалить дополнительные нули от округления до степени 2). Неотрицательные лаги 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

Это не надежный способ согласования сигналов, но это быстро и просто.

Ответ 4

Это зависит от типа сигнала, который у вас есть (периодического?...), о том, имеют ли оба сигнала одинаковые амплитуды и какую точность вы ищете.

Функция корреляции, упомянутая highBandWidth, действительно может сработать для вас. Это достаточно просто, что вы должны попробовать.

Другим, более точным вариантом является тот, который я использую для высокоточного применения спектральной линии: вы моделируете свой "главный" сигнал с помощью сплайна и подбираете с ним сдвинутый по времени сигнал (хотя возможно масштабирование сигнала, если необходимо). Это дает очень точные сдвиги во времени. Одно из преимуществ этого подхода заключается в том, что вам не нужно изучать корреляционную функцию. Вы можете, например, легко создать сплайн с помощью interpolate.UnivariateSpline() (из SciPy). SciPy возвращает функцию, которая затем легко устанавливается с помощью optimize.leastsq().

Ответ 5

Вот еще один вариант:

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))] )