This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

PID to temperature not scale
#1
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
Reply
#2
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
Reply
#3
(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
Reply
#4
(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?
Reply
#5
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
Reply


Forum Jump: