-
Notifications
You must be signed in to change notification settings - Fork 2
/
pid.py
120 lines (101 loc) · 4.55 KB
/
pid.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import time, copy
from brewTimer import brewTimer
class pid:
def __init__(self, pvMin, pvMax):
self.pvMin = pvMin
self.pvMax = pvMax
self.mode = "Manual"
self.lastMode = "Manual"
self.cv = 18.9
self.initCv = 10
self.time = [0.0, 0.0]
self.error = [0.0,0.0]
self.pv = [0.0, 0.0]
self.dPv = [0.0, 0.0, 0.0]
self.initTimer = brewTimer(0.05)
self.dbStatus = "Null"
def doCalc(self, pv, sp, kp, ki, kd, cvMin, cvMax, db):
self.pv.pop(0) #remove the oldest pv
self.pv.append(pv) #add the latest pv to the list
self.error.pop(0) #remove the oldest error
error = (sp - self.pv[1]) * 100.0 / (self.pvMax - self.pvMin) #calculate error in % of span
self.error.append(error) #add the latest error to the list
dPv = (self.pv[0] - self.pv[1]) * 100.0 /(self.pvMax - self.pvMin) #calculate dPv in % of span
self.dPv.pop(0) #remove the oldest dPv
self.dPv.append(dPv)
dE = self.error[1]-self.error[0] #calculate the dE
self.time.pop(0) #remove the last time value from the list
self.time.append(time.time())
dT = self.time[1] - self.time[0] #calculate the dT
#Zero crossing DeadBand
if self.dbStatus == "Low" and pv >= sp:
self.dbStatus = "Active"
if self.dbStatus == "High" and pv <= sp:
self.dbStatus = "Active"
if pv < (sp - db):
self.dbStatus = "Low"
if pv > (sp + db):
self.dbStatus = "High"
if self.time[0] <> 0.0 and self.mode == "Auto":
dP = kp * dE
dI = ki * error * dT / 60.0
dD = kd * (-dPv + 2 * self.dPv[1] - self.dPv[0]) * 60.0 / dT
# print "self.cv ", self.cv, ", dP ", dP, ", dI ", dI, ", dD ", dD
if self.dbStatus != "Active":
self.cv += dP + dI + dD
#Check the limits of the CV
if self.cv > cvMax:
self.cv = copy.copy(cvMax)
if self.cv < cvMin:
self.cv = copy.copy(cvMin)
#Restore the mode to its' previous state after the initialization timer is done
if self.mode == "Initialize" and self.initTimer.isDone():
self.mode = self.lastMode
self.cv = self.initCv
self.error = [0.0,0.0]
self.dPv = [0.0,0.0,0.0]
return self.cv
def getMode(self):
return self.mode
def cmdAuto(self):
self.mode = "Auto"
def cmdManual(self, cv):
self.mode = "Manual"
self.cv = cv
self.error = [0.0,0.0]
self.dPv = [0.0,0.0,0.0]
def cmdInitialize(self, startCv, endCv):
self.lastMode = self.mode
self.mode = "Initialize"
self.cv = startCv
self.initCv = endCv
self.error = [0.0,0.0]
self.dPv = [0.0,0.0,0.0]
#Start 3 second initialization timer
self.initTimer = brewTimer(0.05)
class sim:
def __init__(self):
self.temp = 100.0
self.heatRate = 40.0 #Max heat rate in temp / calc
self.coolRate = .2 #cooling rate constant
self.ambTemp = 32 #ambient temperature
self.cvList = []
while len(self.cvList) < 25: #preset the heat list with 25 deg celcius
self.cvList.append(0.0)
def getTemp(self,cv):
#remove the last value and add a new value to the cvList to form a moving average.
self.cvList.pop(0)
self.cvList.append(cv)
#Calculate the average of the cvList
total = 0.0
for value in self.cvList:
total += value
cvMave = total / len(self.cvList) #calculate the cv moving average
dTempHeat = self.heatRate * cvMave / 100.0
dTempCool = self.coolRate * (self.temp - self.ambTemp)
self.temp = self.temp + dTempHeat - dTempCool
if self.temp > 212.0:
self.temp = 212.0
if self.temp < 32.0:
self.temp = 32.0
return self.temp