Logic Machine Forum
PID to temperature not scale - Printable Version

+- Logic Machine Forum (https://forum.logicmachine.net)
+-- Forum: LogicMachine eco-system (https://forum.logicmachine.net/forumdisplay.php?fid=1)
+--- Forum: Scripting (https://forum.logicmachine.net/forumdisplay.php?fid=8)
+--- Thread: PID to temperature not scale (/showthread.php?tid=4157)



PID to temperature not scale - KoBra - 25.07.2022

Dears, i want a PID that calculates the supply water temp based on the setpoint and the actual temperature. But i have the PID script (see below) but i think the problem is i am keeping 0 is the fact that the output is in scale not temperature

-- clamp and set new output value
function PID2Confusedetoutput()
  local t, object, value, dt.scale


can somebody tell me how the datatype should be set to get the right output?

Code:
PID22 = {
  -- default params
  defaults = {
    -- invert algorithm, used for cooling
    inverted = true,
    -- minimum output value
    min = 5,
    -- maximum output value
    max = 30,
    -- proportional gain
    kp = 0.1,
    -- integral gain
    ki = 0.1,
    -- derivative gain
    kd = 0.1,
  }
}

-- PID2 init, returns new PID2 object
function PID2:init(params)
  local n = setmetatable({}, { __index = PID2 })
  local k, v

  -- set user parameters
  n.params = params

  -- copy parameters that are set by user
  for k, v in pairs(PID2.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 a switch back from manual mode
function PID2: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 PID2: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 PID2:setoutput()
  local t, object, value, dt.scale

  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)
    end
  end
end

-- algorithm step, returns nil when disabled or no action is required, output value otherwise
function PID2: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 PID2: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



RE: PID to temperature not scale - admin - 26.07.2022

Use this if the output data type is 2-byte floating point (float16).
Code:
function PID2:setoutput()
  local t, object, value

  self.output = math.max(self.output, self.params.min)
  self.output = math.min(self.output, self.params.max)

  value = 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.float16)
    end
  end
end



RE: PID to temperature not scale - KoBra - 27.07.2022

(26.07.2022, 06:17)admin Wrote: Use this if the output data type is 2-byte floating point (float16).
Code:
function PID2:setoutput()
  local t, object, value

  self.output = math.max(self.output, self.params.min)
  self.output = math.min(self.output, self.params.max)

  value = 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.float16)
    end
  end
end
tnx for the help, works perfectly


RE: PID to temperature not scale - KoBra - 27.07.2022

(27.07.2022, 09:48)KoBra Wrote:
(26.07.2022, 06:17)admin Wrote: Use this if the output data type is 2-byte floating point (float16).
Code:
function PID2:setoutput()
  local t, object, value

  self.output = math.max(self.output, self.params.min)
  self.output = math.min(self.output, self.params.max)

  value = 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.float16)
    end
  end
end
tnx for the help, works perfectly

it is not as accurate as i hoped. Is there and easy fix to get temperature in 0,1 degree output?


RE: PID to temperature not scale - admin - 28.07.2022

Depending on the value range float16 type won't have 0.1 precision. In the original setoutput function there was a math.floor call that rounded the value down to an integer. You can log the output value before sending it like this:
Code:
function PID2:setoutput()
  local t, object, value

  self.output = math.max(self.output, self.params.min)
  self.output = math.min(self.output, self.params.max)

  value = self.output

  log(value)

  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.float16)
    end
  end
end