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:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
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:
1234567891011121314151617181920
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:
1234567891011121314151617181920
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:
1234567891011121314151617181920
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:
1234567891011121314151617181920212223
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: