PID управление 3х клапаном
Ниже приведу пример текущей настройки для управления 3-х ходовым клапаном с помощью PID регулятора в моей конфигурации openHAB.
Из оборудования установлен 3-х ходовой актуатор ESBE на контуре теплых полов.
Управлением открытия/закрытия занимается контроллер MegaD, задействованы 2 релейных выхода. Для них созданы соответствующие Items.
1. Установлен Add-on PID Controller, который соответственно рассчитывает выходное значение в зависимости от температуры уставки (tp_setpoint
) и текущей температуры подачи (tp_flowpipe
) и записывает его в Item tp_pid
. Настройки по коэффициентами пока выбраны следующие: P=10, I=0.5, D=80, D-T1=300
Код правила для расчета PID:
configuration: {} triggers: - id: 09519cc5-516c-42a4-857f-e3ed9eacec19 label: PID Controller Trigger configuration: input: tp_flowpipe setpoint: tp_setpoint kp: 10 kd: 80 kdTimeConstant: 300 ki: 0.5 loopTime: 120000 type: pidcontroller.trigger conditions: - inputs: {} id: "2" configuration: itemName: boiler_flame state: ON operator: = type: core.ItemStateCondition - inputs: {} id: "3" configuration: itemName: boiler_status state: ON operator: = type: core.ItemStateCondition actions: - inputs: {} id: "1" configuration: itemName: tp_pid type: core.ItemCommandAction
Поскольку котел у меня 1-контурный, и в момент переключения котла в режим подогрева ГВС температура в контуре подачи теплого пола неминуемо падает - стоит исключение на запуск этого правила, когда boiler_flame
и boiler_status
активны. Это позволяет несколько снизить лишнее «кручение» клапаном.
2. Следующее правило запускается по cron каждые 3 минуты, и в зависимости от текущего значения PID управляет 3-х ходовым клапаном.
Код правила управления 3-х ходовым клапаном:
configuration: {} triggers: - id: "1" configuration: cronExpression: 0 0/3 * * * ? * type: timer.GenericCronTrigger conditions: [] actions: - inputs: {} id: "2" configuration: type: application/javascript script: >- /* Items to control 3p Actuator three_way_close - close relay three_way_open - open relay */ function rotateActuator (pidValue) { var actionTimerCorrection = 2 // Maybe 1 to any reasonable value, decreasing rotation time var actionTimerMax = 15000 // milliseconds, maximum 15 sec var actionTimerMin = 1000 // milliseconds, minimum 1 sec console.log('PID: ' + pidValue) var actionItem3pActuator = null var action = null var actionTimer = 0 action = (pidValue > 0) ? 'open' : 'close'; action3pActuator = items.getItem('three_way_' + action) actionTimer = Math.round( Math.abs( pidValue ) / actionTimerCorrection ) * 500 // Limit to actionTimerMax if it more actionTimer = (actionTimer > actionTimerMax) ? actionTimerMax : actionTimer; if (actionTimer >= actionTimerMin) { console.log ('Actuator action: ' + action3pActuator.name + ' for ' + actionTimer + ' (' + actionTimer/1000 + ' sec)') console.info(action3pActuator.name + ' send command ON') action3pActuator.sendCommand('ON') setTimeout(() => { console.info(action3pActuator.name + ' send command OFF') action3pActuator.sendCommand('OFF') }, actionTimer); } else { console.info('actionTimer: ' + actionTimer/1000 + ' < ' + actionTimerMin/1000 + ' sec - nothing to do with 3p actuator') } } rotateActuator(items.tp_pid.state) type: script.ScriptAction
Знать текущее положение клапана, насколько он открыт или закрыт в реальности абсолютно не нужно. В крайних положениях он в любом случае отключается по внутри установленным реле.
Но интересно же! :)
Поэтому попробовал реализовать и такую вещь, как отображение текущего уровня открытия 3-х ходового клапана.
Логика следующая. Есть отдельный Item (tp_3pA_status
), в который будет писаться уровень открытия клапана.
И отдельное правило, которое отслеживает работу реле открытия/закрытия клапана, считает время, на которое соответствующее реле было открыто или закрыто, и обновляет собственно статус в нужный нам Item.
Экспериментальным путем было установлено, что полное время открытия/закрытия клапана составляет в моем случае 127 сек. (при заявленных 120 сек. производителем). Именно это значение используется в дальнейшем.
Код правила для обновления статуса клапана:
configuration: {} triggers: - id: "1" configuration: itemName: three_way_close type: core.ItemStateChangeTrigger - id: "2" configuration: itemName: three_way_open type: core.ItemStateChangeTrigger conditions: [] actions: - inputs: {} id: "3" configuration: type: application/javascript script: >- var maxRotationTime = 127000 var current3pAState = items.tp_3pA_status.state var new3pAState = null var cacheId = 'tp_3pAStatus_Id' var processDuration = 0 var stor = cache.private.get(cacheId, () => ({ 'cacheId': cacheId, 'openStarts': null, 'closeStarts': null, })); function resetStorage(stor) { stor.openStarts = null; stor.closeStarts = null; cache.private.put(stor.cacheId, stor); } function startMoving(stor, direction) { stor[direction + 'Starts'] = Date.now(); cache.private.put(stor.cacheId, stor); console.log('3pA ' + direction + ' starts at: ' + stor[direction + 'Starts']) } function stopMoving(stor, direction) { processDuration = Date.now() - stor[direction + 'Starts']; console.log('3pA ' + direction + ' stopped at ' + Date.now() + ', start was at: ' + stor[direction + 'Starts'] + ', Dur:' + processDuration) rotationPercent = processDuration / maxRotationTime; console.log('Rotation was at: ' + rotationPercent * 100 + '%') new3pAState = Number.parseFloat(current3pAState) + ((direction == 'open') ? + Number.parseFloat(rotationPercent) : - Number.parseFloat(rotationPercent)) console.log('New status is set to: ' + (new3pAState) * 100 + '%') items.tp_3pA_status.postUpdate(new3pAState) resetStorage(stor) } var direction = null switch(event.itemName) { case 'three_way_open': direction = 'open' if (event.itemState.toString() == 'ON') { startMoving(stor, direction) } else { stopMoving(stor, direction) } break case 'three_way_close': direction = 'close' if (event.itemState.toString() == 'ON') { startMoving(stor, direction) } else { stopMoving(stor, direction) } break default: console.error('We not supposed to be here. Something wrong') break } type: script.ScriptAction
4. Ну и в завершение есть еще небольшое правило, для сброса статуса уровня открытия клапана. Оно вращает клапан до крайнего положения закрытия, и обновляет статус в ноль. Ну а на время своей работы отключает другие правила, которые могут попытаться как то с клапаном работать.
configuration: {} triggers: [] conditions: [] actions: - inputs: {} id: "1" configuration: type: application/javascript script: >+ console.log("=== Resetting 3 point actuator Status ===") var maxRotationTime = 127000 // 127 sec var rulesToDisable = [ 'pidTest', 'update3pAStatus', 'rotate_3p_actuator_with_pid', ] // Disable rules console.log('Disabling needed rules, to prevent their runnig:') rulesToDisable.forEach((rule) => { rules.setEnabled(rule, false); console.log('Rule ' + rule + ' disabled.') }); items.three_way_close.sendCommand('ON') console.log('Starting closing 3pA for ' + Math.round(maxRotationTime/1000) + ' sec.') setTimeout(() => { items.three_way_close.sendCommand('OFF') console.log('Closing 3pA done') items.tp_3pA_status.postUpdate(0) console.log('Status of 3pA updated to 0') // Enable rules console.log('Resetting complete. Enabling rules:') rulesToDisable.forEach((rule) => { rules.setEnabled(rule, true); console.log('Rule ' + rule + ' enabled.') }); }, maxRotationTime); type: script.ScriptAction