12.12.2019, 22:34 (This post was last modified: 12.12.2019, 22:46 by Erwin van der Zwart.)
Hi,
You are probably missing the functions that are in the user lib, did you add these to user.pid?
To change the setpoint to a static value you probably need to change the function that gets the value from the object in user.pid, but i would advice to create a virtual object and just set the static value there once.
Next to that you also need require('user.pid') at the start of your script.
There is also a PID block in the FBEditor, maybe this one fits better to your needs.
24.12.2019, 18:50 (This post was last modified: 24.12.2019, 18:50 by josdegroot.)
Thanks Erwin,
I'm already a step further and it lookalike the scripts is working.
If I run the diagnostics inside ETS I see that the Logic machine sends an ON and OFF message to the group that has to turn the electrical radiator on... but nothing will happen....
The scripts that I'm using?
Do you have any idea why the group doesn't respond to the On and Off command? Do I have to set a datatype (switch?) somewhere?
Code:
1234567891011
-- init pid algorithmifnotpthenp = PID:init({
current = '12/2/1',
setpoint = '32/1/1',
output = '12/0/7'
})
end-- run algorithmp:run()
PID = {
-- default paramsdefaults = {
-- invert algorithm, used for coolinginverted = false,
-- minimum output valuemin = 0,
-- maximum output valuemax = 100,
-- proportional gainkp = 1,
-- integral gainki = 1,
-- derivative gainkd = 1,
}
}
-- PID init, returns new PID objectfunctionPID:init(params)
localn = setmetatable({}, { __index = PID })
localk, v-- set user parametersn.params = params-- copy parameters that are set by userfork, vinpairs(PID.defaults) doifn.params[ k ] == nilthenn.params[ k ] = vendend-- reverse gains in inverted modeifn.params.invertedthenn.params.kp = -n.params.kpn.params.ki = -n.params.kin.params.kd = -n.params.kdendreturnnend-- resets algorithm on init or a switch back from manual modefunctionPID:reset()
-- previous valueself.previous = grp.getvalue(self.params.current)
-- reset itermself.iterm = 0-- last running timeself.lasttime = os.time()
-- clamp itermself:clampiterm()
end-- clamps iterm valuefunctionPID: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 valuefunctionPID:setoutput()
localt, object, valueself.output = math.max(self.output, self.params.min)
self.output = math.min(self.output, self.params.max)
value = math.floor(self.output)
localt = type(self.params.output)
-- write to output if object is setift == 'string'ort == 'table'thenift == 'string'thenself.params.output = { self.params.output }
endfor_, outputinipairs(self.params.output) dogrp.write(output, value, dt.scale)
endendend-- algorithm step, returns nil when disabled or no action is required, output value otherwisefunctionPID:run()
localresult-- get manual mode statuslocalmanual = self.params.manualandgrp.getvalue(self.params.manual) orfalse-- in manual mode, do nothingifmanualthenself.running = false-- not in manual, check if reset is required after switching onelseifnotself.runningthenself:reset()
self.running = trueend-- compute new value if not in manual modeifself.runningthen-- get time between previous and current calllocalnow = os.time()
self.deltatime = now - self.lasttimeself.lasttime = now-- run if previous call was at least 1 second agoifself.deltatime > 0thenresult = self:compute()
endendreturnresultend-- computes new output valuefunctionPID:compute()
localcurrent, setpoint, deltasc, deltain, output-- get input valuescurrent = grp.getvalue(self.params.current)
setpoint = grp.getvalue(self.params.setpoint)
-- delta between setpoint and currentdeltasc = setpoint - current-- calculate new itermself.iterm = self.iterm + self.params.ki * self.deltatime * deltascself:clampiterm()
-- delta between current and previous valuedeltain = current - self.previous-- calculate output valueself.output = self.params.kp * deltasc + self.itermself.output = self.output - self.params.kd / self.deltatime * deltain-- write to outputself:setoutput()
-- save previous valueself.previous = currentreturnself.outputend
(25.12.2019, 11:09)admin Wrote: From group monitor screenshot it looks like you have KNX TP/IP loop. Do you have any other IP interfaces connected to the same TP line?
Where can you see this? I'm using a Siemens Ip interface to connect ETS to the TP line. I only would use the logic machine to run scripts..
Can you help me what to change?
You don’t need to use the Siemens interface as the controller does the same functions, you can remove it. if you keep using it then disable KNX IP features in the KNX settings.
25.12.2019, 16:15 (This post was last modified: 25.12.2019, 16:33 by josdegroot.)
(25.12.2019, 16:13)Erwin van der Zwart Wrote: Hi,
You don’t need to use the Siemens interface as the controller does the same functions, you can remove it. if you keep using it then disable KNX IP features in the KNX settings.
BR,
Erwin
check disabled the checkbox @ the logic machine.
Does it make sense that I have the address 1.1.100 for the logic machine?
Will check if the script is working fine now.
I see that the IP loop is gone . not 4 messages anymore .
But the Switch actor doesn't respond to the On or Off messages the logic machine is sending to group 12/0/7. (See attachment)
If I put A switch object from a taster in this group everything works fine...
PID = {
-- default paramsdefaults = {
-- invert algorithm, used for coolinginverted = false,
-- minimum output valuemin = 0,
-- maximum output valuemax = 100,
-- proportional gainkp = 1,
-- integral gainki = 1,
-- derivative gainkd = 1,
}
}
-- PID init, returns new PID objectfunctionPID:init(params)
localn = setmetatable({}, { __index = PID })
localk, v-- set user parametersn.params = params-- copy parameters that are set by userfork, vinpairs(PID.defaults) doifn.params[ k ] == nilthenn.params[ k ] = vendend-- reverse gains in inverted modeifn.params.invertedthenn.params.kp = -n.params.kpn.params.ki = -n.params.kin.params.kd = -n.params.kdendreturnnend-- resets algorithm on init or a switch back from manual modefunctionPID:reset()
-- previous valueself.previous = grp.getvalue(self.params.current)
-- reset itermself.iterm = 0-- last running timeself.lasttime = os.time()
-- clamp itermself:clampiterm()
end-- clamps iterm valuefunctionPID: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 valuefunctionPID:setoutput()
localt, object, valueself.output = math.max(self.output, self.params.min)
self.output = math.min(self.output, self.params.max)
value = math.floor(self.output)
localt = type(self.params.output)
-- write to output if object is setift == 'string'ort == 'table'thenift == 'string'thenself.params.output = { self.params.output }
endfor_, outputinipairs(self.params.output) dogrp.write(output, value, dt.scale)
endendend-- algorithm step, returns nil when disabled or no action is required, output value otherwisefunctionPID:run()
localresult-- get manual mode statuslocalmanual = self.params.manualandgrp.getvalue(self.params.manual) orfalse-- in manual mode, do nothingifmanualthenself.running = false-- not in manual, check if reset is required after switching onelseifnotself.runningthenself:reset()
self.running = trueend-- compute new value if not in manual modeifself.runningthen-- get time between previous and current calllocalnow = os.time()
self.deltatime = now - self.lasttimeself.lasttime = now-- run if previous call was at least 1 second agoifself.deltatime > 0thenresult = self:compute()
endendreturnresultend-- computes new output valuefunctionPID:compute()
localcurrent, setpoint, deltasc, deltain, output-- get input valuescurrent = grp.getvalue(self.params.current)
setpoint = grp.getvalue(self.params.setpoint)
-- delta between setpoint and currentdeltasc = setpoint - current-- calculate new itermself.iterm = self.iterm + self.params.ki * self.deltatime * deltascself:clampiterm()
-- delta between current and previous valuedeltain = current - self.previous-- calculate output valueself.output = self.params.kp * deltasc + self.itermself.output = self.output - self.params.kd / self.deltatime * deltain-- write to outputself:setoutput()
-- save previous valueself.previous = currentreturnself.outputend
PID = {
-- default paramsdefaults = {
-- invert algorithm, used for coolinginverted = false,
-- minimum output valuemin = 0,
-- maximum output valuemax = 100,
-- proportional gainkp = 1,
-- integral gainki = 1,
-- derivative gainkd = 1,
}
}
-- PID init, returns new PID objectfunctionPID:init(params)
localn = setmetatable({}, { __index = PID })
localk, v-- set user parametersn.params = params-- copy parameters that are set by userfork, vinpairs(PID.defaults) doifn.params[ k ] == nilthenn.params[ k ] = vendend-- reverse gains in inverted modeifn.params.invertedthenn.params.kp = -n.params.kpn.params.ki = -n.params.kin.params.kd = -n.params.kdendreturnnend-- resets algorithm on init or a switch back from manual modefunctionPID:reset()
-- previous valueself.previous = grp.getvalue(self.params.current)
-- reset itermself.iterm = 0-- last running timeself.lasttime = os.time()
-- clamp itermself:clampiterm()
end-- clamps iterm valuefunctionPID: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 valuefunctionPID:setoutput()
localt, object, valueself.output = math.max(self.output, self.params.min)
self.output = math.min(self.output, self.params.max)
value = math.floor(self.output)
localt = type(self.params.output)
-- write to output if object is setift == 'string'ort == 'table'thenift == 'string'thenself.params.output = { self.params.output }
endfor_, outputinipairs(self.params.output) dogrp.write(output, value, dt.scale)
endendend-- algorithm step, returns nil when disabled or no action is required, output value otherwisefunctionPID:run()
localresult-- get manual mode statuslocalmanual = self.params.manualandgrp.getvalue(self.params.manual) orfalse-- in manual mode, do nothingifmanualthenself.running = false-- not in manual, check if reset is required after switching onelseifnotself.runningthenself:reset()
self.running = trueend-- compute new value if not in manual modeifself.runningthen-- get time between previous and current calllocalnow = os.time()
self.deltatime = now - self.lasttimeself.lasttime = now-- run if previous call was at least 1 second agoifself.deltatime > 0thenresult = self:compute()
endendreturnresultend-- computes new output valuefunctionPID:compute()
localcurrent, setpoint, deltasc, deltain, output-- get input valuescurrent = grp.getvalue(self.params.current)
setpoint = grp.getvalue(self.params.setpoint)
-- delta between setpoint and currentdeltasc = setpoint - current-- calculate new itermself.iterm = self.iterm + self.params.ki * self.deltatime * deltascself:clampiterm()
-- delta between current and previous valuedeltain = current - self.previous-- calculate output valueself.output = self.params.kp * deltasc + self.itermself.output = self.output - self.params.kd / self.deltatime * deltain-- write to outputself:setoutput()
-- save previous valueself.previous = currentreturnself.outputend
thank you for your answer,
after this i have the following error
"Common functions:331: attempt to perform arithmetic on local 'setpoint' (a nil value)
stack traceback:
Common functions:331: in function 'compute'
Common functions:315: in function 'run'
"
(10.06.2022, 13:58)admin Wrote: Check that setpoint object address is valid in the pid script.
here is the script
-- init pid algorithm
if not p then
p = PID:init({
current = '32/1/7',
setpoint = '32/1/5',
manual = '32/1/8',
inverted = 0,
output = '32/1/6',
min=0,
max=100,
kp=1,
ki=1,
kd=1
})
end
12.02.2024, 21:42 (This post was last modified: 12.02.2024, 21:43 by manos@dynamitec.)
Hello Admin,
I am trying to realize a constant light control loop with the PID script you have on the website.
I've tried to adjust the kp, ki, kd parameters to make the system stable but there are some issues.
Since this not about temperature control and fluctuations to the measured brightness value in the room can be pretty high due to sunny/cloudy day, the system is unstable. I would like to be able to set a min/max step (like +/- 3%) for the value that is written to the bus each control loop even if the PID calculation is resulting to a big difference from the previous value. In this way I would like to avoid positive or negative jumps in the written value. Another thing is to be able to add something like a deadband or hysteresis to the system so if the actual value = setpoint +/- 20 lux for exmple then don't calculate anything. Something similar of what is done now via the manual variable.
As my lua scripting skills are basic could you or anyone in this forum please help me figure this out and how the new script will be? Below I am attaching the script I am using now:
Long time ago I worked on similar solution (not on LM) and to solve this we had to make lux more stable by making an avg form last few values. Try using something like this https://forum.logicmachine.net/showthrea...1#pid27441