2011-01-14 15 views
14

Tengo que comparar dos formas de onda de tiempo contra voltaje. Debido a la peculiaridad de las fuentes de estas formas de onda, una de ellas puede ser una versión desplazada en el tiempo de la otra.encontrar el cambio de tiempo entre dos formas de onda similares

¿Cómo puedo saber si hay un cambio de hora? y si es así, ¿cuánto cuesta?

Estoy haciendo esto en Python y deseo utilizar librerías numpy/scipy.

Respuesta

28

scipy proporciona una función de correlación que funcionará bien para entradas pequeñas y también si desea una correlación no circular, lo que significa que la señal no se ajustará. tenga en cuenta que en mode='full', el tamaño de la matriz devuelta por signal.correlation es la suma de los tamaños de señal de entrada - 1, por lo que el valor de argmax está desactivado por (tamaño de señal -1 = 20) de lo que parece esperar.

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 

Los dos valores diferentes corresponden a si el cambio está en a o b.

Si desea una correlación circular y para un tamaño de señal grande, puede usar el teorema de convolución/transformada de Fourier con la advertencia de que la correlación es muy similar pero no idéntica a la convolución.

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 

de nuevo los dos valores corresponden a si su interpretación de un cambio en a o un cambio en b.

La conjugación negativa se debe a la convolución al voltear una de las funciones, pero en correlación no hay inversión. Puede deshacer el volteo ya sea invirtiendo una de las señales y luego tomando la FFT, o tomando la FFT de la señal y luego tomando el conjugado negativo. es decir, lo siguiente es verdadero: Ar = -A.conjugate() = fft(a[::-1])

+0

1UP: Poco familiarizado con el procesamiento de señal , pero parece que sabes de lo que me estás hablando. – MattH

+0

Gracias por la respuesta. Esta es la primera vez que veo algo que tiene sentido. Ahora una pregunta más, dependiendo del "signo" del valor de cambio de tiempo Restaré o añadiré el turno de tiempo. ¿Cómo obtener el signo? – Vishal

+3

Espera ... ¿por qué necesitas el negativo? No creo que necesites el negativo. Deja que x (t) transforme X (f). Por inversión de tiempo, x (-t) tiene transformación X (-f). Si x (t) es real, entonces X (-f) = conj (X (f)). Por lo tanto, si x (t) es real, entonces x (-t) tiene tr ansform conj (X (f)). Sin negativo –

9

Si uno está desplazado en el tiempo por el otro, verá un pico en la correlación. Dado que el cálculo de la correlación es costoso, es mejor usar FFT. Por lo tanto, algo como esto debería funcionar:

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

time_shift = argmax(abs(c)) 
+1

He intentado hacer lo que usted ha sugerido, para el caso que nos ocupa se dio un resultado erróneo. Ejemplo: >>> a21 matriz ([0, 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1, 0, 0, 0 , 0, 0]) >>> a22 matriz ([0, 0, 0, 0, 0, 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3] , 2, 1, 0]) >>> fa21 = np.fft.fft (a21) >>> fa22 = np.fft.fft (a22) >>> c = np.fft.ifft (fa21 * fa22) >>> time_shift = np.argmax (abs (c)) >>> time_shift Como se puede ver, el cambio de horario real es de 4 puntos y no 20. Me estoy perdiendo algo aquí? – Vishal

+1

-1. Incorrecto porque 'c' es simplemente' a' convolucionado con 'b', no correlacionado. La inversión de tiempo estropeará las cosas y no dará el resultado deseado. –

+1

Tienes razón Steve. Escribí la respuesta como una idea aproximada. Lo he corregido para reflejar la conjugación. – highBandWidth

2

Depende del tipo de señal que tiene (periódicos ...?), Sobre si ambas señales tienen la misma amplitud y precisión de lo que busca.

La función de correlación mencionada por highBandWidth podría funcionar para usted. Es lo suficientemente simple como para intentarlo.

Otra opción más precisa es la que uso para el ajuste de línea espectral de alta precisión: modela su señal "maestra" con una spline y ajusta la señal desplazada en el tiempo con ella (aunque posiblemente escale la señal, si es necesario ser). Esto produce cambios de tiempo muy precisos. Una ventaja de este enfoque es que no tiene que estudiar la función de correlación. Por ejemplo, puede crear la spline fácilmente con interpolate.UnivariateSpline() (desde SciPy). SciPy devuelve una función, que luego se ajusta fácilmente con optimize.leastsq().

+0

¡Gracias! Acabo de utilizar optimize.leastsq: no tenía idea de que esto era manejable para timeshifts; mucho más fácil que un enfoque de convolución. ¿Sabes si hay alguna referencia sobre cómo funciona optimize.leastsq? Pensé que los mínimos cuadrados tenían que funcionar con combinaciones lineales de funciones básicas de entrada. –

+1

En la [documentación] (http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.leastsq.html) se lee que "leastsq" es un envoltorio alrededor de los algoritmos lmdif y lmder de MINPACK. " Puede encontrar más información en el código de MINPACK: http://www.netlib.org/minpack/lmdif.f y http://www.netlib.org/minpack/lmder.f. – EOL

6

Esta función es probablemente más eficiente para señales con valores reales. Utiliza RFFT y cero rellena las entradas a una potencia de 2 suficientemente grande para asegurar lineal (es decir, no circular) de correlación:

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 

El valor de retorno es la longitud M = len(x) + len(y) - 1 (cortado junto con hstack para eliminar los ceros adicionales de redondeando a una potencia de 2).Los retardos no negativos son cxy[0], cxy[1], ..., cxy[len(x)-1], mientras que los retardos negativos son cxy[-1], cxy[-2], ..., cxy[-len(y)+1].

Para que coincida con una señal de referencia, calculo rfft_xcorr(x, ref) y busco el pico. Por ejemplo:

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 

No es una forma robusta de hacer coincidir las señales, pero es rápido y fácil.

1

Aquí hay otra opción:

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