Ich habe ein System, das zwei 50-Hz-Sinuswellen mit 1 kHz abtastet. Wie kann man die Phasenverschiebung zwischen den beiden am besten abschätzen?
Die Messungen sind etwas verrauscht und die Abtastperiode ist nicht sehr gleichmäßig. Ich kenne die Abtastzeiten in hoher Auflösung, kann sie aber nicht so steuern, dass sie gleichmäßig verteilt sind. Die Phasenverschiebung muss anhand von Datenblöcken geschätzt werden, die etwa eine Sekunde abdecken.
Das Messrauschen macht das Nulldurchgangsverfahren unzuverlässig; Wenn das Rauschen dazu führt, dass ein gemessenes Signal bei einem "echten" Nulldurchgang zweimal Null durchquert, bricht der Algorithmus etwas zusammen.
Derzeit mache ich so etwas (Python-Code):
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
Hier s
ist das Vorzeichen des Produkts der beiden Sinuswellen. Das Ermitteln des Mittelwerts dieser Verwendung numpy.trapz
ergibt einen Wert, der angibt, wie oft die beiden Signale das gleiche Vorzeichen haben, wobei 1 "immer", -1 "nicht immer" und null "halbe Zeit" bedeutet. Dies wird in einen Bruchteil der Zeit umgewandelt, in der die Signale dasselbe Vorzeichen haben ( shift_fraction
), und dies wird in einen Phasenverschiebungswinkel umgewandelt.
Das größte Problem dabei ist, dass es Ihnen nicht sagt, ob die Phasendifferenz voreilend oder nacheilend ist.
Hat jemand eine bessere Methode?
Bearbeiten Für diejenigen, die vorschlagen, den Korrelationspeak zu finden, gehe ich richtig in der Annahme, dass diese Methode bei 20 Abtastungen pro Zyklus die Auflösung der Schätzung zwangsläufig auf 18 Grad begrenzt? Was würden Sie davon halten, stattdessen zu berechnen numpy.correlate(v, -a)
, den ersten Nulldurchgang zu finden und dann zu interpolieren, um den tatsächlichen Nullpunkt zu schätzen? Ich denke, dass für sinusförmige Signale die Korrelation auch sinusförmig sein sollte, und daher sollte die Annäherung an kleine Winkel die Interpolation einigermaßen gut machen.
Kann jemand die Korrelations- und Demodulationsmethoden vergleichen?
Weiter bearbeiten Und für diejenigen, die die Demodulationsmethode angeben, habe ich Recht, wenn ich denke, dass ich dies tun kann, wenn ich mich nicht für die tatsächliche Phasendifferenz interessiere, sondern nur für den Leistungsfaktor:
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
Oder kann ich mich nicht darauf verlassen 0 < s_a, c_a, s_v, c_v < 1
?
Der einfach zu befolgende, aber CPU-intensive Weg
Wenn Sie etwas wollen, das offensichtlich funktioniert und einfach zu verstehen ist, und es Ihnen nichts ausmacht, viele CPU-Zyklen darauf zu werfen (klingt so, als würden Sie es nicht tun, wenn Sie Python verwenden), dann könnten Sie einfach Sinus anpassen Wellen zu den beiden Signalen und lesen Sie die Phase aus der Anpassungsfunktion ab.
Sie möchten wahrscheinlich verwenden, scipy.optimise.curve_fit()
um so etwas zu passen
mit
als einziger freier Parameter. Dies könnte zu mehreren unterschiedlichen Werten von konvergieren
, also nimm es modulo
, machen Sie dasselbe für den Strom und subtrahieren Sie die beiden Werte von
, achte darauf, zu addieren oder zu subtrahieren
um es in den sinnvollen Bereich zu bekommen.
Dies ist viel, viel CPU-intensiver als Filtern oder Homodyne-Erkennung in Echtzeit, aber es ist einfach zu befolgen, nur wenige Zeilen lang und CPU-Zyklen sind billig.
Wenn Sie sich diese CPU-Zyklen nicht leisten können
Dann möchten Sie vielleicht versuchen, zwei Quadratur-Sinuswellen in Software zu erzeugen und sie dann zur IQ-Demodulation der beiden Signale zu verwenden. Berechnen Sie dann die Phasen und subtrahieren Sie. Dies wird nicht so einfach zu befolgen sein, könnte aber sehr schnell gemacht werden (sogar in Python) und wird sehr, sehr genau sein.
Nehmen Sie einen Datenblock, der eine ganzzahlige Anzahl von Zyklen ist (vorausgesetzt, Ihr 50-Hz-Netz ist schön und stabil, nehmen Sie einfach 1 s Daten), und multiplizieren Sie ihn Punkt für Punkt mit jeder der erzeugten Sinuswellen. Integrieren Sie dann jeweils mit , numpy.trapz()
um zwei Skalare zu erhalten. Nehmen Sie diese als Argumente für numpy.arctan2()
, und voila, Sie haben die Phase dieses Signals relativ zur erzeugten Sinuswelle. Machen Sie dasselbe für das zweite Signal und subtrahieren Sie die beiden, um die Differenz zu erhalten. Wie oben addieren oder subtrahieren
um es in den sinnvollen Bereich zu bekommen.
Ich werde zwei Möglichkeiten geben. Ich weiß nicht, was bessere Ergebnisse liefert.
Interpolieren Sie Ihre Daten auf ein regelmäßiges Zeitraster. Verwenden Sie dann alle verschiedenen bekannten Methoden zur Phasenschätzung (Nulldurchgänge, Kreuzkorrelation, ...), die Sie und andere Antworten erwähnt haben.
Konstruieren Sie ein neues Sinus- und Cosinus-Array auf Ihrem Zeitraster. Schätzen
für jedes Ihrer beiden Signale, indem Sie so vorgehen, trapz
wie Sie es getan haben, um die durchschnittliche Leistung zu schätzen.
Nun kann die Phase jedes Signals relativ zum Referenzkosinus abgeschätzt werden
, oder in Python numpy.arctan2(B, A)
. Und die Phasendifferenz zwischen den zwei Signalen kann durch Subtraktion erhalten werden.
Vorbehalt: Es besteht die Möglichkeit, dass ich oben einen Fehler gemacht habe, der einen Fehler von ergibt in den Phasenschätzungen der einzelnen Signale. Seien Sie auch vorsichtig Wrap-Around in der letzten Subtraktion. Überprüfen Sie Ihre Ergebnisse auf Angemessenheit, bevor Sie sie verwenden.
In der Welt der Signalverarbeitung wird die Phasendifferenz üblicherweise durch die Kreuzkorrelation zweier Signale geschätzt; nennen wir sie a(t) und b(t). Da a(t) und b(t) ähnlich geformte Wellenformen sind, sollte Kreuzkorrelation ein zuverlässiger Ansatz sein.
Hier ist ein Python-Skript (von dem ein Teil von dieser SO-Antwort inspiriert wurde ), das die correlate
in der Bibliothek gefundene Funktion verwendet numpy
, um die Phasenverschiebung zwischen zwei Sinuswellen , a(t) und b(t), zu berechnen. Sie sollten den Inhalt dieses Skripts für Ihre eigene Anwendung extrapolieren können:
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)
Wenn phase_shift
negativ ist, dann wissen Sie, dass der 1. Signaleingang correlate()
hinter dem 2. Signaleingang zurückbleibt. In unserem Fall bedeutet dies, dass a(t) b(t) um (pi / 2) nacheilt. Wenn phase_shift
positiv, dann wissen Sie, dass der 1. Signaleingang dem 2. Signaleingang vorauseilt.
Wie Sie sehen können, kann die Kreuzkorrelation zweier Signale einfach verwendet werden, um die Phasendifferenz zwischen zwei Signalen zu erkennen. Der Fehlervektor zwischen den tatsächlichen und berechneten Zeit-/Phasenverschiebungen bleibt niedrig, solange Ihre Signale linear zusammenhängen, mit einer Rate über ihrer Nyquist-Grenze abgetastet werden und ein anständiges SNR haben.
Lustige Tatsache, Sie können die Kreuzkorrelation für diese Anwendung auch dann noch verwenden , wenn die Signale aperiodisch sind! Seien Sie vorsichtig, wenn Sie die Kreuzkorrelation zwischen zwei Signalen nehmen, die nicht linear miteinander verbunden sind.
[-t:t/num_samples:t]
und Verwenden einer Indexsuche nicht auch eine verschwenderische Art, die Zeitverschiebung zu berechnen? Was ist los mit 2 * t * (ab_corr.argmax() - num_samples/2) / num_samples
?dt
Array; Ich habe eine alternative Methode zur Berechnung der Zeitverschiebung eingefügt, die eine Indexsuche vermeidet (und dieselbe Zeitverschiebung wie bei der dt
Indexsuchmethode erzeugt). Eine Sache, die an Ihrem Vorschlag falsch ist, ist, dass er auch ein Array generiert (denken Sie daran, t
ist ein Array) ...Sie erwähnen es nicht, aber basierend auf Ihren Variablen scheint es, dass Sie versuchen, die Phasenverschiebung zwischen Strom und Spannung auf einer 50-Hz-Leitung zu messen.
Um korrekte Phasenmessungen durchzuführen, ist es wichtig, dass das Signal sauber ist, und Sie haben ausdrücklich erwähnt, dass dies nicht der Fall ist. Um ein sauberes Signal zu erhalten, können Sie eine Tiefpassfilterung entweder analog vor dem ADC oder digital nach dem ADC durchführen. Natürlich muss jede Phasenverschiebung vom Filtern für beide Signale gleich sein oder der eingeführte Phasenfehler muss vorhersagbar sein.
Zwei übliche Mittel zum Implementieren eines digitalen Tiefpassfilters sind IIR- und FIR-Algorithmen. Glücklicherweise sind sie in der scipy-Bibliothek integriert. Sehen Sie sich zum Beispiel die scipy-ifilter-Funktionen an. Beachten Sie jedoch, dass Ihre Daten als uneinheitliche Zeitdaten betrachtet werden, sodass Sie Ihre Daten zunächst mit einem Spline oder einem anderen geeigneten Interpolationsalgorithmus erneut abtasten müssen.
Sobald die Signale den LP-Filter durchlaufen haben, besteht die gebräuchlichste Methode zum Auffinden der Phasendifferenz darin, die Spitze jedes Signals zu finden, die Zeitdifferenz zwischen den Spitzen zu berechnen und diese in Grad oder Bogenmaß umzuwandeln.
Eine Möglichkeit besteht darin, eine phasenempfindliche Detektion zu verwenden. Bei 50 Hz mit 1000 Samples erhalten Sie 20 Samples pro Zyklus. Verwenden Sie 20-Sample-Blöcke und subtrahieren Sie die Summe der zweiten 10 Samples von der Summe der ersten 10 Samples. Dadurch erhalten Sie einen Wert, der sich auf die Phasenbeziehung zwischen der Grenze des Blocks und dem Signal durch die Sinusfunktion bezieht (Sie müssen für die Amplitude skalieren - Sie können einen Durchschnitt der PP-Spannung verwenden). Es wird eine positive Spitze haben, wenn Sie genau in Phase sind, und eine negative Spitze, wenn Sie 180 aus sind. Der Ansatz entfernt jede DC-Komponente. Sie können diesen Wert dann über viele Zyklen mitteln, um Ihr Rauschen zu mindern. Wenn Sie beide Signale mit demselben Block-Timing messen, erhalten Sie die Phasenbeziehung zwischen jedem Signal und Ihrem Abtastsystem, wodurch Sie eine Antwort erhalten. Alternative,
Wenn Ihr einziges Problem mit der Messung die Mehrdeutigkeit ist, können Sie es lösen, indem Sie zu den Daten zurückkehren, eine Methode anwenden, um zu nahe beieinander liegende Nulldurchgänge zu harmonisieren (nehmen Sie vielleicht ihre mittlere Ankunftszeit) und dann den Winkel berechnen mit die Nulldurchgangsmethode. Das gewünschte Ergebnis ist diejenige der beiden möglichen Phasenverschiebungen, die näher am Nulldurchgangsergebnis liegt.
Wo ich gerade beim Thema bin, eine Methode, um den Phasenwinkel zu finden, die ich noch nie ausprobiert habe, aber funktionieren sollte, die auch mehrdeutige Ergebnisse liefert, besteht darin, die Spannung jeder Sinuswelle zu messen und dann die Spannungsdifferenz zwischen ihnen zu messen. Das würde Ihnen die 3 Seiten eines Dreiecks geben und Sie können die Winkel mit dem Sinussatz / Kosinussatz finden.
Wladislav Martin
numpy
'scorrelate
oder eine Bibliothek wiescipy
(dhscipy.signal.signaltools.correlate(A, B)
) verwenden? Die Zeitverschiebung der Spitze der Kreuzkorrelation erfasst die Phasendifferenz. Es gibt eine gute Antwort auf StackOverflow, die dies bespricht (es ist jedoch komplexer).Marko Buršič
Benutzer103380
Das Photon
mkeith
Wladislav Martin
Andi aka