Сергей! Вы как всегда идеально лаконичны. Респект.
OldBean
Доцент
Красноярск
1K 1.4K
Отв.1481 23 Окт. 18, 05:43
17.5.3. Исполнительные устройства. Классы PM, PWM и HTR
Нам осталось рассмотреть еще один тип модулей варианта LITE. Это силовые модули, на которых реализуются исполнительные устройства. Для тестирования желательно подключить к контроллеру ТЭНа нагрузку в виде небольшой лампочки 70-100 Вт. У лампочки инерция небольшая, поэтому уровень PDM-модуляции легко будет наблюдать визуально. К контроллеру клапана отбора можно подключить клапан или просто реле на 220 В. Чтобы щелкало. В крайнем случае можно ограничиться наблюдением за светодиодиками на платах контроллеров. Они именно для этого туда и поставлены ;)
Базовый класс всех исполнительных устройств - это класс PM. В этом классе реализуется функционал, характерный для всех исполнительных устройств. Это - проверка наличия и работоспособности устройства и отправка физическому устройству кода (слово). Для управления его состоянием.
Конструктор класса PM имеет два параметра, которые нужно задавать при создании объекта. Это: addr - адрес устройства на шине i2c и vlen - длина слова (кода) в байтах, которое отправляется малинкой устройству, когда свойству v объекта этого класса в скрипте присваивается какое-нибудь значение. Т.е. - в сеттере. Например, выполнение инструкции:
имя_устройства.v = 128
приведет к отправке числа 128 данному устройству. Значение параметра vlen по умолчанию - 1. Т.е. 1 байт.
Класс PM имеет два удобных метода (функции) для включения устройства - on() и выключения - off(). При вызове этих функций, устройству отправляется код с максимальным значением (0xff - для байтовых, 0xffff - для двухбайтовых и т.д.) и нулевой, соответственно.
Класс PM можно использовать для работы с релейными устройствами (например, контактор) или для низкоуровневой работы с более сложными исполнительными устройствами. В частности - при работе с ними на уровне кода (процентов от максимума), а не на уровне конкретных физических величин (мощности или расхода). Скрипт с примерами создания объектов класса PM и работы с ними приведен ниже (файл test_PM.py в архиве в приложении к данному топику):
Скрытый текст
#coding:utf-8 import time import sys from nna import *
"""Демонстрация использования классов-оберток, описанных в модуле nna Тестирование базового класса исполнительного устройства - PM :author: OldBean 22.10.2018"""
pm1 = PM(0x12) # Релейное исполнительное устройство (контактор) pm2 = PM(0x13) # 1-байтовое исполнительное устройство (ТЭН) pm3 = PM(0x14, vlen = 2) # 2-байтовое исполнительное устройство (клапан)
pm1.on() # "Включаем" первый силовой модуль (в реальности - это контактор - 0x12) time.sleep(1)
# Сделаем "треугольник" на втором (байтовом) силовом модуле for c in range(0, 101, 2): # Линейный рост байтового кода, передаваемого устройству pm2.v = c print c time.sleep(0.1) for c in range(98, -2, -2): # Линейный снижение байтового кода pm2.v = c print c time.sleep(0.1) time.sleep(2)
"""У наших контроллеров с ШИМ-регулированием период довольно велик (10 сек), поэтому пилу делать не будем - просто пару значений кода: 1000 (максимальное значение) и 500. Желательно проверить все времена секундомером"""
print('Импульс - 20 сек') pm3.on() # Тот же максимальный уровень, только другим способом time.sleep(20)
print('Пауза - 5 сек') pm3.off() time.sleep(5)
print('3 импульса по 5 сек') pm3.v = 500 # Код ШИМ - 50.0% time.sleep(30)
pm3.v = 0 # Код ШИМ - 0% time.sleep(1)
pm1.off() # Выключим
В скрипте создаются три объекта-силовых модуля, соответствующих реальным модулям: контактор, однобайтовый PDM-контроллер ТЭНа и двухбайтовый ШИМ-контроллер клапана отбора. Контактор в начале теста включает высокое и выключает его в конце теста. Демонстрируется использование методов on() и off(). Внутри этих "рамок" иллюстрируется управление однобайтовыми и двухбайтовыми исполнительными устройствами. Смысл этих тестов описан в комментариях к скрипту. Вторая часть тестов (с ШИМ-контроллером) довольно неспешная. Поэтому нужно запастись терпением и секундомером для контроля длительностей импульсов и пауз.
В принципе, класса PM вполне достаточно для управления всеми исполнительными устройствами автоматики варианта LITE. Однако, мы стремимся к максимальному абстрагированию прикладной управляющей программы от деталей реализации автоматики. Поэтому остался последний шаг: сделать так, чтобы в управляющей программе мы могли бы оперировать физическими величинами (мощность нагрева в ваттах, скорость отбора в мл/час и т.п.), а не какими-то абстрактными кодами, которые мы должны посчитать, с учетом калибровок, и отправить исполнительному устройству. Для этого в библиотеке классов nna реализованы два класса-потомка класса PM. Это - классы PWM и HTR. Первый "отвечает" за двухбайтовые ШИМ-регуляторы и позволяет задавать уровень модуляции в терминах физической величины, управляемой данным регулятором. Например, задавать уровень модуляции клапана отбора в мл/час. Второй (HTR) предназначен для управления ТЭНами путем задания (и поддержания, если нужно) мощности нагрева ТЭНов в ваттах. Причем, с учетом среднеквадратичного напряжения питающей сети. Если, конечно, в системе есть датчик RMS.
Рассмотрим сначала класс PWM. Конструктор принимает три параметра: addr - адрес ШИМ-контроллера на шине i2c, qmax - значение управляемой физической величины при 100% модуляции (например, расход спирта при полностью открытом клапане отбора) и unit - единица измерения управляемой физической величины.
Свойство q позволяет задавать уровень модуляции ШИМ-регулятора в терминах физических величин. Например, инструкция
valve.q = 453
установит такой уровень модуляции клапана отбора, что расход через клапан отбора будет 453 мл/час.
Ниже приведен пример использования объекта класса PWM (в архиве в приложении к топику это файл test_PWM.py):
Скрытый текст
#coding:utf-8 import time import sys from nna import *
"""Демонстрация использования классов-оберток, описанных в модуле nna Тестирование класса PWM (исполнительные устройства с ШИМ-регулированием) :author: OldBean 22.10.2018"""
cont = PM(0x12) # Контактор valve = PWM(0x14, qmax = 930.0, unit = 'мл/час') # Клапан отбора
cont.on() # Подаем напряжение на шину высокого time.sleep(1)
Поскольку класс PWM является наследником класса PM, то у нас остается возможность управлять контроллером при помощи кодов (свойство v базового класса) и методами on() и off(), которые зададут 100% и 0% уровни модуляции, соответственно.
Перейдем теперь ко второму "наследнику" - классу HTR. Он предназначен для управления ТЭНами. Конструктор класса имеет три параметра, которые мы должны задать при создании объекта. Это: addr - адрес PDM-контроллера на шине i2c, w0 - номинальная мощность ТЭНа (т.е. мощность ТЭНа при напряжении 220 В) и rmss - ссылка на объект-датчик RMS, который мы должны создать заранее. Если датчик RMS не задан (параметр rmss опущен), то система будет работать. Но, наивно будет считать, что напряжение сети равно 220 В ;)
После создания объекта класса HTR, мы можем управлять мощностью ТЭНа, присваивая свойству w значение мощности в ваттах. Например, инструкция
heater.w = 620
установит мощность нагрева 620 Вт. Естественно, с учетом напряжения сети, если датчик RMS установлен в системе.
Понятно, что, как и в предыдущем случае, классу-наследнику доступны свойства и методы родительского класса. В данном случае это: свойство v, для управления контроллером при помощи кода (уровня PDM в %), и методы on() и off() для установки 100% и нулевого уровня PDM-модуляции. Пример создания объекта класса HTR и работы с ним - в скрипте ниже (или - в файле test_HTR.py в прилагаемом к топику архиве):
Скрытый текст
#coding:utf-8 import time import sys from nna import *
"""Демонстрация использования классов-оберток, описанных в модуле nna Тестирование класса HTR (контроллер ТЭНа) со стабилизацией мощности :author: OldBean 22.10.2018""" #------------------------------------------------------------------------------- def out(): # Функция для выдачи информации на консоль print 'Уставка - %.1f Вт'%(htr.w), if not rmss is None: print ', RMS = %3.1f В'%(rmss.rms), print ', pdm = %d'%(htr.v) #------------------------------------------------------------------------------- cont = PM(0x12) # Контактор rmss = RMS(0x11) # Датчик RMS #rmss = None # Если работаем без датчика RMS - разремовать htr = HTR(0x13, w0 = 75, rmss = rmss) # Контроллер ТЭНа. Нагрузка - лампочка 75 Вт #------------------------------------------------------------------------------- cont.on() # Подаем напряжение на шину высокого time.sleep(1) """Работа с контроллером ТЭНа как просто с байтовым исполнительным устройством с PDM-регулирование. Свойство v - код, передаваемый устройству""" for c in range(0, 101, 2): # Линейный рост кода, передаваемого устройству htr.v = c print c time.sleep(0.1) for c in range(98, -2, -2): # Линейное снижение кода htr.v = c print c time.sleep(0.1) time.sleep(2)
"""Работаем с контроллером ТЭНа как с регулятором мощности нагрева ТЭНа. Свойство w - устанавливаемая мощность нагрева, с учетом RMS напряжения сети.""" htr.w = 75.0; out() time.sleep(5) htr.w = 50; out() time.sleep(5) htr.w = 3.0; out() time.sleep(5) htr.w = 0 cont.off() # Снимаем высокое #-------------------------------------------------------------------------------
В первой части теста как раз показана низкоуровневая работа через уровень PDM-модуляции, а во второй части - уже через мощности нагрева в ваттах. Обновленный релиз модуля nna от 23.10.2018 и тестовые скрипты находятся в архиве в приложении к данному топику. Итак, все классы, необходимые для написания скриптов автоматизации ректификационной установки (вариант LITE), разработаны, в меру отлажены и собраны в едином файле-модуле nna.
Используя эти классы, мы теперь можем написать скрипт, в котором опишем нашу виртуальную ректификационную установку и алгоритм процесса ректификации, полностью абстрагируясь от деталей реализации электроники, протоколов обмена и форматов данных. Если мы теперь запустим этот скрипт в малинке - она честно выполнит в автоматическом режиме все то, что мы ей предписали в скрипте. Получим спирт. Неплохой, хороший или очень хороший ;) В зависимости от качества сырца, оборудования и алгоритма ректификации, который мы заложили в скрипт.
В принципе, на этом, процесс разработки библиотеки классов nna можно было бы и остановить. Все что нужно для написания скриптов автоматической ректификации у нас уже есть. Но! Хотелось бы иметь возможность общаться с малинкой не только в процессе разработки скрипта, но и - во время его выполнения. Т.е. - в процессе самой ректификации. Однако для этого у нас в библиотеке пока ничего нет. В общем-то, с учетом того, что малинка "живет" под Linux-ом, существует множество способов добавить "интерактивности" в нашу библиотеку и получить возможность взаимодействовать с работающим скриптом "снаружи". Но, к сожалению, я пока не определился с этим вопросом. Поэтому нужен небольшой тайм-аут в разработке библиотеки.
В скрипте создаются три объекта-силовых модуля, соответствующих реальным модулям: контактор, однобайтовый PDM-контроллер ТЭНа и двухбайтовый ШИМ-контроллер клапана отбораOldBean, 23 Окт. 18, 05:43
У меня головы и тело отдельно.Буду пробовать добавить.Еще контактор на воду и второй клапан отбора думаю получится включить в скрипт?
OldBean
Доцент
Красноярск
1K 1.4K
Отв.1483 23 Окт. 18, 09:42
У меня головы и тело отдельно.Буду пробовать добавить.Еще контактор на воду и второй клапан отбора думаю получится включить в скрипт?nic2015, 23 Окт. 18, 08:16
Конечно получится. Если гнезд крейта хватает для модулей. Есть, правда, еще одно ограничение - до 127 устройств на одной шине i2c. Но до этого, наверное, еще далеко... ;)
OldBean
Доцент
Красноярск
1K 1.4K
Отв.1484 24 Окт. 18, 13:29
17.5.4. Краткая документация по классам модуля nna
Библиотека классов nna предназначения для написания скриптов управления процессами дистилляции и ректификации на языке Python. Библиотека позволяет полностью абстрагироваться от деталей реализации электроники, протоколов обмена, форматов данных и т.п. В зависимости от задач, управлять указанными процессами можно "вручную", набирая команды в интерпретаторе питона, либо в автоматическом режиме, оформив последовательность команд и логику процесса в виде файла-скрипта на питоне.
Текущая версия библиотеки классов nna и документация к ней в виде простого текста - в архиве в приложении к топику. Для удобства - еще и здесь в топике:
Скрытый текст17.5.4. Краткая документация по классам модуля nna
Модуль nna содержит определения классов-оберток для объектов, используемых в скриптах автоматизации ректификационной установки (вариант LITE): датчики, цифровые модули, силовые модули и т.д.
Модуль содержит следующие классы, которые могут использоваться в автоматизации (вариант LITE):
PM - базовый класс-обертка силовых модулей PWM - класс-обертка для контроллеров с ШИМ-регулированием (с шагом 0.1%) HTR - класс-обертка для контроллеров с PDM-регулированием (с шагом 1%) Sensor - класс-обертка для датчиков, подключаемых к цифровому модулю DM - класс-обертка цифрового модуля BMP180 - класс-обертка для датчиков атмосферного давления и температуры воздуха RMS - класс-обертка датчика RMS сетевого напряжения
OldBean 24.10.2018
------------------------------------------------------------------------------- class PM - базовый класс-обертка исполнительного устройства (силового модуля).
Конструктор имеет два параметра: addr - адрес исполнительного устройства на шине i2c vlen - длина слова в байтах, передаваемых устройству (по умолчанию 1 байт)
Свойства объектов класса:
v - при присваивании свойству целого числа (слово), отправляет это число исполнительному устройству. Длина слова указывается в конструкторе объекта (параметр vlen). При получении свойства - текущее значение кода, отправленного исполнительному устройству
Методы объектов класса:
on() - включает устройство "на максимум". При вызове метода, устройству посылается максимальный код, зависящий от длины слова, принимаемого устройством (задается параметром vlen при создании объекта) off() - выключает исполнительное устройство. При вызове метода устройству отправляется 0
Примеры создания и использования объектов:
rele = PM(0x12) # Создаем объект релейного типа (контактор) pdm = PM(0x13) # Создаем объект-PDM-регулятор (например, контроллер ТЭНа) pwm = PM(0x14, 2) # Создаем объект - ШИМ-регулятор (например, клапан отбора). vlen = 2
rele.on() # Включаем контактор rele.off() # Выключаем контактор pdm.v = 25 # Установим уровень модуляции 25% (четверть от текущей максимальной мощности) pdm.on() # Включаем PDM-регулятор на максимум (100%) pdm.v = 0 # Выключение PDM-регулятора pwm.v = 552 # Установим уровень модуляции ШИМ-контроллера на 55.2%
------------------------------------------------------------------------------- Класс PWM - класс-обертка для исполнительных устройств с ШИМ-регулированием (PWM) от 0 до 100% с шагом 0.1%. Является наследником класса PM. Позволяет устанавливать уровень модуляции в терминах регулируемой физической величины (например, расхода, при регулировании клапана).
Конструктор имеет три параметра: addr - адрес исполнительного устройства на шине i2c qmax - максимальное значение физической величины, регулируемой исполнительным устройством. Т.е. - при ШИМ = 100% (код - 1000). Например, это может быть максимальный расход, через полностью открытый клапан отбора unit - текстовая строк - единица измерения регулируемой физической величины
Свойства объектов класса:
q - при присваивании свойству значения регулируемой физической величины в единице измерения, заданной в конструкторе объекта (параметр unit; например, мл/час для регулятора клапана отбора) сначала вычисляется значение кода исполнительного устройства, соответствующего заданному значению, затем этот код отсылается устройству. Код вычисляется на основании калибровки (параметр конструктора qmax). При считывании свойства (в скрипте - в правой части операции присваивания) возвращается текущее значение физической величины. u - возвращает единицу измерения регулируемой физической величины. Свойство только для чтения. v - унаследованное от базового класса PM свойство. Может использоваться для управления исполнительным устройством при помощи установки кода (целое число от 0 до 1000), соответствующего нужному уровню модуляции. Младший разряд кода - это дробная часть процентов модуляции. Т.е. код 123 соответствует уровню модуляции 12.3%.
Унаследованные от базового класса PM методы:
on() - включает устройство "на максимум". При вызове метода, устройству посылается максимальный код (0xffff, но в устройстве он обрезается до 1000) off() - выключает исполнительное устройство. При вызове метода, устройству отправляется 0.
Пример создания и использования объекта:
valve1 = PWM(0x14, 930, 'мл/час') # Создание объекта-модулятора 1-го клапана отбора
valve1.q = 465 # Установка расхода 465 мл/час (50% модуляция - 5-секундные импульсы) valve1.v = 256 # Установка уровня модуляции 25.6% (импульсы - 2.56 сек ;) valve1.off() # Выключить отбор
------------------------------------------------------------------------------- Класс HTR - класс-обертка для исполнительных устройств с PDM-регулированием нагревателей (например, ТЭНов). PDM - Pulse Density Modulation еще известен как метод Брезенхема. Диапазон регулировки от 0 до 100% с шагом 1%. Позволяет устанавливать уровень модуляции в терминах мощности нагрева (ваттах), а при наличии датчика RMS - еще и с учетом текущего среднеквадратичного напряжения сети.
Конструктор имеет три параметра:
addr - адрес исполнительного устройства на шине i2c w0 - номинальная мощность нагревателя (ТЭНа) при 220 В. rmss - ссылка на объект датчика RMS (см. класс RMS). Если датчик RMS не задан (по умолчанию rmss = None), регулятор будет устанвливать мощность, в предположении, что напряжение сети равно 220 В.
Свойства объектов класса:
w - при присваивании свойству значения мощности (в ваттах) сначала вычисляется значение кода регулятора, соответствующего этому значению мощности, в предположении, что напряжение сети равно 220 В. Затем, если датчик RMS задан, считывается информация о среднеквадратичном напряжении сети и производится соответствующая коррекция кода. Окончательный код уже пересылается контроллеру. При считывании свойства (в скрипте - в правой части операции присваивания) возвращается текущее значение мощности нагрева. v - унаследованное от базового класса PM свойство. Может использоваться для управления контроллером ТЭНа при помощи непосредственной установки кода (целое число от 0 до 100, лишнее обрежется ;). В таком случае код - это проценты от максимальной мощности нагревателя при текущем напряжении сети.
Унаследованные от базового класса PM методы:
on() - включает нагрев "на максимум". При вызове метода, устройству посылается максимальный код (0xff, но в устройстве он обрезается до 100) off() - выключает нагрев. При вызове метода, устройству отправляется 0.
simple_heater.on() # Включаем максимальный нагрев simple_heater.w = 300 # Нагрев примерно 300 Вт (т.к. работаем без RMS) simple_heater.v = 50 # Мощность нагрева - 50% от максимальной simple_heater.off() # Выключаем нагрев
heater = HTR(0x13, 75, my_RMS_sensor) # Вместо ТЭНа - лампочка 75 Вт, датчик RMS есть heater.w = 15 # На лампочке - реальные 15 Вт heater.v = 1 # На лампочке - 1% мощности (мигает 1 раз в секунду ;) heater.w = 0 # Потушим лампочку
------------------------------------------------------------------------------- Класс Sensor - класс-обертка всех датчиков, подключаемых к цифровому модулю. С пользовательской точки зрения, объекты этого класса играют вспомогательную роль и служат лишь для удобства при обращении к данным, измеренным этим датчиком, по имени датчика, а не по его индексу в списке датчиков, подключенных к цифровому модулю (свойство sensors объектов класса DM - это список подключенных датчиков).
Объекты класса Sensor не требуется создавать "вручную". Они создаются автоматически при вызове метода addSensor(...) объекта класса DM при обращении.
Свойства объектов класса Sensor, доступные для чтения:
с - код АЦП датчика, полученный в последнем обновлении данных цифрового модуля (целое число) v - текущее значение физической величины датчика (после последнего обновления данных цифрового модуля). Как правило, это вещественное число. u - единица измерения физической величины, измеряемой данным датчиком (строка символов) ------------------------------------------------------------------------------- Класс DM - класс-обертка цифрового модуля, к которому подключаются датчики, имеющие 1-Wire интерфейс (с протоколом прикладного уровня как у датчиков DS18B20).
Конструктор имеет один параметр - addr. Это адрес цифрового модуля на шине i2c.
Свойства объектов класса DM:
sensors - это свойство доступно для чтения и представляет собой список из 8 ссылок на датчики (объекты класса Sensor), подключенные к соответствующим восьми каналам цифрового модуля. Если к каналу датчик не подключен, то соответствующий элемент списка равен None.
Методы объектов класса DM:
addSensor(ch = 0, coef = 0, unit = None, offset = 0) - создает датчик (объект класса Sensor) и добавляет его в список датчиков, подключенных к данному цифровому модулю. Метод имеет четыре параметра: ch - номер канала (0...7) цифрового модуля, к которому подключен датчик. Если канал уже занят, то создание и подключение объекта-датчика не производится. coef - калибровочный коэффициент, на который умножается код АЦП (за вычетом смещения offset) при получении значения измеренной физической величины. unit - единица измерения физической величины (строка символов) offset - целое число, вычитаемое из кода АЦП, для коррекции нуля. update() - обновление буфера данных объекта DM. При вызове этого метода, весь буфер цифрового модуля (16 байт) передается в малинку. Далее коды АЦП и, вычисленные с учетом калибровок и смещений, значения физических величин присваиваются соответствующим полям датчиков (объектов класса Sensor). При выполнении этого метода новых измерений не производится. Для пересылки используются данные, полученные в результате последнего измерения, выполненного цифровым модулем (измерения производятся 1 раз в секунду).
Примеры создания и использования объектов классов DM и Sensor:
dm = DM(0x15) # Создаем объект - цифровой модуль ts = dm.addSensor(2, 0.0625, '°C') # Датчик температуры на канале 2, смещение 0 по умолчанию dm.addSensor(6, 0.0733, 'mm Hg', 0x0029) # Датчик давления со смещением на канале 6 ps = dm.sensors[6] # Сделаем имя (ссылку) датчику давления dm.update() # Загрузим данные из цифрового модуля в малинку temperature = dm.sensors[2].v # Получим температуру датчика по его номеру канала pressure = ps.v # Получим давление по имени датчика sum(x <> None for x in dm.sensors) # Количество подключенных датчиков. # Питон - очень выразительный язык. Не так ли? ;)
------------------------------------------------------------------------------- Класс BMP180 - класс-обертка для датчика атмосферного давления и температуры BMP180.
Конструктор не имеет параметров. Адрес датчика на шине i2c задан производителем (0x77).
Свойства объектов класса BMP180:
p - атмосферное давление в мм.рт.ст. t - температура воздуха в °C
Примеры создания и использования объектов класса BMP180:
atm = BMP180() # Создаем объект - датчик BMP180 pressure = atm.p # Атмосферное давление в мм.рт.ст. temperature = atm.t # Температура воздуха в °C
------------------------------------------------------------------------------- Класс RMS - Класс-обертка для датчика среднеквадратичного напряжения в сети.
Конструктор имеет два параметра:
addr - адрес датчика на шине i2c coef - калибровочный коэффициент для пересчета кода в вольты
Свойства объектов класса RMS:
rms - значение среднеквадратичного напряжения сети в вольтах crms - "сырой" код RMS. Это сумма квадратов кодов АЦП 5000 выборок мгновенных значений напряжения сети (4-байтовое беззнаковое целое). По этому коду в малинке рассчитывается значение RMS. code - код RMS. Это свойство нужно для калибровки датчика RMS (получение параметра coef, задаваемого в конструкторе).
Процедура калибровки датчика RMS: 1. Параллельно с датчиком RMS к сети подключается образцовый вольтметр (True RMS) и производится измерение среднеквадратичного напряжения сети Uобр. 2. В интерпретаторе питона определяется свойство code. Например, следующей последовательностью команд питона: >>> from nna import * >>> rms = RMS(0x15) >>> rms.code 3. Вычисляется калибровочный коэффициент по формуле: coef = Uобр/code, который в дальнейшем используется в конструкторе при создании объекта класса RMS (параметр coef).
Примеры создания и использования объектов класса RMS:
my_rms = RMS(0x15, 0.3279) # Создание объекта-датчика RMS U = my_rms.rms # Получение среднеквадратичного напряжения сети raw_rms_code = my_rms.crms # Получение "сырого" кода RMS
-контроллер 1.5 кВт нагревателя без RMSOldBean, 23 Окт. 18, 09:42
А стоит ли доверять производителю оборудования? Будет погрешность, если на тене написано 1500 ватт, не факт что на самом деле так.
OldBean
Доцент
Красноярск
1K 1.4K
Отв.1486 25 Окт. 18, 03:05
А стоит ли доверять производителю оборудования? Будет погрешность, если на тене написано 1500 ватт, не факт что на самом деле так.nic2015, 24 Окт. 18, 21:36
Номинальная мощность нагрева - это всего лишь некая "размерная привязка". Для того, чтобы говорить в терминах мощности. В ее очень точном значении особого смысла нет, т.к. оптимальная/рабочая мощность нагрева при ректификации зависит от многих факторов (геометрия колонны, плотность и качество насадки и т.п.) и обычно подбирается экспериментально. Более того, никто не запрещает и самому измерить номинальную мощность нагревателя... ;)
OldBean
Доцент
Красноярск
1K 1.4K
Отв.1487 31 Окт. 18, 08:02
17.6. Логическая схема ПО варианта LITE
Наконец-то нашел более-менее "человеческое" средство для организации разделяемой памяти. Оно оказалось "редиской" ;) Redis - это нереляционная (NoSQL) система типа "ключ-значение". БД целиком располагается в оперативной памяти, но периодическая (как настроишь ;) репликация данных на диск выполняется. Так что ничего, при выключении питания компьютера, не пропадает. Давным-давно, еще где-то в 90-х, приходилось плотно работать с замечательной Berkeley DB. Остались очень теплые воспоминания. Поэтому, когда набрел на редиску, то решил немного поэкспериментировать с ней в качестве shared memory. И не пожалел об этом! Прекрасный инструмент для этой цели!
С точки зрения разработки ПО, это как раз и было то самое недостающее звено в архитектуре автоматики варианта LITE. Для связи с клиентскими приложениями. Чтобы было легко обеспечить всякие разные пользовательские интерфейсы. Кое-какое тестирование с редиской на малинке я проделал. Все прекрасно работает. Так что с окончательной архитектурой системы вроде бы все стало ясно. Блок-схема - на рисунке. Ненавязчивая автоматизация ректификационной установки. Автоматика. Таким образом, вдобавок к гибкости и расширяемости аппаратной части (первого уровня), попробуем обеспечить эти же качества системы и на третьем уровне иерархии. Первый шаг для этого сделан: клиентская часть полностью абстрагирована от деталей реализации железа и скриптов управления процессами. Предыдущий топикВернуться к оглавлениюСледующий топик
Ну и в завершение вчерашнего "блога" ;) Идея с редиской оказалась достаточно эффективной. Сегодня прикинул "на коленке" небольшой пример "универсального" redis-клиента на питоне с ткинтером для автоматизации варианта LITE. Чтобы "почувствовать" как это все будет в деле. ИМХО, неплохо!
Суть тестов. Клиент запускается с рабочей станции под Ubuntu. В этой же локальной сети, по адресу 192.168.0.22 находится малинка, на которой запущено приложение, которое в цикле пишет данные со всех датчиков в БД Redis и, также, читает оттуда данные для контроллеров исполнительных устройств. Если данные для контроллеров, полученные из БД, не совпадают установленными ранее, то малинка посылает контроллерам новые значения. Железо, в свою очередь, реагирует на это. Таким образом, при помощи данного клиента вполне можно управлять системой в ручном режиме в реальном времени. Причем, при изменении конфигурации железа, какие-то изменения в клиенте даже могут и не потребоваться. Клиента можно запустить и непосредственно на малинке. А можно и там и там одновременно ;)
Скриншот работающего клиента - на картинке в приложении. Посмотреть как выглядит питоновский скрипт, реализующий данного клиента - ниже. Клиента несложно "причесать" (группировки полей, их имена, единицы измерения и т.п.). Просто сейчас речь не об этом. Поэтому пока - такая raw ;) Скрытый текст
#coding:utf-8 """Пример простого клиента для работы с ненавязчивой автоматикой (вариант LITE) через NoSQL СУБД Redis OldBean, 01.11.2018"""
import redis from Tkinter import *
r = redis.StrictRedis('192.168.0.22') # Соединяемся с БД keys = r.keys('*') # Получим все ключи из базы данных #------------------------------------------------------------------------------ def showValues(): # Показываем значения параметров, полученных от редиски for key in keys: index = keys.index(key) if key != 'cont' and key != 'heater' and key != 'valve' and key != 'quitFlag' : val = r.get(key) # Получаем от редиски значение по ключу и форматируем их if key == 'rmss' or key == 'atm' or key == 'pot_pres': s = '% 3.1f'%(float(val)) else: s = '% .3f'%(float(val)) digits[index].configure(text = s) # Показываем в соотвествующих полях labels[0].after(1000, showValues) # Через секунду опять сюда зайдем #------------------------------------------------------------------------------ def sendDataToRedis(): # Отсылаем данные редиске по нажатию кнопки for key in keys: index = keys.index(key) if key == 'cont' or key == 'heater' or key == 'valve' or key == 'quitFlag' : val = digits[index].get() # Данные из редактируемого поля if val != r.get(key): # Данные в редиске не совпадают с val - обновляем r.set(key, val) #------------------------------------------------------------------------------ root = Tk() root.title('Simple client') labels = [] digits = [] for key in keys: # Формируем метки и поля ввода/вывода информации index = keys.index(key) labels.append(Label(width = 9, anchor = 'w', text = key.decode('utf-8'))) labels[index].grid(row = index) # Для полей ввода - окна редактирования (Entry) if key == 'cont' or key == 'heater' or key == 'valve' or key == 'quitFlag' : digits.append(Entry(width = 8, justify = RIGHT)) digits[-1].insert(0, '0') else: # Для полей вывода - просто метки, текст которых будем обновлять digits.append(Label(width = 8, anchor = 'e')) digits[index].grid(row = index, column = 1) but = Button(text = ' Send data to Redis ', command = sendDataToRedis) but.grid(row = index + 1, columnspan = 2) showValues() root.mainloop()
Это, действительно, весь клиент! ;)
Думаю, нужно будет переработать структуры данных библиотеки nna. Увы, существующий вариант библиотеки разрабатывался без учета редиски. Поэтому там (в серверной части, "крутящейся" на малинке) получилось много некрасивых ветвлений и других "подкостыльников". Поэтому серверную часть я сюда и не выкладываю. Потом положу уже причесанный вариант.
Ну и в качестве резюме: похоже, вопрос с универсальным клиентом уже не стоит. Появилось устойчивое ощущение что его удастся сделать. Более того, не очень-то сложно сделать и автоматическое определение конфигурации железа и автоматизировать генерирование соответствующих объектов. Нужно будет как-нибудь на досуге немного подправить прошивки модулей и попробовать такую фичу. Интересно...
Супер! После "причёсывания" алгоритма так и просится хороший графический клиент. Красота внутреннего содержания просто обязана быть оформлена в виде "вишенки на тортике"
OldBean
Доцент
Красноярск
1K 1.4K
Отв.1490 01 Нояб. 18, 17:21
так и просится хороший графический клиенgol_avto, 01 Нояб. 18, 14:09
Ну это - само собой. На графики температуры в real time иногда можно пялиться часами. Особенно, когда пробуешь какой-нибудь достаточно "жесткий" алгоритм стабилизации температуры в колонне. Мне это почему-то всегда напоминает соревнования по керлингу. Малинка старается загнать температуру в пару-тройку квантов около уставки, неистово "трет щеткой" (скоростью отбора). То с одной стороны, то с другой... Три, три, три, три! У... зараза! Опять проскочила уставку!
Азарта, пожалуй даже и побольше будет! Потому что на следующем прогоне, можно подкорректировать "правила" и опять от всей души "поболеть" за малинку... ;)))
сообщение удалено
nic2015
Магистр
Феодосия
219 56
Отв.1491 05 Нояб. 18, 18:40
Датчик диф. давления выдает -0.22. А как в данном случае использовать параметр offset?
Датчик диф. давления выдает -0.22. А как в данном случае использовать параметр offset?nic2015, 05 Нояб. 18, 18:40
Дифференциальное давление (в мм.рт.ст.) вычисляется по формуле: dP = coef*(код_АЦП - offset). В Вашем случае код_АЦП, когда оба штуцера датчика открыты, равен в шестнадцатеричном формате 0x0026 (или 0x0025 - не могу разобрать). Вот это целое число и есть offset, которое нужно использовать в качестве параметра в функции addSensor, при добавлении датчика к цифровому модулю. Конкретный пример фрагмента кода:
my_dm = DM(addr = 0x15) # Создаем объект - цифровой модуль ... my_dif_pres_sensor = my_dm.addSensor(ch = 6, coef = 0.0733, unit = 'мм.рт.ст', offset = 0x0026) # Добавляем к 6-му каналу цифрового модуля диф. датчик давления ...
Asus
Доцент
Москва
1.4K 692
Отв.1493 06 Нояб. 18, 10:12
Дорогой OldBean, сейчас на форуме наметился счастливый поворот к вакуумным технологиям брожения, дистилляции и ректификации. Может быть имеет смысл расширить границы контрольной уставки старт-стопа - температура(f)давление - до комнатных температур перегонки?
OldBean
Доцент
Красноярск
1K 1.4K
Отв.1494 06 Нояб. 18, 17:49
сейчас на форуме наметился счастливый поворот к вакуумным технологиям брожения, дистилляции и ректификации.Asus, 06 Нояб. 18, 10:12
Честно говоря, я не думал о ректификации при пониженном давлении. Есть ли в этом вообще какой-нибудь смысл? Какие параметры ректификации становятся лучше? Хотя, никаких принципиальных ограничений на температуры процессов в систему не закладывалось.
Asus
Доцент
Москва
1.4K 692
Отв.1495 06 Нояб. 18, 19:04
Есть. Из-за отсутствия кислорода и низкой температуры меньше новообразований в процессе ректификации и дистилляции.
OldBean
Доцент
Красноярск
1K 1.4K
Отв.1496 07 Нояб. 18, 05:26
Есть. Из-за отсутствия кислорода и низкой температуры меньше новообразований в процессе ректификации и дистилляции.Asus, 06 Нояб. 18, 19:04
С дистилляцией вопроса нет. Непрерывно отбирать спирт у живых дрожжей - вполне достойная задача ;) Я же говорил только о ректификации. Здесь ситуация совсем не такая однозначная.
Во-первых, свободного кислорода внутри колонны в процессе ректификации и при атмосферном давлении немного.
Во-вторых, ну уменьшите Вы температуру процесса градусов на 30-40. Для системы "спирт-вода" больше вряд ли получится. Есть ограничения снизу (температура дефлегматора должна быть хотя бы не ниже комнатной, при жаре). А в кельвинах, которые определяют кинетику, эта дельта всего лишь порядка 10-15%. Т.е. - далеко не в разы! К сожалению, у меня нет данных по кинетике каталитических процессов (тем более - для разных материалов насадки) в системе "спирт-вода", чтобы точно сказать. Но что-то не верится, что такая небольшая дельта сильно уменьшит скорости реакций в этой системе. Это может быть только для каких-нибудь пороговых (цепных) процессов, которые в этой системе вроде не просматриваются (а для взрыва паров, в колонне слишком мало кислорода ;)
Ну и в-третьих, при вакуумировании, давление падает гораздо быстрее, чем температура кипения. Падает плотность и, следовательно, массопотоки паровой фазы в колонне. Последние-то, в отличие от температуры, упадут в разы и даже на порядки! Придется снижать скорости отбора. Это, естественно, приведет к заметному увеличению общего времени ректификации. А это - увеличение расходов на электроэнергию и воду. У если к этому добавить заметное усложнение оборудования, то особого энтузиазма, по поводу вакуумной ректификации системы "спирт-вода", как то не возникает...
Esc
Профессор
Москва
2.1K 2.1K
Отв.1497 07 Нояб. 18, 10:46
Непрерывно отбирать спирт у живых дрожжей - вполне достойная задачаOldBean, 07 Нояб. 18, 05:26
Сергей Владимирович, 1.Кислород растворён и в браге и сырце изначально, и в достаточном количестве. Подсос из атмосферы тоже есть всегда из-за возмущающего колебания давления в колонне от взрывного кипения в кубе. 2.Скорость элементарных хим.реакций (без учёта понижающих поправок Вант-Гоффа) квадратично зависит от температуры. 3.Уже наработана позитивная практика на профильных ветках форума и все результаты и оценки выложены. Пост можно удалить за оффтоп, но просьба остаётся в силе, если это сильно не обременит. Сам я этого не сделаю, из-за неграмотности в программировании.
OldBean
Доцент
Красноярск
1K 1.4K
Отв.1499 07 Нояб. 18, 17:47
У меня есть что возразить и про растворимость кислорода, и про его "подсос" в колонну, и про скорости реакций. Но, я думаю, этот спор вряд ли конструктивен. Если Вы считаете, что вакуумная ректификация решит Ваши проблемы, то почему бы ей не заниматься? Но давайте вернемся к уставке. Я не совсем понял что Вы имеете в виду. В чем проблема.
В существующих на данное время скриптах, алгоритм оценки оптимальной уставки следующий. После отбора голов включается отбор уже с рабочей скоростью отбора (для подголовников, тела и исправимых хвостов). Температура датчика (внизу колонны) при этом обычно немножко (на несколько десятых долей градуса) подрастает, затем в течении 20-30 мин чуть-чуть (на 1-2 десятых градуса) снижается и потом несколько часов может стоять примерно на этом уровне с медленными колебаниями на 1-2 десятых градуса. Как показывает опыт, вот этот первый максимум плюс пару десятых градуса как раз и стОит выбрать в качестве оптимальной уставки. Она автоматически запоминается. Потом, после включения режима стабилизации, система использует ее в качестве рабочей уставки и "старается" регулировать отбор так, чтобы температура датчика ни в коем случае не превысила эту уставку более чем на квант.
Если же Вы проводите ректификацию при пониженном давлении, то температура датчика внизу колонны, естественно, будет меньше. Но именно она и будет взята в качестве уставки. Хоть комнатная, хоть 78°. Системе все равно.