Error al identificar correctamente Ls y Rs de Vin y Vo medidos en LTSpice

Como ejercicio (usando Python para procesar los datos exportados de LTSpice), estoy tratando de determinar los valores de Rs y Ls que se usaron en un circuito, a partir de las medidas de Vin, Vout y el adelanto/retraso de fase entre los dos.

Simulé un circuito usando un valor específico para Rs y Ls, y luego traté de determinar cuáles eran esos valores a partir de los datos medidos. El circuito de muestra se muestra a continuación, así como las formas de onda obtenidas de él.

diagrama de circuitoformas de onda

LTSpice no muestrea a intervalos uniformes, por lo que un truco para recuperar datos muestreados uniformemente es tomar una FFT de las señales y luego tomar otra FFT de las señales FFT para recuperar los datos del dominio del tiempo, ahora con muestreo uniforme (LTSpice no interpolación para la FFT). Hago esto y luego exporto estos datos a una secuencia de comandos de Python donde trato de determinar las amplitudes de Vo y Vi, y el retraso de fase entre ellos, y luego uso esos valores para identificar las R y las L a través de las siguientes fórmulas :

desde fuera,

entonces
ingrese la descripción de la imagen aquídonde V1 y Vo son las amplitudes de las sinusoides, yingrese la descripción de la imagen aquí

Por lo tanto, resolviendo la magnitud y la fase de Z, puedo determinar los valores de R y L:
ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Desdeingrese la descripción de la imagen aquí

entonces

eq10.

El problema es que la respuesta que obtengo es incorrecta, y dado que no hay "ruido" en las mediciones, supuse que obtendría fácilmente la respuesta exacta. Suponiendo que mis cálculos anteriores sean correctos, creo que el problema se debe al hecho de que parece que no puedo determinar con precisión las amplitudes de Vo, Vi y el retraso de fase entre ellos a partir de las muestras procesadas.

Intenté la diferenciación para encontrar los picos y el retraso de fase: diferencio las formas de onda y luego interpolo los valores de tiempo y voltaje cuando ocurre un cambio en el signo que indica el máximo (y el mínimo) de las formas de onda. Luego promedio los valores de los picos y la diferencia entre los picos de Vin y los picos de Vo sobre todas las muestras para tratar de reducir el error.

También probé la correlación cruzada circular para encontrar el retraso de fase, pensando que quizás el error en la determinación del retraso de fase era demasiado grande.

Finalmente, intenté ajustar las diferentes muestras de Vo y Vi a sinusoides utilizando el método de mínimos cuadrados.

Los tres casos no me dan la solución correcta para Ls y Rs, como se muestra en la tabla (también incluye la solución calculada ideal)ingrese la descripción de la imagen aquí

Puedo compartir los datos, el código del cuaderno de Python, etc., si alguien está interesado en ayudarme a descubrir por qué este ejercicio aparentemente sencillo no funciona para mí.

Gracias.

[EDITAR] Se agregó algo de código....

Configuración:

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]

Método Derivado:

# 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)

boceto de forma de onda

Método XCorr

desde numpy.fft importar 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

formas de onda xcorr

Estimador LS para encontrar Amplitud

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)

ingrese la descripción de la imagen aquí

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]

Solución FFT

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)

Resultados en:

ingrese la descripción de la imagen aquí

fft_sol

Solución ideal

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)

Resumen de la solución Después de la recomendación de @ocspro acerca de limitar el paso máximo en la simulación LTSpice a 1n, los resultados son mejores, aunque no 100 % correctos en la identificación de las Rs. Quizás esto se deba a la alta sensibilidad numérica del cos (fase Z) alrededor del pi/2... no estoy seguro de cómo abordar esto (ver la solución xcorr). De todos modos, parece que todos los enfoques tomados arrojan soluciones similares (excepto para Xcorr donde Rs es negativo debido a que la fase Z calculada es ligeramente mayor que pi/2):

Método de diferenciación diff_method_sol

Método Xcorr xcorr_method_sol

Método de mínimos cuadradosls_sqr_sol

Método FFT FFT_method_sol

Solución ideal

Solucion Ideal

Paso 1: ¿Puedes leer el archivo de salida de LTspice en tu código de Python y simplemente volver a trazarlo para verificar que lo estás leyendo correctamente? Paso 2: ¿Puedes estimar la magnitud y la fase de V i y V o ¿correctamente? Solo después de asegurarse de que puede realizar los pasos 1 y 2, debe preocuparse por el paso 3: use V i y V o para estimar las variables del circuito R s , L s , y R 1 .
Además, ¿su código Python asume que la frecuencia de la señal se conoce a priori, o también tiene que estimar la frecuencia a partir de los datos?
¿Cuántos puntos FFT? ¿Ha experimentado con diferentes longitudes de FFT y ha visto alguna tendencia entre la longitud de FFT y la precisión?
@ThePhoton, sí, todas las estimaciones son buenas ... es la precisión lo que parece ser el problema. La frecuencia se conoce de antemano.
Espera, pero ahora que leí con más atención, noté que dijiste "Creo que el problema surge del hecho de que parece que no puedo determinar con precisión las amplitudes de Vo, Vi y el retraso de fase entre ellos a partir de las muestras procesadas. ". Así que no se apresure y pregunte acerca de las fórmulas que calculan los valores de los componentes a partir de las amplitudes y las fases. Pregunte por qué no está estimando las amplitudes y fases con precisión. (Y si quieres respuestas útiles, incluye tu código que estima las amplitudes y fases)
@BrianDrummond, estoy usando las capacidades de FFT en LTPSice; no estoy seguro de cuántos puntos usa. La otra alternativa es exportar las señales de dominio de tiempo "tal cual" y luego hacer una interpolación spline cúbica o algo similar y comparar el resultado que obtengo de ese enfoque con el enfoque en el que confío en la interpolación que LTspice hace en los datos. en el proceso de cálculo de las FFT. Sin embargo, LTSpice afirma que su método tiene una precisión muy alta... Puedo probar el enfoque de interpolación a continuación. Gracias por su respuesta
@The Photon --- eso es lo que estoy preguntando... Y me gustaría compartir todo el código y los datos --- ¿sabe cómo puedo hacerlo?
Hay un botón para dar formato al código como código (es decir, con una fuente monoespaciada, sangría y con un color de fondo diferente) en el editor de preguntas. Si publica un gran bloque de código, creará una ventana desplazable. Pero debe tratar de concentrarse en el código que causa el problema real, porque los voluntarios en su mayoría no leerán páginas y páginas de cosas para llegar al problema real.
Ok... Agregué el código... Creo que es demasiado. Sería mejor si pudiera compartir el Jupyter Notebook, sería más claro. Me sorprende que StackExchange no proporcione una manera fácil de compartir este tipo de cosas. Si pudiera compartir mis datos y mi cuaderno, sería más fácil ayudar a la gente, creo... Gracias por su ayuda hasta ahora.
@BrianDrummond: acabo de volver a leer su comentario y me di cuenta de que podría haber estado preguntando sobre el enfoque de correlación cruzada que usa la FFT y la FFT inversa. Aún así, no sé cuántos puntos usa numpy.fft ... Veré la documentación para ello. Gracias.
"LTSpice no muestrea a intervalos uniformes, por lo que un truco para recuperar datos muestreados de manera uniforme es tomar una FFT de las señales y luego tomar otra FFT de las señales FFT para recuperar los datos del dominio del tiempo" . manera complicada de resolver un problema simple.
@Bruce Abbot no estoy seguro de entender tu comentario. ¿Cuál es la solución simple para resolver los pasos de tiempo no uniformes de un análisis transitorio LTSpice?
Extraiga el tiempo de cruce por cero de cada onda sinusoidal obteniendo los valores positivos y negativos más pequeños y extrapolándolos entre ellos.
Puede obligar a LTSpice a generar muestras a intervalos uniformes, en la pestaña de configuración .TRANS, IIRC es un campo de entrada llamado algo así como 'imprimir cada xxx'
@Neil_UK - gracias - Miré y no puedo averiguar dónde configurarlo para un muestreo uniforme según su sugerencia. Los únicos modificadores son: UIC, constante, no descartar, inicio y paso. Y en la tarjeta de simulación trans real, solo puedo hacer: tiempo de parada, tiempo para comenzar a guardar datos, paso máximo, inicio de CC ext en 0, detener sim cuando se alcanza ss, paso de la fuente de corriente de carga y omitir la solución del punto de operación inicial . Estoy muy interesado en esto, así que si usted puede averiguarlo, por favor hágamelo saber. Gracias.
@jrive Hmmm. Estoy convencido de que solía funcionar, pero ahora no parece funcionar. Vea esta pregunta que respondo, parece que estoy muy seguro allí. LTSpice IV no tiene esta función. LTSpice XVII afirma hacerlo, pero solo se puede configurar en texto, no en la GUI de simulación de edición, y luego parece que no funciona ahora. Recomiendo interpolar el archivo de salida a los pasos de tiempo deseados, eso funcionará. Seguiré experimentando un poco, sería útil.
@jrive Ahora me encontré con esto en mis búsquedas y veo de dónde está obteniendo su proceso FFT, no estaba claro en su OP que fuera interno de LTSpice, o que fuera un truco documentado. Verifique que los datos de tiempo exportados de este truco 2xFFT sean consistentes con los datos de tiempo exportados normalmente, las cosas pueden salir mal con las implementaciones de FFT (el diagrama de dispersión xy de Excel se interpola automáticamente). Además, lea las otras respuestas a la pregunta vinculada en mi comentario anterior, parece que vale la pena echarle un vistazo a ltsputil.
@jrive Solía ​​​​usar una especia diferente, Simetrix, que definitivamente tenía esta función, llamada print_step. Sin embargo, la versión gratuita era crippleware, limitada a 50 nodos, que era suficiente para un modelo de opamp, pero no para dos, por lo que era completamente inescalable, así que cambié a LT. Es probable que nunca intenté usarlo en LTSpice y me confundí entre los dos simuladores. Si lees el LTwiki , dice que el parámetro de paso no sirve. Es una pena en el truco de 2 FFT, ¡no te dará los datos interpolados sin hacer las FFT!
@Bruce Abbot, todavía no entiendo cómo la extrapolación entre los cruces por cero me da un muestreo uniforme...
Con ondas sinusoidales puras en un circuito lineal, todo lo que necesita para determinar la fase son los puntos de cruce por cero. Pero es posible que no tenga muestras exactamente en el punto cero. Así que tome dos muestras consecutivas, una en un lado y la otra en el otro lado del cero, extrapole linealmente entre ellas (casi no hay diferencia con una onda sinusoidal a esta pequeña distancia) y encuentre el momento en que esa línea cruza cero. Esto es mucho más preciso que tratar de medir la fase utilizando los picos de las ondas sinusoidales.

Respuestas (2)

¿Quizás el error de precisión radica en la cantidad de puntos de simulación? Usando la función fft incorporada en LTspice ( .four 1000k 1 -1 V(Vi) V(Vo)), obtuve grandes discrepancias en magnitud y fase al ejecutar su simulación con configuraciones de simulación estándar.

Al disminuir el paso de tiempo máximo, logré obtener una magnitud y un cambio de fase casi idénticos a la solución ideal

Comparación del paso máximo de 1 ns frente a ninguno:

  • Vi = .9999 frente a .9803
  • Vo = .7883 frente a .7722
  • cambio de fase = .6625 frente a .6475

Captura de pantalla del circuito y registro.

Alternativamente, se puede disminuir la tolerancia relativa para forzar pasos de tiempo más cortos. El paso automático es probablemente bastante grande debido al circuito simple y lineal.

gracias. Mucho mejor. Sigo teniendo un error en las Rs identificadas, pero mucho más cerca que antes: Rs_ident =0.204, Ls_ident =.204., usando el método de Mínimos cuadrados:Vi = [0.99996636] Vo = [0.78834297] Phase Dly = [0.6624794] Zmag = [1281,7480386] Zph = [1,5706335] Rs = [0,20870581] Ls = [0,000204]. El método Xcorr no funciona con los nuevos datos, aún no estoy seguro de por qué... el retraso de fase se vuelve loco.

En LTspice, un aspecto comúnmente olvidado con el uso de inductores es su resistencia en serie predeterminada de 1 mΩ.
¡Asegúrese de establecerlo explícitamente en cero para obtener resultados de coincidencia aún mejores!

ingrese la descripción de la imagen aquí

También agregue el bit about Rpar, que por defecto 1e12multiplica el valor de la inductancia. Para inductancias bajas, Rpartambién se nota.
sí... lo había hecho... gracias.