Softwares desenvolvidos¶
Interface Gráfica¶
A interface gráfica foi feita em linguagem python. Para rodar o programa corretamente é necessário ter o Python 2.7 com os seguintes pacotes instalados:
- Matplotlib - pacote padrão para plotar gráficos.
- PySerial - pacote utilizado para comunicação serial entre python e arduino.
- numpy - pacote fundamental para computação científica com python.
- Tkinter - pacote para gerenciar a interface gráfica, um intepretador de TK.
Sistemas linux, como Ubuntu e Debian, costumam vir com python2.7 instalado como padrão. Caso o aluno não tenha python em seu computador basta instalá-lo através do gerenciador de pacotes de seu sistema, em ubuntu, por exemplo, digite sudo apt-get install python2.7.
Em windows aconselhamos instalar a distribuição *Python(x,y)*, pois nela já se encontram todos os pacotes necessários. Ao instalar o python(x,y) verifique se as bibliotecas necessárias estão selecionadas para instalação.
# encoding: utf-8
"""
@author Emilio Galera, Heitor de Bittencourt
@date Dezembro, 2016
Interface gráfica para o sistema EPR do LEF.
"""
from Tkinter import *
from tkFileDialog import askopenfilename, asksaveasfilename
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
import matplotlib.ticker as mtick
import serial
import serial.tools.list_ports
import time
import numpy as np
import struct
import random
global count_max
count_max = 500000
global stop_flag
stop_flag = False
# tempo entre aquisições (em milisegundos)
global delay
delay = 1
global number_of_points
number_of_points = 1
global mean
mean = 1
global x_axis
x_axis = []
global y_axis
y_axis = []
global b_axis
b_axis = []
# descobre qual a porta que o arduino esta conectado
def get_arduino_port():
ports = list(serial.tools.list_ports.comports())
for p in ports:
if "Arduino" in p[1]:
print("Arduino in", p[0])
return p[0]
print("Did not find arduino. Using \"/dev/ttyUSB0\" as fallback.")
return "/dev/ttyUSB0"
#essa função se comunica com o arduino e obtem dados do campo B, rampa
#do registrador (tensão de referencia de 0 a 1 volt) e sinal do lock-in
def read_data():
global count_max
sem_dados = True
count = 0
x = 0.
y = 0.
b = 0.
connection.write("B")
#print("B")
ehlo1 = connection.read(1)
datalen = connection.read(1)
data = connection.read(int(datalen))
ehlo2 = connection.read(1)
#print(ehlo1, datalen, data, ehlo2)
if ehlo1 == b'x' and ehlo2 == b'X':
dados_x = float(data)
else:
dados_x = None
ehlo1 = connection.read(1)
datalen = connection.read(2)
data = connection.read(int(datalen))
ehlo2 = connection.read(1)
#print(ehlo1, datalen, data, ehlo2)
if ehlo1 == b'y' and ehlo2 == b'Y':
dados_y = float(data)
else:
dados_y = None
ehlo1 = connection.read(1)
datalen = connection.read(1)
data = connection.read(int(datalen))
ehlo2 = connection.read(1)
#print(ehlo1, datalen, data, ehlo2)
if ehlo1 == b'b' and ehlo2 == b'B':
dados_b = float(data)
else:
dados_b = None
#print dados_x, dados_y
#print type(dados_x), type(dados_y)
return dados_x, dados_y, dados_b
# essa função coleta dados e plota os mesmos de maneira recursiva
def plot_received_data(collected_points):
# se for o primeiro ponto, a função espera até que a rampa inicie
# para iniciar a coleta.
if collected_points == 0:
from_AD_x, from_AD_y, from_AD_b = read_data()
while True:
aux, aux2, aux3 = read_data()
if abs(from_AD_x - aux) > 0.0002:
break
global stop_flag
if not stop_flag:
from_AD_x, from_AD_y, from_AD_b = read_data()
while from_AD_x == None or from_AD_y == None:
from_AD_x, from_AD_y, from_AD_b = read_data()
global x_axis, y_axis, b_axis
try:
# corta os ultimos pontos para não plotar a volta abrupta de
# tensão da rampa.
if abs(from_AD_x - x_axis[len(x_axis) - 1]) < .1:
x_axis.append(from_AD_x)
y_axis.append(from_AD_y)
b_axis.append(from_AD_b)
#graph.scatter(from_AD_x, from_AD_y, color="red")
else:
stop_flag = True
#graph.set_xlim(min(x_axis) * .99, max(x_axis) * 1.01)
#canvas.draw()
except IndexError:
if collected_points == 0:
x_axis.append(from_AD_x)
y_axis.append(from_AD_y)
b_axis.append(from_AD_b)
else:
pass
window.after(delay, plot_received_data, collected_points + 1)
else:
bt_on.config(state="normal")
bt_off.config(state="disabled")
print len(x_axis), len(b_axis)
(b, b0), cov = np.polyfit(x_axis, b_axis, 1, cov = True)
#print b, b0
B_axis = []
for x_iten in x_axis:
B_axis.append(10000. * ((x_iten * b) + b0))
#print B_axis
try:
graph.lines[0].remove()
except:
pass
try:
graph.clear()
except:
pass
canvas.draw()
graph.grid()
graph.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.2e'))
graph.xaxis.set_major_formatter(mtick.FormatStrFormatter('%.2e'))
graph.set_ylabel("Sinal (Volts)", size=18)
graph.set_xlabel("B (Gauss)", size=18)
graph.autoscale(True, "y", False)
graph.plot(B_axis, y_axis, color="red", linestyle="solid", linewidth="2.5")
graph.set_xlim(min(B_axis) * .99, max(B_axis) * 1.01)
canvas.draw()
b_axis = B_axis
print "Fim da coleta"
# essa função inicia a leitura
#inicia os parâmetros de tempo e varredura antes da leitura
def start_reading():
global mean
graph.set_xlim(0, number_of_points)
canvas.draw()
tp = tempo.get()
cp = campo.get()
#print type(tp), tp
#print type(cp), cp
connection.write("T")
connection.write(str(tp))
time.sleep(0.1)
echo1_tp = connection.read(1)
echo2_tp = connection.read(len(str(tp)))
echo3_tp = connection.read(1)
if echo1_tp == b't' and echo3_tp == b'T':
print str(echo2_tp)
#time.sleep(0.01)
connection.write("D")
connection.write(str(cp))
time.sleep(0.1)
echo1_cp = connection.read(1)
echo2_cp = connection.read(len(str(cp)))
echo3_cp = connection.read(1)
if echo1_cp == b'd' and echo3_cp == b'D':
print str(echo2_cp)
#time.sleep(0.01)
#time.sleep(0.01)
mean = '5'
#print mean
connection.write("A")
time.sleep(0.01)
connection.write(mean)
time.sleep(0.1)
try:
graph.lines[0].remove()
except IndexError:
pass
global x_axis, y_axis, b_axis
x_axis = []
y_axis = []
b_axis = []
global stop_flag
stop_flag = False
bt_on.config(state="disabled")
bt_off.config(state="normal")
connection.write("I")
graph.set_xlabel(u"Tensäo Rampa (Volts)", size=18)
graph.autoscale(True, "y", False)
plot_received_data(0)
#força uma parada de leitura, é chamada pelo botão
def stop_reading():
global stop_flag
stop_flag = True
connection.write("P");
#plota o gráfico de um arquivo txt
def plot_file():
cores = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'g'] #no more white
# recebe o nome do arquivo a ser lido
file_name = askopenfilename()
# Le o arquivo em uma linha e retorna uma matriz com os dados
data = np.loadtxt(file_name)
# separa a matriz de dados em vetores para X e Y
x_axis = data[:, 0]
y_axis = data[:, 1]
# grafica
graph.plot(x_axis, y_axis, "k-", lw="1.5", color = cores[random.randint(0, len(cores)-1)])
graph.autoscale()
# desenha
canvas.draw()
#escreve os dados do ultimo gráfico plotado em um arguivo txt
def write_data():
global mean
global x_axis, y_axis
# recebe o nome do arquivo a ser salvo
file_name = asksaveasfilename()
print file_name + "!!!!!"
header = "Arquivo: " + file_name + "\n"
header = header + "Numero de pontos: " + str(len(x_axis)) + "\n"
header = header + "Numero de medias: " + str(mean)
print header
# salva os dados
np.savetxt(file_name, np.transpose(
[b_axis, y_axis]), delimiter='\t', header=header, comments='# ')
#função chamada quando o botão de limpar a tela for ativado
def clear_plot():
global x_axis, y_axis
x_axis = []
y_axis = []
# remove o gráfico feito
graph.lines[0].remove()
# reseta os limites do gráfico
graph.set_xlim(0, 1)
graph.set_ylim(-1, 1)
# redesenha
canvas.draw()
# função chamada ao fechar o programa
def on_closing():
print("Adios")
# manda o comando C pro arduino resetar
connection.write("C")
connection.close()
window.destroy()
if __name__ == '__main__':
connection = serial.Serial(get_arduino_port(), 115200, timeout = 2)
time.sleep(1)
print("Foi")
window = Tk()
window.minsize(width=900,height=600)
window.title("Trambolhino - EPR - LEF")
window.state("normal")
#### divide a janela em áreas ###
#### TÍTULO ####
title_area = Frame(window)
title_area.pack(side="top", fill="y")
#### GRÁFICO ####
graph_area = Frame(window)
graph_area.pack(side="top", fill="both", expand=True)
#### INTERAÇÃO COM USUÁRIO ####
user_area = Frame(window)
user_area.pack(side="bottom", fill="x")
### ###
# TÍTULO
title = Label(title_area, text="Trambolhino - Emilio Galera & Heitor de Bittencourt, 2016",
font="arial 14 bold")
title.pack(side="top", fill="x", expand=True)
# GRÁFICO
fig = Figure()
canvas = FigureCanvasTkAgg(fig, graph_area)
canvas.draw()
canvas.get_tk_widget().pack(side="bottom", fill="both", expand=True)
graph = fig.add_subplot(1, 1, 1)
graph.grid()
graph.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.2e'))
graph.xaxis.set_major_formatter(mtick.FormatStrFormatter('%.2e'))
graph.set_ylabel("Sinal (Volts)", size=18)
graph.set_xlabel("B (Gauss)", size=18)
graph.autoscale(True, "y", False)
toolbar = NavigationToolbar2TkAgg(canvas, graph_area)
toolbar.update()
toolbar.pack(side="left")
# USUÁRIO
# Botões para iniciar e para a leitura, salvar um gráfico em txt
#(apenas as coordenadas x e y), ler um arquivo txt e plotar o
#gáfico e limpar a tela.
user_buttons = Frame(user_area)
user_buttons.pack(side="right", fill="y", expand=True)
# grid 2x3 (duas colunas e três linhas)
user_buttons.columnconfigure(0, weight=1)
user_buttons.columnconfigure(1, weight=1)
for i in range(0, 3):
user_buttons.rowconfigure(i, weight=1)
bt_on = Button(user_buttons, text="Ler conversor",
font="Arial 12 bold", width=10, command=start_reading)
bt_on.grid(row=0, column=0, pady=3)
bt_off = Button(user_buttons, text="Parar leitura", font="Arial 12 bold",
width=10, state="disabled", command=stop_reading)
bt_off.grid(row=1, column=0, pady=3)
bt_write = Button(user_buttons, text="Salvar",
font="Arial 12 bold", width=10, command=write_data)
bt_write.grid(row=0, column=1, pady=3)
bt_read = Button(user_buttons, text="Ler",
font="Arial 12 bold", width=10, command=plot_file)
bt_read.grid(row=1, column=1, pady=3)
bt_clear = Button(user_buttons, text="Limpar",
font="Arial 12 bold", width=10, command=clear_plot)
bt_clear.grid(row=2, column=1, pady=3)
#Radio buttons, para tempo e escala de varredura
radio_buttons = Frame(user_area)
radio_buttons.pack(side="left", fill="y", expand=True)
radio_buttons.columnconfigure(0, weight=1)
radio_buttons.columnconfigure(1, weight=1)
for i in range(0, 5):
radio_buttons.rowconfigure(i, weight=1)
# Radio button para selecionar o tempo de varredura
tempo = IntVar()
titulo_tempo = Label(radio_buttons, text="Tempo", font="Arial 12 bold",width=10)
titulo_tempo.grid(row = 0, column = 0)
tempo30RB = Radiobutton(radio_buttons, text = "30 segundos", variable = tempo, value = '0')
tempo30RB.grid(row = 1, column = 0, sticky = 'W')
tempo60RB = Radiobutton(radio_buttons, text = "60 segundos", variable = tempo, value = '1')
tempo60RB.grid(row = 2, column = 0, sticky = 'W')
tempo3mRB = Radiobutton(radio_buttons, text = "3 minutos", variable = tempo, value = '2')
tempo3mRB.grid(row = 3, column = 0, sticky = 'W')
tempo5mRB = Radiobutton(radio_buttons, text = "5 minutos", variable = tempo, value = '3')
tempo5mRB.grid(row = 4, column = 0, sticky = 'W')
#Radio button para selecionar o delta B
campo = IntVar()
titulo_campo = Label(radio_buttons, text="Campo", font="Arial 12 bold",width=10)
titulo_campo.grid(row = 0, column = 1)
campo50G = Radiobutton(radio_buttons, text = "50 Gauss", variable = campo, value = '0')
campo50G.grid(row = 1, column = 1, sticky = 'W')
campo100G = Radiobutton(radio_buttons, text = "100 Gauss", variable = campo, value = '1')
campo100G.grid(row = 2, column = 1, sticky = 'W')
campo500G = Radiobutton(radio_buttons, text = "500 Gauss", variable = campo, value = '2')
campo500G.grid(row = 3, column = 1, sticky = 'W')
campo1000G = Radiobutton(radio_buttons, text = "1000 Gauss", variable = campo, value = '3')
campo1000G.grid(row = 4, column = 1, sticky = 'W')
window.protocol("WM_DELETE_WINDOW", on_closing)
window.mainloop()
Observações importantes¶
O programa EPR-LEF.py foi testado nos computadores dos alunos, e no computador disponível no laboratório.
Os resultados obtidos nos computadores dos alunos foram satisfatórios, foram coletados pontos suficiente para observar todos os sinais necessários. O computador do laboratório não obteve pontos o suficientes para apresentar resultados aceitáveis: estava lento demais mesmo antes do programa ser rodado. Levantamos a hipótese de que o sistema operacional não era adequado, o computador estava usando Windows XP, formatamos a máquina e colocamos nela o sistema lubuntu, um sistema específico para máquinas com restrição de recursos. Nesse sistema o programa EPR-LEF.py obteve resultados melhores porém, ainda assim, não foi o suficiente para observar sinais claros e limpos.
Nas três máquinas testadas, os resultados foram substancialmente melhorados quando retiramos a função de desenhar o gráfico enquanto os dados são obtidos.
Instabilidades no gerador de rampa também dificultaram as medidas, o mesmo apresenta perda de linearidade no final da varredura a qual não conseguimos solucionar. A imagem Fig. 36 a seguir mostra este problema. Ligamos um osciloscópio digital nas saídas de tensão para a fonte do eletroímã (mostrado na curva amarela) e também na saída para registrador gráfico (em azul).
A curva azul deveria ser linear de zero a cerca de um volt, com duração de 300 segundos, mas vemos que rampa está muito instável: após cerca de 100 segundos, a tensão sobe abruptamente para o valor máximo, tendo duração de cerca de 140 segundos.
Fig. 36 Saídas do controlador de varredura vistas no osciloscópio. Em amarelo, vemos o sinal enviado para a fonte do magneto, em azul está a rampa a ser usada em registrador gráfico. Esse sistema apresenta um problema de instabilidade muito acentuado: a rampa (azul) era para ser linear, de zero a um volt, com duração de 300 segundos, mas após cerca de 100 segundos rampa atinge abruptamente o valor máximo.
Recomendamos fortemente aos alunos que irão realizar esta prática que utilizem seus próprios computadores para coleta de dados com o programa EPR-LEF.py
Firmware para Arduino¶
Para obter os dados de tensão da rampa e sinal do lock-in utilizamos um microcontrolador Arduino.
Para programá-lo utilizamos linguagem C++. O arduino da caixa já está carregado com o programa de aquisição de dados mas, caso seja necessário carregá-lo novamente são necessárias algumas bibliotecas adicionais para que o programa compile corretamente.
- Nanoshield_ADC.h - biblioteca para controlar o módulo ADC (convesor analógico digital).
- SoftReset.h - biblioteca utilizada para resetar o arduino através de software.
- SoftwareSerial.h - biblioteca utilizada para se comunicar com o software, python nesse caso.
Todas as bibliotecas se emcontram no projeto EPR-LEF na pasta Firmware/libs.
/**
* @author Emilio Galera, Heitor de Bittencourt
* @date Dezembro, 2016
*
* Firmware para Arduino fazer a aquisicao de dados do equipamento de EPR
* do LEF.
*/
#include <Arduino.h>
#include <Wire.h>
#include <stdlib.h>
// external libraries
#include <Nanoshield_ADC.h>
#include <SoftReset.h>
#include <SoftwareSerial.h>
//#include <WString.h>
// canais do ADC para leitura do valor x e B
const int channel_x = 1;
const int channel_b = 2;
const int pinLed13 = 13;
// pinos que acionam start/stop do controle de varredura do campo
const int start_r = 2;
const int stop_r = 3;
// Pinos digitais para comunicacao com chave digital de selecao de tempo de
// varredura e variacao de campo magnetico
const int temp_A = 4;
const int temp_B = 5;
const int campo_A = 6;
const int campo_B = 7;
Nanoshield_ADC adc;
SoftwareSerial lockin(10, 11); // RX, TX
void setup()
{
adc.begin();
Serial.begin(115200);
lockin.begin(9600);
delay(2000);
while (!Serial);
pinMode(start_r, OUTPUT);
pinMode(stop_r,OUTPUT);
pinMode(temp_A, OUTPUT);
pinMode(temp_B, OUTPUT);
pinMode(campo_A, OUTPUT);
pinMode(campo_B, OUTPUT);
delay(1000);
digitalWrite(start_r, LOW);
digitalWrite(stop_r, HIGH);
delay(7);
digitalWrite(stop_r, LOW);
digitalWrite(temp_A, LOW);
digitalWrite(temp_B, LOW);
digitalWrite(campo_A, LOW);
digitalWrite(campo_A, LOW);
pinMode(pinLed13, OUTPUT);
// o lock-in envia um cabeçalho ao iniciar
// o while abaixo le o mesmo e o descarta
analogReference(DEFAULT);
while (lockin.available() > 0)
Serial.print(lockin.read());
lockin.print("W0\r");
//ganho utilizado para medir as voltagens com o ADC
adc.setGain(GAIN_EIGHT);
}
void loop()
{
int opcao = 100;
int media = 1;
int i;
double x;
double y;
double b;
char result[20];
int merda;
int tempo_aux = 0;
int campo_aux = 0;
char aux_y[11];
int j;
if (Serial.available() > 0) {
opcao = Serial.read();
switch(opcao) {
case 'A':
/*while(Serial.peek() < 0);
media = Serial.parseInt();*/
ret1:
if (Serial.available() > 0)
media = Serial.parseInt();
else
goto ret1;
opcao = 100;
//Serial.println(media);
break;
case 'B':
digitalWrite(pinLed13, HIGH);
x = 0;
y = 0;
b = 0;
for(i = 0; i < media; i++){
x += 2*adc.readVoltage(channel_x);
b += adc.readVoltage(channel_b);
lockin.print("q\r");
j = 0;
while(lockin.available() > 0 && j < 20){
aux_y[j]= lockin.read();
j++;
}
y += atof(aux_y);
}
x /= (double) media;
y /= (double) media;
b /= (double) media;
dtostrf(x, 3, 6, result);
Serial.write('x');
Serial.print(strlen(result));
Serial.print(result);
Serial.write('X');
dtostrf(y, 4, 10, result);
Serial.write('y');
Serial.print(strlen(result));
Serial.print(result);
Serial.write('Y');
dtostrf(b, 3, 6, result);
Serial.write('b');
Serial.print(strlen(result));
Serial.print(result);
Serial.write('B');
digitalWrite(pinLed13, LOW);
opcao = 100;
break;
case 'C':
opcao = 100;
soft_restart();
break;
// reset lock-in
case 'Z':
opcao = 100;
lockin.print("z\r");
break;
// inicia rampa
case 'I':
opcao = 100;
digitalWrite(start_r, HIGH);
delay(10);
digitalWrite(start_r, LOW);
break;
// interrompe rampa
case 'P':
opcao = 100;
digitalWrite(stop_r, HIGH);
delay(10);
digitalWrite(stop_r, LOW);
break;
// selecao de Tempo
// 0 - 0.5 min
// 1 - 1 min
// 2 - 3 min
// 3 - 5 min
case 'T':
opcao = 100;
while(Serial.peek() < 0);
tempo_aux = Serial.read();
Serial.write('t');
Serial.write(tempo_aux);
Serial.write('T');
switch(tempo_aux) {
case '1':
digitalWrite(temp_B, LOW);
digitalWrite(temp_A, HIGH);
media = 50;
break;
case '2':
digitalWrite(temp_B, HIGH);
digitalWrite(temp_A, LOW);
media = 150;
break;
case '3':
digitalWrite(temp_B, HIGH);
digitalWrite(temp_A, HIGH);
media = 250;
break;
case '0':
default:
digitalWrite(temp_B, LOW);
digitalWrite(temp_A, LOW);
media = 25;
break;
}
break;
// Selecao de delta B
// 0 - 50 gauss
// 1 - 100 gauss
// 2 - 500 gauss
// 3 - 1000 gauss
case 'D':
opcao = 100;
while(Serial.peek() < 0);
campo_aux = Serial.read();
Serial.write('d');
Serial.write(campo_aux);
Serial.write('D');
switch(campo_aux) {
case '1':
digitalWrite(campo_B, LOW);
digitalWrite(campo_A, HIGH);
break;
case '2':
digitalWrite(campo_B, HIGH);
digitalWrite(campo_A, LOW);
;
case '3':
digitalWrite(campo_B, HIGH);
digitalWrite(campo_A, HIGH);
break;
case '0':
default:
digitalWrite(campo_B, LOW);
digitalWrite(campo_A, LOW);
break;
}
break;
default:
opcao = 100;
}
}
}