PID функция
Добавить скрипт в Скрипты –> Общие функции (Scripts ->Common functions)
PID = {
-- default params
defaults = {
-- invert algorithm, used for cooling
inverted = false,
-- minimum output value
min = 0,
-- maximum output value
max = 100,
-- proportional gain
kp = 1,
-- integral gain
ki = 1,
-- derivative gain
kd = 1,
}
}
-- PID init, returns new PID object
function PID:init(params)
local n = setmetatable({}, { __index = PID })
local k, v
-- set user parameters
n.params = params
-- copy parameters that are set by user
for k, v in pairs(PID.defaults) do
if n.params[ k ] == nil then
n.params[ k ] = v
end
end
-- reverse gains in inverted mode
if n.params.inverted then
n.params.kp = -n.params.kp
n.params.ki = -n.params.ki
n.params.kd = -n.params.kd
end
return n
end
-- resets algorithm on init or switch back from manual mode
function PID:reset()
-- previous value
self.previous = grp.getvalue(self.params.current)
-- reset iterm
self.iterm = 0
-- last running time
self.lasttime = os.time()
-- clamp iterm
self:clampiterm()
end
-- clamps iterm value
function PID:clampiterm()
self.iterm = math.max(self.iterm, self.params.min)
self.iterm = math.min(self.iterm, self.params.max)
end
-- clamp and set new output value
function PID:setoutput()
local t, object, value
self.output = math.max(self.output, self.params.min)
self.output = math.min(self.output, self.params.max)
value = math.floor(self.output)
local t = type(self.params.output)
-- write to output if object is set
if t == 'string' or t == 'table' then
if t == 'string' then
self.params.output = { self.params.output }
end
for _, output in ipairs(self.params.output) do
grp.write(output, value, dt.scale)
end
end
end
-- algorithm step, returns nil when disabled
-- or no action is required, output value otherwise
function PID:run()
local result
-- get manual mode status
local manual = self.params.manual
and grp.getvalue(self.params.manual) or false
-- in manual mode, do nothing
if manual then
self.running = false
-- not in manual, check if reset is required after switching on
elseif not self.running then
self:reset()
self.running = true
end
-- compute new value if not in manual mode
if self.running then
-- get time between previous and current call
local now = os.time()
self.deltatime = now - self.lasttime
self.lasttime = now
-- run if previous call was at least 1 second ago
if self.deltatime > 0 then
result = self:compute()
end
end
return result
end
-- computes new output value
function PID:compute()
local current, setpoint, deltasc, deltain, output
-- get input values
current = grp.getvalue(self.params.current)
setpoint = grp.getvalue(self.params.setpoint)
-- delta between setpoint and current
deltasc = setpoint - current
-- calculate new iterm
self.iterm = self.iterm + self.params.ki * self.deltatime * deltasc
self:clampiterm()
-- delta between current and previous value
deltain = current - self.previous
-- calculate output value
self.output = self.params.kp * deltasc + self.iterm
self.output = self.output - self.params.kd / self.deltatime * deltain
-- write to output
self:setoutput()
-- save previous value
self.previous = current
return self.output
end
Параметры
Обязательные:
- current – (адрес или имя объекта) текущее значение температуры (2 byte float или любое числовое значение)
- setpoint – (адрес или имя объекта) значение требуемой (установленной) температуры (2 byte float или любое числовое значение)
Опциональные:
- manual – (адрес или имя объекта) PID алгоритм останавливается, если значение этого объекта равно 1
- output – (адрес или имя объекта, может быть представлено в виде таблицы с множеством объектов) управляемый объект (1 byte scaled)
- inverted – (булево, по умолчанию равно false) инвертирует алгоритм при использовании для охлаждения
- min – (число, по умолчанию 0) минимальное выходное значение
- max – (число, по умолчанию 100) максимальное выходное значение
- kp – (число, по умолчанию 1) Пропорциональная составляющая
- ki – (число, по умолчанию 1) Интегральная составляющая
- kd – (число, по умолчанию 1) Дифференциальная составляющая
Добавление резидентного скрипта
PID алгоритм должен быть добавлен в Скрипты –> Резидентные (Scripts -> Resident)
Пример скрипта:
-- инициализация pid алгоритма
if not p then
p = PID:init({
current = '1/1/1',
setpoint = '1/1/2',
output = '1/1/3'
})
end
-- запуск алгоритма
p:run()
Пример скрипта с несколькими управляемыми объектами:
-- >инициализация pid алгоритма
if not p then
p = PID:init({
current = '1/1/1',
setpoint = '1/1/2',
output = { 'PWM 1', 'PWM 2', '1/1/5' }
})
end
-- запуск алгоритма
p:run()
Выходное значение:
p:run() возвращает выходное значение. Если не установлен параметр output, вы можете использовать это значение для управления объектами в ручном режиме