Fehler bei der korrekten Identifizierung von Ls und Rs aus gemessenem Vin und Vo in LTSpice

Als Übung (unter Verwendung von Python zur Verarbeitung von LTSpice-exportierten Daten) versuche ich, die Werte von Rs und Ls zu bestimmen, die in einer Schaltung verwendet wurden, aus den Messungen von Vin, Vout und der Phasenvoreilung/-verzögerung zwischen den beiden.

Ich habe eine Schaltung mit bestimmten Werten für Rs und Ls simuliert und dann versucht, diese Werte anhand der gemessenen Daten zu bestimmen. Die Beispielschaltung ist unten gezeigt, ebenso wie die daraus erhaltenen Wellenformen.

SchaltplanWellenformen

LTSpice tastet nicht in gleichmäßigen Intervallen ab, daher besteht ein Trick, um gleichmäßig abgetastete Daten wiederherzustellen, darin, eine FFT der Signale zu nehmen und dann eine weitere FFT der FFT-Signale zu nehmen, um die Zeitbereichsdaten wiederherzustellen, jetzt mit gleichmäßiger Abtastung (LTSpice tut dies Interpolation für die FFT). Ich mache das und exportiere diese Daten dann in ein Python-Skript, wo ich versuche, die Amplituden von Vo und Vi und die Phasenverzögerung zwischen ihnen zu bestimmen, und dann diese Werte verwende, um die Rs und Ls über die folgenden Formeln zu identifizieren :

Seit vout,

dann
Geben Sie hier die Bildbeschreibung einwobei V1 und Vo die Amplituden von Sinuskurven sind, undGeben Sie hier die Bildbeschreibung ein

Daher kann ich durch Auflösen nach Größe und Phase von Z die Werte von R und L bestimmen:
Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

SeitGeben Sie hier die Bildbeschreibung ein

Dann

eq10.

Das Problem ist, dass die Antwort, die ich bekomme, falsch ist, und da es kein "Rauschen" in den Messungen gibt, nahm ich an, dass ich leicht die genaue Antwort bekommen würde. Unter der Annahme, dass meine obige Mathematik korrekt ist, rührt das Problem meiner Meinung nach von der Tatsache her, dass ich die Amplituden von Vo, Vi und die Phasenverzögerung zwischen ihnen aus den verarbeiteten Samples nicht genau bestimmen kann.

Ich habe versucht, die Spitzen und die Phasenverzögerung zu differenzieren: Ich differenziere die Wellenformen und interpoliere dann die Werte für Zeit und Spannung, wenn eine Vorzeichenänderung auftritt, die das Maximum (und das Minimum) der Wellenformen anzeigt. Ich mittele dann die Spitzenwerte und die Differenz zwischen den Vin-Spitzen und den Vo-Spitzen über alle Proben, um zu versuchen, den Fehler zu reduzieren.

Ich habe auch die zirkuläre Kreuzkorrelation versucht, um die Phasenverzögerung zu finden, weil ich dachte, dass der Fehler bei der Bestimmung der Phasenverzögerung vielleicht zu groß sei.

Schließlich habe ich versucht, die verschiedenen Vo- und Vi-Samples mit der Methode der kleinsten Quadrate an Sinuskurven anzupassen.

Alle drei Fälle geben mir nicht die richtige Lösung für Ls und Rs, wie in der Tabelle gezeigt (sie enthält auch die ideale, berechnete Lösung).Geben Sie hier die Bildbeschreibung ein

Ich kann die Daten, den Python-Notebook-Code usw. teilen, wenn jemand daran interessiert ist, mir dabei zu helfen, herauszufinden, warum diese scheinbar einfache Übung für mich nicht funktioniert.

Danke schön.

[BEARBEITEN] Etwas Code hinzugefügt ....

Aufstellen:

vi=data[1:,1]
vo=data[1:,2]
time=data[1:,0]
plt.plot(time,vi)
plt.plot(time,vo)
plt.show()

 deltaT= time[2]-time[1]

Ableitungsmethode:

# Use the derivatve to find the slope change from positive to negative. Select the point half way between previous and current sample for the corresponding values of vo, vi, and time.

# In[28]:


tmpderVo = 0
tmpderVi = 0

peakVotimearray=[]
peakVoarray    =[]
peakVitimearray=[]
peakViarray    =[]
derVoArr       =[]
derViArr       =[]

for i in range(len(time)-1):
    derVo = (vo[i+1]-vo[i])/deltaT  # derivative
    derVoArr.append(derVo)
    if np.sign(tmpderVo)==1:
        if np.sign(derVo) != np.sign(tmpderVo):
            # interpolate time and Vo 
            newtime= time[i]+deltaT/2
            peakVotimearray.append(newtime)
            peakVo = vo[i]+(vo[i+1]-vo[i])/2
            peakVoarray.append(peakVo)

    derVi=(vi[i+1]-vi[i])/deltaT  # derivative
    derViArr.append(derVi)
    if np.sign(tmpderVi)==1:
        if  np.sign(derVi) != np.sign(tmpderVi):
            # interpolate time and Vi  -- half way point 
            newtime= time[i]+deltaT/2
            peakVitimearray.append(newtime)
            peakVi = vi[i]+(vi[i+1]-vi[i])/2
            peakViarray.append(peakVi)

    tmpderVo = derVo
    tmpderVi = derVi

plt.plot(derVoArr[10:100],'-*k')
plt.show()
# Average Vo and Vi peaks
peakVoave= np.mean(np.array(peakVoarray)[10:-10])
stdVoave = np.std(np.array(peakVoarray)[10:-10])
peakViave= np.mean(np.array(peakViarray)[10:-10])
stdViave = np.std(np.array(peakViarray)[10:-10])

# Average Time delay
timedlyarray=np.array(peakVitimearray)-np.array(peakVotimearray)
timedlyave = np.mean(timedlyarray[10:-10])
timedlystd  = np.std(timedlyarray[10:-10])

print('time delay average= ', timedlyave, '\t Time Delay STD = ',timedlystd )
print('Coefficient of Variability of Delay Measurement = ', timedlystd/timedlyave)
print('\nAverage Vo Amplitude = ' , peakVoave,'\t Average Vi Amplitude = ' , peakViave)
print('Vo Amplitude STD = ', stdVoave, '\t Vi Amplitude STD = ', stdViave)
print('\nCoefficient of Variability of Vo Peak Measurement = ', stdVoave/peakVoave)
print('Coefficient of Variability of Vi Peak Measurement = ', stdViave/peakViave)

print('\nSkipped the first 10 values in array for average calculation to avoid any data edge effects\n')


plt.plot(time[periodstart:periodend],vo[periodstart:periodend], time[periodstart:periodend],vi[periodstart:periodend])
# indices for peak arrays no longer match original data indices, need to recover them below
frac = periodstart/len(time)  # what fraction of whole time array are we starting at
offset=int(len(peakVotimearray)*frac) # determine offset into peaktime array, one peak per period
plt.vlines(peakVotimearray[offset:int(offset+len(time[periodstart:periodend])*deltaT/1e-6)], -1, 1, colors='r', linestyles='dashed',linewidth=1)
plt.vlines(peakVitimearray[offset:int(offset+len(time[periodstart:periodend])*deltaT/1e-6)], -1, 1, colors='r', linestyles='dashed',linewidth=1)
plt.title('Sketch of Vi and Vo and their phase relationship')
plt.legend(['Vin','Vo'])
plt.show()

# ### Determine Time Delay using Peaks found via Derivatives
peakdly=timedlyave
peakdlyrad=timedlyave/T*2*np.pi
print(peakdlyrad)

Wellenformskizze

XCorr-Methode

aus numpy.fft importiere fft, ifft

def periodic_corr(x, y):
    """Periodic correlation, implemented using the FFT.

    x and y must be real sequences with the same length.
    """
    return ifft(fft(x) * fft(y).conj()).real

xcorr=periodic_corr(vi,vo)
dlyndx= np.argmax(xcorr)
print('Index: ',dlyndx, '\tTime delay: ',dlyndx*deltaT)

plt.plot(time,vi)
plt.plot(time+dlyndx*deltaT,vo)
plt.show()
timedly=dlyndx*deltaT/T*2*np.pi

xcorr-Wellenformen

LS-Schätzer zum Ermitteln der Amplitude

D0 =np.array([np.cos(2*np.pi*f*time),np.sin(2*np.pi*f*time),np.ones(time.size)],'float').transpose()

vin=np.array([vi]).T
vout=np.array([vo]).T
print(np.concatenate([vin,vout], axis=1))

from numpy.linalg import inv

s_hat_vin = np.matmul(inv(np.matmul(D0.T,D0)),np.matmul(D0.T,vin))
s_hat_vo  = np.matmul(inv(np.matmul(D0.T,D0)),np.matmul(D0.T,vout))

vinMag = np.sqrt(s_hat_vin[0]**2+s_hat_vin[1]**2)
vinPh  = -np.arctan2(s_hat_vin[1],s_hat_vin[0])

voMag = np.sqrt(s_hat_vo[0]**2+s_hat_vo[1]**2)
voPh  = -np.arctan2(s_hat_vo[1],s_hat_vo[0])

print(vinMag,vinPh)
print(voMag, voPh)

#plt.plot(time,vo)
plt.plot(time,vinMag*np.cos(2*np.pi*f*time + vinPh) + s_hat_vin[2])
plt.plot(time,voMag *np.cos(2*np.pi*f*time + voPh) + s_hat_vo[2])
plt.plot(time,voMag *np.cos(2*np.pi*f*time + voPh+(vinPh-voPh)) + s_hat_vo[2])
plt.show()

lsm_dly = (vinPh-voPh)

Geben Sie hier die Bildbeschreibung ein

def Zmagphase(vipeak,vopeak,theta,R1):
    """Returns the magnitude and phase of Z."""

    magZ  = (vopeak*R1)/(np.sqrt(vipeak**2 - 2*vipeak*vopeak*np.cos(theta)+ vopeak**2))
    phaseZ = theta - np.arctan2(-vopeak*np.sin(theta),(vipeak-vopeak*np.cos(theta)))

    return [magZ,phaseZ]

def Z2LsRs(mag,ph,f):
    """Determines Ls and Rs from Z in polar form"""
    w= 2*np.pi*f
    Rs = mag*np.cos(ph)
    Ls = mag*np.sin(ph)/(w)

    return [Rs, Ls]

FFT-Lösung

Fs = 1/deltaT
T= deltaT
N =  len(vi)
freq = Fs/N*np.arange(1,int(N/2)+1)

y=np.fft.fft(vi)
vimagfft=2/N*np.abs(y)[0:int(N/2)+1]
vimagfft=vimagfft[1:]
viphase = np.angle(y)[1:int(N/2)+1]

x=np.fft.fft(vo)
vomagfft=2/N*np.abs(x)[0:int(N/2)+1]
vomagfft=vomagfft[1:]
vophase = np.angle(x)[1:int(N/2)+1]

plt.plot(freq,vimagfft,'-*k', freq, vomagfft, '-*r')
plt.axis([0, 10000000, 0, 10])
plt.autoscale(True, 'y')
plt.show()


viFFT = np.max(vimagfft)
voFFT = np.max(vomagfft)

thetaFFT = vophase[np.argmax(vomagfft)]-viphase[np.argmax(vimagfft)]
print('ViampFFT = ', viFFT, '\t VoampFFT = ' , voFFT)
print('Phase Delay =',thetaFFT)

Ergebnisse in:

Geben Sie hier die Bildbeschreibung ein

fft_sol

Ideale Lösung

from numpy import exp, abs, angle

def polar2z(r,theta):
    return r * exp( 1j * theta )

def z2polar(a,b):
    z = a + 1j * b
    return ( abs(z), angle(z) )

Vin = 1*exp(0j)


Vo=1*exp(0j)*(.118+(2*np.pi*1e6*204e-6j))/(1e3+.118+(2*np.pi*1e6*204e-6j))

Vomag=np.abs(Vo)
Votheta=np.angle(Vo)

magZideal= (Vomag*R1)/(np.sqrt(abs(Vin)**2 - 2*abs(Vin)*Vomag*np.cos(Votheta)+ Vomag**2))
print('Z_magIdeal = ', magZideal)


phZideal = Votheta - np.arctan2(-Vomag*np.sin(Votheta),(abs(Vin)-Vomag*np.cos(Votheta)))
print(phZideal)


R = magZideal*np.cos(phZideal)
L = magZideal*np.sin(phZideal)/(w)
print('R = ',R,'\t', 'L = ',L)

Zusammenfassung der Lösung Nach der Empfehlung von @ocspro, den maximalen Schritt in der LTSpice-Simulation auf 1n zu begrenzen, sind die Ergebnisse besser, wenn auch nicht 100 % korrekt bei der Identifizierung der Rs. Vielleicht liegt dies an der hohen numerischen Empfindlichkeit des cos (phaseZ) um den Pi / 2 ... nicht sicher, wie dies angegangen werden soll (siehe xcorr-Lösung). Wie auch immer, es scheint, dass alle gewählten Ansätze ähnliche Lösungen ergeben (mit Ausnahme von Xcorr, wo Rs negativ ist, da die berechnete phaseZ etwas größer als pi / 2 ist):

Differenzierungsmethode diff_method_sol

Xcorr-Methode xcorr_method_sol

Methode der kleinsten Quadratels_sqr_sol

FFT-Methode FFT_method_sol

Ideale Lösung

Ideal Soln

Schritt 1: Können Sie die LTspice-Ausgabedatei in Ihren Python-Code einlesen und einfach neu plotten, um zu überprüfen, ob Sie sie richtig lesen? Schritt 2: Können Sie die Größe und Phase von abschätzen? v ich Und v Ö korrekt? Erst nachdem Sie sich vergewissert haben, dass Sie die Schritte 1 und 2 ausführen können, sollten Sie sich um Schritt 3 kümmern: Verwenden v ich Und v Ö Schaltungsgrößen abzuschätzen R S , L S , Und R 1 .
Geht Ihr Python-Code auch davon aus, dass die Frequenz des Signals a priori bekannt ist, oder müssen Sie die Frequenz auch aus den Daten schätzen?
Wie viele Punkte FFT? Haben Sie mit verschiedenen FFT-Längen experimentiert und einen Trend zwischen FFT-Länge und Genauigkeit festgestellt?
@ThePhoton, ja - die Schätzungen sind alle gut ... es scheint die Genauigkeit zu sein, die das Problem darstellt. Die Häufigkeit ist im Voraus bekannt.
Warten Sie, aber jetzt, wo ich genauer lese, merke ich, dass Sie sagten: "Ich denke, das Problem rührt von der Tatsache her, dass ich die Amplituden von Vo, Vi und die Phasenverzögerung zwischen ihnen aus den verarbeiteten Samples nicht genau bestimmen kann. ". Also nicht überstürzen und nach den Formeln fragen, die die Komponentenwerte aus den Amplituden und Phasen berechnen. Fragen Sie, warum Sie die Amplituden und Phasen nicht genau schätzen. (Und wenn Sie nützliche Antworten wünschen, fügen Sie Ihren Code hinzu, der die Amplituden und Phasen schätzt.)
@BrianDrummond, ich verwende die FFT-Funktionen in LTPSice - ich bin mir nicht sicher, wie viele Punkte es verwendet. Die andere Alternative besteht darin, die Zeitbereichssignale "wie sie sind" zu exportieren und dann eine kubische Spline-Interpolation oder ähnliches durchzuführen und das Ergebnis, das ich von diesem Ansatz erhalte, mit dem Ansatz zu vergleichen, bei dem ich mich auf die Interpolation verlasse, die LTspice auf die Daten ausführt bei der Berechnung der FFTs. LTSpice behauptet jedoch, dass ihre Methode eine sehr hohe Präzision hat ... Ich werde vielleicht als nächstes den Interpolationsansatz ausprobieren. danke für Ihre Antwort
@The Photon --- das ist, was ich frage ... Und ich würde gerne den gesamten Code und die Daten teilen --- wissen Sie, wie ich das machen kann?
Im Frageneditor gibt es eine Schaltfläche zum Formatieren von Code als Code (dh mit einer Monospace-Schriftart, eingerückt und mit einer anderen Hintergrundfarbe). Wenn Sie einen großen Codeblock posten, wird ein scrollbares Fenster erstellt. Aber Sie sollten versuchen, sich auf den Code zu konzentrieren, der das eigentliche Problem verursacht, denn Freiwillige lesen sich meistens nicht seitenlang Zeug durch, um zum eigentlichen Problem zu gelangen.
Ok ... Ich habe den Code hinzugefügt .... Es ist zu viel, denke ich. Es wäre besser, wenn ich das Jupyter-Notizbuch teilen könnte – es wäre klarer. Ich bin überrascht, dass dieser StackExchange keine einfache Möglichkeit bietet, solche Dinge zu teilen. Wenn ich meine Daten und mein Notizbuch teilen könnte, wäre es für die Leute einfacher zu helfen, denke ich .... Vielen Dank für Ihre bisherige Hilfe.
@BrianDrummond - Ich habe gerade Ihren Kommentar noch einmal gelesen und festgestellt, dass Sie möglicherweise nach dem Kreuzkorrelationsansatz gefragt haben, der die FFT und die inverse FFT verwendet. Trotzdem weiß ich nicht, wie viele Punkte numpy.fft verwendet ... Ich werde in der Dokumentation nachsehen. Danke schön.
"LTSpice tastet nicht in gleichmäßigen Intervallen ab, daher besteht ein Trick zum Wiederherstellen gleichmäßig abgetasteter Daten darin, eine FFT der Signale und dann eine weitere FFT der FFT-Signale zu verwenden, um die Zeitbereichsdaten wiederherzustellen" - das scheint lächerlich zu sein komplizierte Art, ein einfaches Problem zu lösen.
@Bruce Abbot bin mir nicht sicher, ob ich deinen Kommentar verstehe. Was ist die einfache Lösung zur Lösung der ungleichmäßigen Zeitschritte aus einer LTSpice-Transientenanalyse?
Extrahieren Sie die Nulldurchgangszeit jeder Sinuswelle, indem Sie die kleinsten positiven und negativen Werte erhalten und zwischen ihnen extrapolieren.
Sie können LTSpice zwingen, Samples in gleichmäßigen Intervallen auszugeben, auf der Registerkarte .TRANS Setup, IIRC ist es ein Eingabefeld mit dem Namen "print every xxx".
@Neil_UK - danke - ich habe nachgesehen und kann nicht herausfinden, wo ich es gemäß Ihrem Vorschlag auf einheitliches Sampling einstellen soll. Die einzigen Modifikatoren sind: UIC, Steady, Nodiscard, Startup und Step. Und in der eigentlichen Trans-Simulationskarte kann ich nur tun: Zeit stoppen, Zeit zum Starten des Datenspeicherns, max. Schritt, ext dc start bei 0, sim stoppen, wenn ss erreicht ist, Laststromquelle schalten und anfängliche Arbeitspunktlösung überspringen . Ich bin wirklich daran interessiert, also wenn du es herausfinden kannst, lass es mich wissen. Danke schön.
@jrive Hmm. Ich bin davon überzeugt, dass es früher funktioniert hat, aber jetzt scheint es nicht mehr zu funktionieren. Siehe diese Frage, die ich beantworte, ich scheine mir da sehr sicher zu sein. LTSpice IV verfügt nicht über diese Funktion. LTSpice XVII behauptet, aber es ist nur im Text einstellbar, nicht in der Bearbeitungssimulations-GUI, und dann scheint es jetzt nicht zu funktionieren. Ich würde empfehlen, die Ausgabedatei auf Ihre gewünschten Zeitschritte zu interpolieren, das wird funktionieren. Ich werde noch ein bisschen experimentieren, es wäre nützlich.
@jrive Ich bin jetzt bei meinen Suchen darauf gestoßen und sehe, woher Sie Ihren FFT-Prozess bekommen. Aus Ihrem OP ging nicht hervor, dass es sich um einen internen Trick von LTSpice handelte oder um einen dokumentierten Trick . Überprüfen Sie, ob die von diesem 2xFFT-Trick exportierten Zeitdaten mit den normal exportierten Zeitdaten übereinstimmen. Bei FFT-Implementierungen kann etwas schief gehen (Excel-XY-Streudiagramm interpoliert automatisch). Lesen Sie auch die anderen Antworten auf die in meinem vorherigen Kommentar verlinkte Frage durch, ltsputil scheint einen Blick wert zu sein.
@jrive Früher habe ich ein anderes Gewürz verwendet, Simetrix, das definitiv diese Funktion namens print_step hatte. Allerdings war die kostenlose Version Crippleware, limitiert auf 50 Nodes, was für ein Opamp-Modell reichte, aber nicht zwei, also komplett unskalierbar, also bin ich auf LT umgestiegen. Es ist wahrscheinlich, dass ich nie versucht habe, es in LTSpice zu verwenden, und zwischen den beiden Simulatoren verwechselt wurde. Wenn Sie das LTwiki lesen, heißt es, dass der Schrittparameter nutzlos ist. Schade um den 2-FFT-Trick, er liefert Ihnen die interpolierten Daten nicht ohne die FFTs!
@Bruce Abbot, ich verstehe immer noch nicht, wie das Extrapolieren zwischen den Nulldurchgängen mir eine einheitliche Abtastung gibt .....
Bei reinen Sinuswellen in einer linearen Schaltung brauchen Sie zur Bestimmung der Phase nur die Nulldurchgangspunkte. Möglicherweise haben Sie jedoch Proben nicht genau am Nullpunkt. Nehmen Sie also zwei aufeinanderfolgende Proben, eine auf der einen und die andere auf der anderen Seite von Null, extrapolieren Sie linear zwischen ihnen (fast kein Unterschied zu einer Sinuswelle bei diesem kleinen Abstand) und finden Sie den Zeitpunkt, an dem diese Linie Null kreuzt. Dies ist viel genauer als der Versuch, die Phase anhand der Spitzen der Sinuswellen zu messen.

Antworten (2)

Vielleicht liegt der Genauigkeitsfehler in der Anzahl der Simulationspunkte? Mit der integrierten fft-Funktion in LTspice ( .four 1000k 1 -1 V(Vi) V(Vo)) habe ich große Diskrepanzen in Größe und Phase erhalten, als ich Ihre Simulation mit Standard-Simulationseinstellungen ausgeführt habe.

Durch Verringern des maximalen Zeitschritts gelang es mir, Größe und Phasenverschiebung fast identisch mit der idealen Lösung zu erhalten

Vergleich des maximalen Schritts von 1 ns vs. keiner:

  • Vi = 0,9999 gegenüber 0,9803
  • Vo = 0,7883 gegenüber 0,7722
  • Phasenverschiebung = 0,6625 gegenüber 0,6475

Screenshot von Schaltung und Protokoll

Alternativ kann man die relative Toleranz verringern, um kürzere Zeitschritte zu erzwingen. Die automatische Schrittweite ist aufgrund der einfachen und linearen Schaltung wahrscheinlich ziemlich groß.

Danke. Viel besser. Ich bekomme immer noch einen Fehler in den identifizierten Rs, aber viel näher als zuvor: Rs_ident =0.204, Ls_ident =.204., unter Verwendung der Methode der kleinsten Quadrate:Vi = [0.99996636] Vo = [0.78834297] Phase Dly = [0.6624794] Zmag = [1281,7480386] Zph = [1,5706335] Rs = [0,20870581] Ls = [0,000204]. Die Xcorr-Methode funktioniert nicht mit den neuen Daten, ich bin mir noch nicht sicher warum ... die Phasenverzögerung spielt verrückt.

Ein in LTspice häufig vergessener Aspekt bei der Verwendung von Induktivitäten ist der standardmäßige Serienwiderstand von 1 mΩ.
Stellen Sie sicher, dass Sie es explizit auf Null setzen, um noch bessere Übereinstimmungsergebnisse zu erhalten!

Geben Sie hier die Bildbeschreibung ein

Fügen Sie auch das Bit about hinzu Rpar, das standardmäßig 1e12den Induktivitätswert multipliziert. Macht sich auch bei kleinen Induktivitäten Rparbemerkbar.
ja ... das hatte ich getan ... danke.