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í s
está el signo del producto de las dos ondas sinusoidales. Encontrar el promedio de este uso numpy.trapz
da 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
?
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
con
como único parámetro libre. Esto podría converger en varios valores diferentes de
, así que tómalo módulo
, luego haga lo mismo para la corriente y reste los dos valores de
, teniendo cuidado de sumar o restar
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
para ponerlo en el rango significativo.
Voy a dar dos opciones. No sé cuál dará mejores resultados.
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.
Construya una nueva matriz de seno y coseno en su cuadrícula de tiempo. Estimar
para cada una de sus dos señales, usando trapz
como 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
, 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 en las estimaciones de fase de las señales individuales. También tenga cuidado con posibles 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 correlate
función que se encuentra en la numpy
biblioteca 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_shift
es 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_shift
es 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.
[-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
?dt
matriz; 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 dt
método de búsqueda de índice). Una cosa que está mal con su sugerencia es que también generará una matriz (recuerde, t
es 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.
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,
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.
Vladislav Martín
numpy
'scorrelate
o una biblioteca comoscipy
(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).Marko Bursic
usuario103380
el fotón
keith
Vladislav Martín
Andy alias