В зависимости от объема методики поверки (калибровки) у современных мультиметров бывает более 200 поверяемых точек.
При наличии подходящего оборудования, с интерфейсами RS-232 или USB, возможно написать программу для автоматизации процесса калибровки, тем самым упростив эту процедуру.
Рис.1
Рис.2
Для связи с приборами, поддерживающими стандарт VISA, мы будем использовать библиотеку PyVISA, позволяющую
нам общаться с приборами используя различные интерфейсы, такие как GPIB, RS-232, USB и Ethernet.
Команда для установки:
pip install pyvisa
Для составления протокола в excel необходима библиотека openpyxl.
Команда для установки:
pip install openpyxl
Команда для установки PIL:
pip install pillow
Также необходимо установить библиотеку VISA отдельно от PyVISA. Вы можете скачать эту библиотеку
от National Instruments или от Keysight (IO Libraries).
В процессе идентификации, если прибор подключен по USB, Вы увидите следующий VISA
адрес (например в Connect Expert от Keysight): USB0::0x0957::0x0A07::MY53002107::0::INSTR. В этой строке заложена информация о приборе:
0x0957 – производитель флешь памяти, в данном случае Agilent Technologies
(в новых моделях уже значится 0x2A8D, что означает Keysight Technologies)
0x0A07 – тип прибора, в данном случает мультиметр 34401A (0x1301, 0x1401 – мультиметр 34461A,
0x1F01 – генератор N5183A, 0x5707 – генератор 33622A,0x5418 – измерители мощности N1913A и другие)
MY53002107 – заводской номер
Для удобства мы сделали пользовательский интерфейс средствами стандартной библиотеки Tkinter:
Рис.3
В первых двух полях нужно указать определившиеся приборы: мультиметр и калибратор. После нажатия Connect
в полях ID выведутся идентификационные номера приборов, состоящие из типа, заводского номера и версии прошивки.
Для того чтобы программа определяла только типы приборов три раза разбиваем VISA адрес на составляющие
(начало, разделитель, конец) с помощью метода partition:
# -*- coding: utf-8 -*-
import visa#подключаем библиотеку visa
rm = visa.ResourceManager()
rg1 = rm.list_resources()#запрос кортежа подключенных портов
rg2 = list(rg1)#преобразование кортежа в список
def pribor():#функция запускается при старте GUI
v=len(rg1)#кол-во элементов кортежа
i=-1
while i < v-1:#цикл разбиения и формирования нового списка
i=i+1#начинаем с 0-ого эл-та (i=0; rg[0])
b1=rg1[i]#i-й эл-т кортежа
b1, b2, b3 = b1.partition('::')#разбиение VISA адреса на части
b4, b5, b6 = b3.partition('::')
b7, b8, b9 = b6.partition('::')
b10, b11, b12 = b9.partition('::')
if b1 == 'ASRL1':
rg2[i] = 'COM1'#новые эл-т списка
if b1 == 'ASRL2':
rg2[i] = 'COM2'
if b7 in ('0x1301', '0x1401'):
rg2[i] = '34461A'
combo1.configure(values=rg2)#запись новых эл-тов списка в 1-й combobox
combo2.configure(values=rg2)
В цикл можно добавлять сколько угодно условий для определения типов приборов, в зависимости от того какой у Вас
подключен мультиметр, а также сколько угодно COM портов (в данном примере у нас COM1, к которому подключен калибратор Fluke 5500E (5522A) и COM2 к которому подключается мультиметр 34401А не имеющий USB).
Далее создадим класс для идентификации приборов:
class id:
def __init__(self, type, adr, port, b):
self.type = type
self.adr = adr
self.port = port
self.b = b
def connect_d(self):#функция соединение мультиметров
global a4
global a7
global inst_1
v=len(rg1)
i=-1
if combo1.get() == self.type:#условие проверки мультиметра
while i < v-1:
i=i+1
if re.search(self.adr, rg1[i]):#поиск адресов в списке
inst_1 = rm.open_resource(rg1[i])
if self.port == 'com':#если подключение по COM порту
inst_1.write("SYST:REM")
time.sleep(1)
data_1 = inst_1.query("*IDN?")#запрос *IDN? мультиметра
a.set(data_1)
a1 = data_1
a1, a2, a3 = a1.partition(',')
a4, a5, a6 = a3.partition(',')
a7, a8, a9 = a6.partition(',')
f.set(a4 + ',' + a7 +',' + d + '.xlsx')
if a4 in ('34401A', '34410A', '34411A', '34460A', '34461A', '34465A', '34470A'):
a10.set('Мультиметр ' + a4 + ' ' + 'подключен')
lb.insert(END, a10.get())
lb.see(END)
def connect_f(self):#функция соединение калибратора
global b4
global inst_2
v=len(rg1)
i=-1
if combo2.get() == self.type:#условие проверки калибратора
while i < v-1:
i=i+1
if re.search(self.adr, rg1[i]):
inst_2 = rm.open_resource(rg1[i], baud_rate = 9600, data_bits = 8, write_termination= '\r', read_termination= '\r')
data_2 = inst_2.query("*IDN?")#запрос *IDN? калибратора
b.set(data_2)
b1=data_2
b1, b2, b3 = b1.partition(',')
b4, b5, b6 = b3.partition(',')
b7, b8, b9 = b6.partition(',')
b14.set('Калибратор ' + b1 + ' ' + b4 + ' ' + 'подключен')
lb.insert(END, b14.get())
lb.see(END)
def connect_dmm():#функция после нажатия кнопки Connect DMM
USB1 = id('34411A', r'\b0x0A07\b', 'usb')
USB2 = id('34461A', r'\b0x1401\b', 'usb')
COM1 = id('COM1', r'\bASRL1\b', 'com')
COM2 = id('COM2', r'\bASRL2\b', 'com')
COM3 = id('COM3', r'\bASRL3\b', 'com')
USB1.connect_d()
USB2.connect_d()
COM1.connect_d()
COM2.connect_d()
COM3.connect_d()
def connect_fluke():#функция после нажатия кнопки Connect Fluke
COM1 = id('COM1', r'\bASRL1\b', 'com')
COM2 = id('COM2', r'\bASRL2\b', 'com')
COM3 = id('COM3', r'\bASRL3\b', 'com')
COM1.connect_f()
COM2.connect_f()
COM3.connect_f()
Для создания класса калибровки используем потоки:
class call(Thread):
def __init__(self, name, d1, volt1, volt2, cell1, cell2, band, time, accurancy):
Thread.__init__(self)
self.name = name
self.d1 = d1
self.volt1 = volt1
self.volt2 = volt2
self.cell1 = cell1
self.cell2 = cell2
self.band = band
self.time = time
self.accurancy = accurancy
self.start()
def run(self):
sem.acquire()#семафор для поочередного выполнения потоков
inst_1.write(self.volt2)#тип измерения мультиметра (DC, AC …)
inst_1.write(self.band)#полоса измерений мультиметра
time.sleep(1)
inst_2.write('*CLS')#очищение калибратора
inst_2.write(self.volt1)#установка значения на калибраторе
inst_2.write('OPER')#включение выхода калибратора
time.sleep(5)
inst_1.write('READ?')#запрос на чтение показания мультиметра
time_1 = float(self.time)
time.sleep(time_1)
data_3 = inst_1.read()#чтение показания мультиметра
data_4 = float(self.d1)
data_5 = float(data_3)
if self.name == 'cap':#перевод показаний емкости в мкФ для протокола
data_c3 = (data_5 - data_c2)
data_c4 = data_c3*10E+8
data_6 = ((data_c3-data_4)/data_4)*100
ws[self.cell1] = data_c4
elif self.name == 'res2':#перевод показаний сопротивления в МОм для протокола
data_r = data_5/10E+5
data_6 = ((data_5-data_4)/data_4)*100
ws[self.cell1] = data_r
elif self.name in ('dc', 'ac', 'dci', 'aci', 'fr', 'res4'):
data_6 = ((data_5-data_4)/data_4)*100
ws[self.cell1] = data_5
ws[self.cell2] = data_6
ws['G7'] = a4
ws['B10'] = a7
ws['F16'] = h.get()
ws['F17'] = k.get()
ws['F18'] = l.get()
ws['B11'] = m.get()
data_7 = float(self.accurancy)
colour = PatternFill(start_color='FFFFDAB9', end_color='FFFFDAB9', fill_type='solid')
if data_6 > data_7:#подсветка погрешностей в протоколе
ws[self.cell2].fill = colour
elif data_6 < -data_7:
ws[self.cell2].fill = colour
if a4 == '34401A':#сохранение значений в протокол
wb.save(protokol+'\\34401A\\'+f.get())
ws['E97'] = n.get()
ws['C99'] = e
elif a4 == '34410A':
wb.save(protokol+'\\34410A\\'+f.get())
ws['E289'] = n.get()
ws['C291'] = e
elif a4 == '34411A':
wb.save(protokol+'\\34411A\\'+f.get())
ws['E289'] = n.get()
ws['C291'] = e
elif a4 == '34460A':
wb.save(protokol+'\\34460A\\'+f.get())
ws['E121'] = n.get()
ws['C123'] = e
elif a4 == '34461A':
wb.save(protokol+'\\34461A\\'+f.get())
ws['E124'] = n.get()
ws['C126'] = e
elif a4 == '34465A':
wb.save(protokol+'\\34465A\\'+f.get())
ws['E337'] = n.get()
ws['C339'] = e
elif a4 == '34470A':
wb.save(protokol+'\\34470A\\'+f.get())
ws['E337'] = n.get()
ws['C339'] = e
inst_2.write('STBY')#выключение выхода калибратора
time.sleep(1)
progress1.step(1)
sem.release()
class reset(Thread):#сброс при переходе на следующий вид измерения
def __init__(self):
Thread.__init__(self)
self.start()
def run(self):
sem.acquire()
time.sleep(2)
inst_2.write('*RST')
inst_1.write('*RST')
time.sleep(2)
progress1.step(1)
sem.release()
class message(Thread):#сообщения о переключениях проводов
def __init__(self, text):
Thread.__init__(self)
self.text = text
self.start()
def run(self):
sem.acquire()
start_thread(q.put(( tkMessageBox.showinfo, ('ВНИМАНИЕ!', self.text), {} )))
progress1.step(1)
sem.release()
class cap(Thread):#компенсация проводов при измерении емкости
def __init__(self):
Thread.__init__(self)
self.start()
def run(self):
sem.acquire()
global data_c2
inst_1.write('CONF:CAP')
time.sleep(5)
inst_1.write('READ?')
time.sleep(5)
data_c1 = inst_1.read()
data_c2 = float(data_c1)
time.sleep(1)
progress1.step(1)
sem.release()
Функция после нажатия кнопки старт:
def start():
global ws
global wb
thread = message('Соедините провода для измерения постоянного напряжения')
thread = reset()
if a4 in ('34410A', '34411A'):
wb = load_workbook(shablon+'\\34410A,34411A.xlsx')#выбор шаблона протокола
ws = wb.active
progress1.configure(maximum = 221)
#0.1V
thread = call('dc', '0.005', 'OUT 0.005 V', 'CONF:VOLT:DC 0.1', 'C28', 'D28', 'DET:BAND 20', '3', '0.075')
thread = call('dc', '0.05', 'OUT 0.05 V', 'CONF:VOLT:DC 0.1', 'C29', 'D29', 'DET:BAND 20', '3', '0.012')
thread = call('dc', '0.095', 'OUT 0.095 V', 'CONF:VOLT:DC 0.1', 'C30', 'D30', 'DET:BAND 20', '3', '0.009')
Полный код программы, инструкция по установке и шаблоны протоколов приведены на GitHub.
На данный момент калибруются 7 типов мультиметров: Keysight (Agilent) 34401A, 34410A, 34411A, 34460A, 34461A, 34465A, 34470A.
GitHub: https://github.com/itllab/DMM
GitHub: https://github.com/itllab/DMM
Комментарии
Отправить комментарий