Formas de estimar el cambio de fase

Tengo un sistema que está muestreando dos ondas sinusoidales de 50 Hz a 1 kHz. ¿Cuál es la mejor manera de estimar el cambio de fase entre los dos?

Las medidas son algo ruidosas y el periodo de muestreo no es muy parejo. Conozco los tiempos de muestra en alta resolución, pero no puedo controlarlos para que estén espaciados uniformemente. El cambio de fase debe estimarse en fragmentos de datos que cubran aproximadamente un segundo.

El ruido de medición hace que el método de cruce por cero no sea confiable; si el ruido hace que una señal medida cruce el cero dos veces en un cruce por cero "real", entonces el algoritmo falla un poco.

Actualmente estoy haciendo algo como esto (código python):

t, v, a = [NumPY arrays of samples]
p_inst = v * a
s = numpy.sign(p_inst)
s_avg = numpy.trapz(s, x=t) / (t[-1] - t[0])
shift_fraction = s_avg / 2 + 0.5
shift_angle = (1 - shift_fraction) * numpy.pi

Aquí sestá el signo del producto de las dos ondas sinusoidales. Encontrar el promedio de este uso numpy.trapzda un valor que representa con qué frecuencia las dos señales tienen el mismo signo, con 1 que significa "todo el tiempo", -1 que significa "ninguna de las veces" y cero que significa "la mitad del tiempo". Esto se convierte en una fracción del tiempo en que las señales tienen el mismo signo ( shift_fraction) y esto se convierte en un ángulo de cambio de fase.

El mayor problema con esto es que no te dice si la diferencia de fase está adelantada o retrasada.

¿Alguien tiene un método mejor?

Editar Para aquellos que sugieren encontrar el pico de correlación, ¿tengo razón al pensar que, con 20 muestras por ciclo, ese método necesariamente limitará la resolución de la estimación a 18 grados? ¿Qué le parecería calcular numpy.correlate(v, -a), encontrar el primer cruce por cero y luego interpolar para estimar el punto cero real? Creo que, para señales sinusoidales, la correlación también debería ser sinusoidal, por lo que la aproximación de ángulo pequeño debería hacer que la interpolación sea razonablemente buena.

¿Alguien puede comparar los métodos de correlación y demodulación?

Edición adicional Y, para aquellos que dan el método de demodulación, ¿tengo razón al pensar que si no me importa la diferencia de fase real, solo el factor de potencia, entonces puedo hacer esto?

s = sin(t * 2 * pi * f)
c = sin(t * 2 * pi * f)
a_s_d = s * a
v_s_d = s * v
a_c_d = c * a
v_c_d = c * v
s_a = numpy.trapz(a_s_d, t)
c_a = numpy.trapz(a_c_d, t)
s_v = numpy.trapz(v_s_d, t)
c_v = numpy.trapz(v_c_d, t)
# cos ( a - b ) = cos a * cos b + sin a * sin b
power_factor = c_a * c_v + s_a * s_v

¿O no puedo confiar en 0 < s_a, c_a, s_v, c_v < 1?

¿Ha considerado tomar una correlación cruzada de las dos señales, usando numpy's correlateo una biblioteca como scipy(es decir, scipy.​signal.​signaltools.correlate(A, B))? El cambio de tiempo del pico de la correlación cruzada captura la diferencia de fase. Hay una buena respuesta en StackOverflow que analiza esto (aunque es más complejo).
correlación cruzada, pero obtendrá picos cada 2 * PI si las señales son sinusoidales.
¿Por qué está etiquetado como "corrección del factor de potencia"? La corrección del factor de potencia tiene que ver con líneas de alto voltaje y no con muestreo.
@KingDuken, está estimando la diferencia de fase entre las formas de onda de voltaje y corriente (según los nombres de las variables en el pseudocódigo). Probablemente con el propósito de estimar el factor de potencia.
El filtrado podría ayudar con el ruido. Tal vez el cruce por cero sería más confiable.
@MarkoBuršič Tienes razón. Sin embargo, dadas dos sinusoides finitas, el más alto de esos picos corresponderá a (0 <= cambio de tiempo < período del seno). El resto de esos picos se degradarán con cada cambio de fase adicional (2 * pi).
Si está tratando de estimar PF, mire mi respuesta aquí: electronics.stackexchange.com/questions/76213/… - si está tratando de estimar la potencia, consulte esto también: electronics.stackexchange.com/questions/202667/… y esto: electronics.stackexchange.com/questions/82188/…

Respuestas (6)

La forma fácil de seguir pero intensiva en CPU

Si desea algo que sea obvio sobre cómo funciona y simple de entender, y no le importa arrojarle muchos ciclos de CPU (parece que no, si está usando python), entonces podría encajar sine ondas a las dos señales y lea la salida de fase de la función de ajuste.

Probablemente querrá usar scipy.optimise.curve_fit()para adaptarse a algo como V = A × pecado ( 2 π F t + ϕ ) con ϕ como único parámetro libre. Esto podría converger en varios valores diferentes de ϕ , así que tómalo módulo 2 π , luego haga lo mismo para la corriente y reste los dos valores de ϕ , teniendo cuidado de sumar o restar 2 π para ponerlo en el rango significativo.

Esto requerirá mucho, mucho más CPU que el filtrado o la detección homodina en tiempo real, pero es fácil de seguir, solo unas pocas líneas de largo y los ciclos de CPU son baratos.

Si no puede pagar esos ciclos de CPU

Luego, es posible que desee intentar generar dos ondas sinusoidales en cuadratura en el software y luego usarlas para realizar la demodulación IQ de las dos señales. Luego calcula las fases y resta. Esto no será tan fácil de seguir, pero podría hacerse muy rápido (incluso en python) y será muy, muy preciso.

Tome una porción de datos que es un número entero de ciclos (suponiendo que su red de 50 Hz sea agradable y estable, solo tome 1s de datos) y multiplíquelo punto por punto con cada una de las ondas sinusoidales generadas. Luego integre cada uno usando numpy.trapz()para obtener dos escalares. Tómelos como argumentos para numpy.arctan2(), y listo, tiene la fase de esa señal en relación con la onda sinusoidal generada. Haz lo mismo con la segunda señal y resta los dos para obtener la diferencia. Como arriba, suma o resta 2 π para ponerlo en el rango significativo.

Terminé usando su método de demodulación en cuadratura. ¡Lo siento, tardé tanto en dar crédito!

Voy a dar dos opciones. No sé cuál dará mejores resultados.

  1. Interpole sus datos en una cuadrícula de tiempo regular. Luego, use todos los métodos conocidos para la estimación de fase (cruces por cero, correlación cruzada, ...) que usted y otras respuestas mencionaron.

  2. Construya una nueva matriz de seno y coseno en su cuadrícula de tiempo. Estimar

A = 0 T F ( t ) pecado ( 2 π F t ) d t
y
B = 0 T F ( t ) porque ( 2 π F t ) d t

para cada una de sus dos señales, usando trapzcomo lo hizo para estimar la potencia promedio.

Ahora, la fase de cada señal en relación con el coseno de referencia se puede estimar como broncearse 1 ( A B ) , o en Python numpy.arctan2(B, A). Y la diferencia de fase entre las dos señales se puede obtener por sustracción.

Advertencia: existe la posibilidad de que haya cometido un error en lo anterior que dará un error de π / 2 en las estimaciones de fase de las señales individuales. También tenga cuidado con posibles 2 π envolvente en la resta final. Verifique que sus resultados sean razonables antes de usarlos.

En el mundo del procesamiento de señales, la diferencia de fase se estima comúnmente con la correlación cruzada de dos señales; llamémoslos a(t) y b(t). Dado que a(t) yb(t) son formas de onda de forma similar, la correlación cruzada debería ser un enfoque confiable.

Aquí hay una secuencia de comandos de Python (parte de la cual se inspiró en esta respuesta SO ) que usa la correlatefunción que se encuentra en la numpybiblioteca para calcular el cambio de fase entre dos ondas sinusoidales , a (t) y b (t). Debería poder extrapolar el contenido de este script para su propia aplicación:

import numpy as np

# Create the time axis (seconds)
num_samples = 10001
samples_per_second = 1000
freq_Hz = 50.0
t = np.linspace(0.0, ((num_samples - 1) / samples_per_second), num_samples)
# Create a sine wave, a(t), with a frequency of 1 Hz
a = np.sin((2.0 * np.pi) * freq_Hz * t)
# Create b(t), a (pi / 2.0) phase-shifted replica of a(t)
b_shift = (np.pi / 2.0)
b = np.sin((2.0 * np.pi) * freq_Hz * t + b_shift)

# Cross-correlate the signals, a(t) & b(t)
ab_corr = np.correlate(a, b, "full")
dt = np.linspace(-t[-1], t[-1], (2 * num_samples) - 1)
# Calculate time & phase shifts
t_shift_alt = (1.0 / samples_per_second) * ab_corr.argmax() - t[-1]
t_shift = dt[ab_corr.argmax()]
# Limit phase_shift to [-pi, pi]
phase_shift = ((2.0 * np.pi) * ((t_shift / (1.0 / freq_Hz)) % 1.0)) - np.pi

manual_t_shift = (b_shift / (2.0 * np.pi)) / freq_Hz

# Print out applied & calculated shifts
print ("Manual time shift: {}".format(manual_t_shift))            --> 0.005
print ("Alternate calculated time shift: {}".format(t_shift_alt)) --> 0.005000000000000782
print ("Calculated time shift: {}".format(t_shift))               --> 0.005000000000000782                         
print ("Manual phase shift: {}".format(b_shift))                  --> 1.5707963267948966, b(t) relative to a(t)                            
print ("Calculated phase shift: {}".format(phase_shift))          --> -1.570796326794651, a(t) relative to b(t)

Cuando phase_shiftes negativo, entonces sabe que la entrada de la primera señal correlate()está retrasada con respecto a la entrada de la segunda señal. En nuestro caso, esto significa que a(t) va a la zaga de b(t) por (pi / 2). Cuando phase_shiftes positivo, entonces sabe que la primera entrada de señal está adelantada a la segunda entrada de señal.

Como puede ver, la correlación cruzada de dos señales se puede usar simplemente para detectar la diferencia de fase entre dos señales. El vector de error entre los cambios de tiempo/fase reales y calculados permanecerá bajo siempre que sus señales estén relacionadas linealmente, se muestreen a una velocidad superior a su límite de Nyquist y tengan una SNR decente.

Dato curioso, aún puede usar la correlación cruzada para esta aplicación, incluso si las señales son a-periódicas. Tenga cuidado al tomar la correlación cruzada entre dos señales que están relacionadas de forma no lineal.

Gracias por una respuesta detallada. ¿Podría echar un vistazo a la edición de la pregunta y responder? En su ejemplo, tiene 1000 muestras por ciclo, mientras que yo tengo 20, ¿no limita esto más bien la resolución de este método?
Además, ¿generar una matriz [-t:t/num_samples:t]y usar una búsqueda de índice no es una forma desperdiciada de calcular el cambio de tiempo? ¿ Qué tiene de malo 2 * t * (ab_corr.argmax() - num_samples/2) / num_samples?
@Tom Ver mis resultados; coinciden con el cambio de fase (pi / 2) que esperamos bien. No creo que la correlación cruzada esté limitada por la frecuencia de muestreo, siempre que obedezca el teorema de Nyquist (es decir, no submuestree sus señales). En cuanto a la dtmatriz; He incluido un método alternativo para calcular el cambio de hora que evita una búsqueda de índice (produciendo el mismo cambio de hora que con el dtmétodo de búsqueda de índice). Una cosa que está mal con su sugerencia es que también generará una matriz (recuerde, tes una matriz) ...

No lo menciona, pero según sus variables, parece que está intentando medir el cambio de fase entre la corriente y el voltaje en una línea de 50 Hz.

Para realizar mediciones de fase adecuadas, es esencial que la señal esté limpia y usted mencionó específicamente que no lo está. Para obtener una señal limpia, puede realizar un filtrado de paso bajo de forma analógica antes del ADC o publicar digitalmente el ADC. Naturalmente, cualquier cambio de fase del filtrado debe ser igual para ambas señales o el error de fase introducido debe ser predecible.

Dos medios comunes de implementar un filtro de paso bajo digital son los algoritmos IIR y FIR. Afortunadamente, la biblioteca de scipy las tiene incorporadas. Por ejemplo, mire las funciones de filtro de scipy. Sin embargo, tenga en cuenta que sus datos se consideran datos de tiempo no uniformes, por lo que primero deberá volver a muestrear sus datos utilizando una spline u otro algoritmo de interpolación apropiado.

Una vez que las señales han pasado por el filtro LP, la técnica más común para encontrar la diferencia de fase es encontrar el pico de cada señal, calcular la diferencia de tiempo entre los picos y convertirla a grados o radianes.

En realidad, sería más exacto decir que no tengo mucha confianza en el ruido de las señales, en lugar de decir que tengo pruebas sólidas para decir que es malo. En cuanto a su método para calcular el cambio de fase, ¿cómo es mejor que el método de cruce por cero? Me parece que la pequeña pendiente en el pico de una sinusoide haría que estimar el pico sea peor que estimar el cruce por cero.
Lo he usado antes con bastante éxito. Con su alta frecuencia de muestreo, no debería ser un problema.

Una forma es utilizar la detección sensible a la fase. A 50 Hz con 1000 muestras, obtiene 20 muestras por ciclo. Use bloques de 20 muestras y reste la suma de las segundas 10 muestras de la suma de las primeras 10 muestras. Esto le dará un valor que está relacionado con la relación de fase entre el límite del bloque y la señal mediante la función sinusoidal (tendrá que escalar la amplitud; puede usar un promedio del voltaje PP). Estará en un pico positivo cuando esté exactamente en fase y en un pico negativo cuando esté a 180°. El enfoque elimina cualquier componente de CC. Luego puede promediar este valor durante muchos ciclos para mitigar su ruido. Si mide ambas señales con el mismo tiempo de bloque, obtiene la relación de fase entre cada señal y su sistema de muestreo, lo que le da una respuesta. Alternativamente,

¿Funciona este método con intervalos de muestra irregulares?
No. Confía en dos cosas: la cantidad de tiempo para las muestras positivas y la cantidad de tiempo para las muestras invertidas son exactamente iguales, y que están en la frecuencia de interés. Creo que puede ver que básicamente está tomando los datos y restando la mitad de las muestras de la otra mitad, lo que debería arrojar cero para cualquier elemento aleatorio. Solo los datos que tengan la característica de tener un valor de diferente polaridad en cada medio ciclo a su tasa de bloque producirán algún resultado a largo plazo.

Si su único problema con la medición es la ambigüedad, puede resolverlo volviendo a los datos, usando algún método para armonizar los cruces por cero demasiado cerca uno del otro (tome su tiempo medio de llegada, tal vez), y luego calculando el ángulo usando el método de cruce por cero. El resultado que desea será cualquiera de los dos cambios de fase posibles que esté más cerca del resultado del cruce por cero.

Mientras estoy en el tema, un método para encontrar el ángulo de fase que nunca he probado, pero que debería funcionar, que también da resultados ambiguos, es medir el voltaje de cada onda sinusoidal y luego medir la diferencia de voltaje entre ellos. Eso te daría los 3 lados de un triángulo y puedes encontrar los ángulos con ley de senos/ley de cosenos.