sexta-feira, 8 de fevereiro de 2013

Exportando dados do LTspice e lendo eles em um programa Python + ajuste de curvas

(Obs.: post técnico, mas nada que um mortal não consiga ler)

Esses dias, precisei ler dados gerados no LTspice em um programa Python. Aliás, essa é uma solução relevante para incluir gráficos do LTspice em trabalhos acadêmicos, papers etc... visto que ele não gera gráficos de boa qualidade visual quando impressos.

A solução é bem simples:

No LTspice desenhe o circuito, simule etc... Selecione o gráfico, mande File/Export para gerar um .txt, selecionem os sinais a serem armazenados no arquivo. Como exemplo usei o circuito abaixo e fiz uma análise CC, variando V1 e medindo a tensão antes dos diodos:



Vou chamar esses resultados de diodo.txt. O LTspice gera um arquivo delimitado por tabulações. Agora, para ler em Python, eu poderia usar a biblioteca csv, mas existe uma forma mais pythônica de ler esses dados e plotá-los: usando a função loadtxt da biblioteca NumPy.

Os parâmetros dessa são:
  • fname: nome do arquivo
  • dtype: tipo de dados esperado
  • delimiter: separador
  • skiprows: quantas linhas pular no começo
  • usecols: uma tupla que permite especificar as colunas que queremos pular
A partir daqui ficou óbvio do que precisamos: da fname e da skiprows (a primeira linha é cabeçalho). Assim, teremos o código a seguir:
(se não abrir: https://gist.github.com/renanbirck/4743036)
#!/usr/bin/python2.7
# coding: utf-8
# (pd) 2013 Renan Birck <renan.ee.ufsm@gmail.com>
import numpy as np, matplotlib.pyplot as plt
from scipy import optimize
from time import time
list_of_files = [('diodo.txt','Dois diodos em polaridades opostas')]
datalist = [ ( np.loadtxt(filename,skiprows=1), label ) for filename, label in list_of_files ]
Vin = None
Vout = None
for data, label in datalist:
if not Vin:
Vin = data[:,0]
if not Vout:
Vout = data[:,1]
plt.plot( data[:,0], data[:,1], label=label )
plt.legend()
plt.title(u"Curva do grampeador a diodo")
plt.xlabel(u"Vin")
plt.ylabel(u"Vout")
plt.show()
view raw gistfile1.py hosted with ❤ by GitHub


Ele pode ser usado com o arquivo diodo.txt que forneço de exemplo (aviso: 4 MB. Clique com o botão direito e mande salvar para o seu browser não sentar)

Obteremos isso:


Podemos mexer na legenda, etc etc... mas cheguei ao que eu queria demonstrar.

E agora, já que temos esses dados, podemos ajustar eles a um modelo. Visualmente, nota-se que eles parecem com uma tangente hiperbólica. Assim, façamos um modelo do tipo

e usaremos o método dos mínimos quadrados para ajustá-lo a uma função desse tipo (poderíamos usar qualquer outro método de otimização). O código então tornar-se-á:
(se não abrir: https://gist.github.com/renanbirck/4743133)
#!/usr/bin/python2.7
# coding: utf-8
# (pd) 2013 Renan Birck <renan.ee.ufsm@gmail.com>
import numpy as np, matplotlib.pyplot as plt
from scipy import optimize
from time import time
list_of_files = [('diodo.txt','Dois diodos em polaridades opostas')]
datalist = [ ( np.loadtxt(filename,skiprows=1), label ) for filename, label in list_of_files ]
Vin = None
Vout = None
for data, label in datalist:
if not Vin:
Vin = data[:,0]
if not Vout:
Vout = data[:,1]
plt.plot( data[:,0], data[:,1], label=label )
plt.legend()
plt.title(u"Curva do grampeador a diodo")
plt.xlabel(u"Vin")
plt.ylabel(u"Vout")
plt.show()
begin = time()
print "Entrando no ajuste de curvas..."
# Modelo para ajuste das curvas usando a tangente hiperbólica:
# f(x) = a tanh(bx+c) + d tanh(e*(x-f)+g) + h tanh(i*(x-j)+k)
fitfunc = lambda p, x: p[0]*np.tanh(p[1]*x+p[2]) + p[3]*np.tanh(p[4]*(x-p[5]) +p[6] ) + p[7]*np.tanh(p[8]*(x-p[9])+p[10] ) # Modelo
errfunc = lambda p, x, y: fitfunc(p,x) - y # Função objetivo (modelo - valor real)
p0 = [1,1,1,1,1,1,0,1,1,0,0] # Chutes iniciais
p1, success = optimize.leastsq(errfunc,p0[:],args=(Vin,Vout)) # MMQ
finish = time()
print "ajuste levou %f segundos." % (finish - begin)
plt.cla()
plt.clf()
plt.plot(Vin,Vout,"r-",label="Resposta do circuito")
plt.plot(Vin,fitfunc(p1,Vin),"g-",label="Resposta ajustada")
plt.plot(Vin,Vout-fitfunc(p1,Vin),"b-",label=u"Diferença")
plt.legend(loc=4)
plt.show()
view raw model_fit.py hosted with ❤ by GitHub


Após executarmos, obtemos o gráfico:


Como queríamos demonstrar, obtivemos um bom ajuste. O modelo é bem adequado; para visualizarmos os parâmetros bastaria um print p no final do código.

E nem doeu: foram exemplos adaptados do que eu encontrei na documentação do SciPy. :)

Quanto ao desempenho, a leitura dos dados leva 2 segundos, e o ajuste cerca de 15 segundos num i7-2670QM, mas isso pode (e vai) variar dependendo do chute inicial; para um trabalho mais avançado seria interessante e necessário fazer um meta-ajuste.

Nenhum comentário:

Postar um comentário

Não são lidos e não me responsabilizo pelo conteúdo deles.