forked from Manuel83/cbpi-PIDArduino
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path__init__.py
104 lines (86 loc) · 3.41 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import logging
import time
from modules import cbpi
from modules.core.controller import KettleController
from modules.core.props import Property
@cbpi.controller
class PIDArduinoPowerOutput(KettleController):
a_p = Property.Number("P", True, 0)
b_i = Property.Number("I", True, 0)
c_d = Property.Number("D", True, 0)
d_max_out = Property.Number("max. output %", True, 100)
def run(self):
sampleTime = 5
wait_time = 5
p = float(self.a_p)
i = float(self.b_i)
d = float(self.c_d)
maxout = float(self.d_max_out)
pid = PIDArduinoPowerOutput(sampleTime, p, i, d, 0, maxout)
self.heater_on()
self.sleep(2)
while self.is_running():
heat_percent = int(pid.calc(self.get_temp(), self.get_target_temp()))
self.actor_power(heat_percent)
self.sleep(5)
self.heater_off()
# Based on Arduino PID Library
# See https://github.com/br3ttb/Arduino-PID-Library
class PIDArduinoPowerOutput(object):
def __init__(self, sampleTimeSec, kp, ki, kd, outputMin=float('-inf'),
outputMax=float('inf'), getTimeMs=None):
if kp is None:
raise ValueError('kp must be specified')
if ki is None:
raise ValueError('ki must be specified')
if kd is None:
raise ValueError('kd must be specified')
if sampleTimeSec <= 0:
raise ValueError('sampleTimeSec must be greater than 0')
if outputMin >= outputMax:
raise ValueError('outputMin must be less than outputMax')
self._logger = logging.getLogger(type(self).__name__)
self._Kp = kp
self._Ki = ki * sampleTimeSec
self._Kd = kd / sampleTimeSec
self._sampleTime = sampleTimeSec * 1000
self._outputMin = outputMin
self._outputMax = outputMax
self._iTerm = 0
self._lastInput = 0
self._lastOutput = 0
self._lastCalc = 0
if getTimeMs is None:
self._getTimeMs = self._currentTimeMs
else:
self._getTimeMs = getTimeMs
def calc(self, inputValue, setpoint):
now = self._getTimeMs()
if (now - self._lastCalc) < self._sampleTime:
return self._lastOutput
# Compute all the working error variables
error = setpoint - inputValue
dInput = inputValue - self._lastInput
# In order to prevent windup, only integrate if the process is not saturated
if self._lastOutput < self._outputMax and self._lastOutput > self._outputMin:
self._iTerm += self._Ki * error
self._iTerm = min(self._iTerm, self._outputMax)
self._iTerm = max(self._iTerm, self._outputMin)
p = self._Kp * error
i = self._iTerm
d = -(self._Kd * dInput)
# Compute PID Output
self._lastOutput = p + i + d
self._lastOutput = min(self._lastOutput, self._outputMax)
self._lastOutput = max(self._lastOutput, self._outputMin)
# Log some debug info
self._logger.debug('P: {0}'.format(p))
self._logger.debug('I: {0}'.format(i))
self._logger.debug('D: {0}'.format(d))
self._logger.debug('output: {0}'.format(self._lastOutput))
# Remember some variables for next time
self._lastInput = inputValue
self._lastCalc = now
return self._lastOutput
def _currentTimeMs(self):
return time.time() * 1000