Files
brewpi/components/pid/temp_controller_smith.py
T
2021-10-12 12:07:36 +02:00

237 lines
6.7 KiB
Python
Executable File

from components.plant.pot import Pot, HeatDiffusion
from matplotlib.pyplot import plot, figure, subplot, grid, show, legend
from components.pid import Pid, Kalman
from components import APid
import numpy as np
from enum import Enum
from components.pid.tc_constants import *
class States(Enum):
IDLE = 0,
HEAT = 1,
HOLD = 2
class TempController(APid):
def __init__(self, dt, params, model_params):
APid.__init__(self)
self.pid_hold = Pid(dt)
self.pid_rate = Pid(dt)
self.theta_ist_set = 0
self.theta_soll_set = 0
self.heatrate_ist_set = 0
self.heatrate_soll_set = 1.0
self.heatrate_soll = 1.0
self.theta_ist = 0
self.heatrate_ist = 0
self.params = params
self.model_params = model_params
self.kalman_model = Kalman(dt, params['Kalman'])
self.kalman_model_delay = Kalman(dt, params['Kalman'])
self.kalman_plant = Kalman(dt, params['Kalman'])
self.y = -1
self.state = States.IDLE
self.use_kalman = True
self.pid_hold.set_params(params['Hold'])
self.pid_rate.set_params(params['Heat'])
self.model = Pot(dt, model_params)
self.is_startup = True
def set_theta_ist(self, value):
self.theta_ist_set = value
if self.is_startup:
self.is_startup = False
self.kalman_model.initial((value, 0))
self.kalman_model_delay.initial((value, 0))
self.kalman_plant.initial((value, 0))
def get_theta_ist(self):
return self.theta_ist
def set_heatrate_ist(self, value):
self.heatrate_ist_set = value
def get_heatrate_ist(self):
return self.heatrate_ist
def set_theta_soll(self, value):
self.theta_soll_set = value
def get_theta_soll(self):
return self.theta_soll
def get_theta_soll_set(self):
return self.theta_soll_set
def set_heatrate_soll(self, value):
self.heatrate_soll_set = value
def get_heatrate_soll(self):
return self.heatrate_soll
def get_heatrate_soll_set(self):
return self.heatrate_soll_set
def process(self):
# Process Kalman of Model
k_model = self.kalman_model.process_measurement((self.model.get_temperature_intermediate(), 0), 0.0)
xp_model = self.kalman_model.process(k_model)
theta_ist_model = xp_model[0, 0]
heatrate_ist_model = xp_model[1, 0] * 60
# Process Kalman of delayed Model
k_model_delay = self.kalman_model_delay.process_measurement((self.model.get_temperature(), 0), 0.0)
xp_model_delay = self.kalman_model_delay.process(k_model_delay)
theta_ist_model_delay = xp_model_delay[0, 0]
dtheta_ist_model_delay = xp_model_delay[1, 0] * 60
# Process Kalman of Plant
Z_plant = self.kalman_plant.process_measurement((self.theta_ist_set, 0), 0.0)
xp_plant = self.kalman_plant.process(Z_plant)
theta_ist_plant = xp_plant[0, 0]
heatrate_ist_plant = xp_plant[1, 0] * 60
# Compensate for max heat rate to reduce overshoot
if self.heatrate_soll_set > 0:
self.pid_hold.scale(1.0/self.heatrate_soll_set)
self.heatrate_soll = self.heatrate_soll_set * self.pid_hold.get_y()
self.theta_ist = theta_ist_plant
self.heatrate_ist = heatrate_ist_plant
print ("Model : T_ist={:2.2f}, dT_ist={:2.2f}".format(theta_ist_model, heatrate_ist_model))
print ("Model*z-1: T_ist={:2.2f}, dT_ist={:2.2f}".format(theta_ist_model_delay, dtheta_ist_model_delay))
print ("Plant : T_ist={:2.2f}, dT_ist={:2.2f}".format(theta_ist_plant, heatrate_ist_plant))
if 0:
theta_err = self.theta_soll_set - (theta_ist_plant - theta_ist_model_delay + theta_ist_model)
else:
theta_err = self.theta_soll_set - theta_ist_plant
heatrate_err = self.heatrate_soll - (heatrate_ist_plant - dtheta_ist_model_delay + heatrate_ist_model)
diff = self.theta_soll_set - self.theta_ist
self.process_fsm(diff)
self.process_pid(theta_err, heatrate_err)
def process_fsm(self, diff):
# Process state
state_next = self.state
if self.state == States.IDLE:
if diff >= THRESH_IDLE_HEAT:
state_next = States.HEAT
self.pid_rate.reset()
elif diff >= -THRESH_IDLE_HOLD:
state_next = States.HOLD
self.pid_rate.reset()
elif self.state == States.HOLD:
if diff >= THRESH_HOLD_HEAT:
state_next = States.HEAT
self.pid_rate.reset()
elif diff <= -THRESH_HOLD_IDLE:
state_next = States.IDLE
elif self.state == States.HEAT:
if diff <= -THRESH_HEAT_IDLE:
state_next = States.IDLE
elif diff <= THRESH_HEAT_HOLD:
state_next = States.HOLD
self.pid_hold.reset()
if state_next != self.state:
self.state = state_next
print("New state = {}".format(state_next))
if state_next == States.HEAT:
self.model.initial(self.theta_ist)
self.kalman_model.initial((self.theta_ist, 0))
self.kalman_model_delay.initial((self.theta_ist, 0))
def process_pid(self, theta_err, heatrate_err):
self.pid_hold.process(theta_err, -self.theta_ist)
self.pid_rate.process(heatrate_err, -self.heatrate_ist)
if self.state == States.IDLE:
self.y = 0
else:
self.y = self.pid_rate.get_y()
self.model.set_power(max(0, 3500 * self.y))
self.model.process()
def get_power(self):
return self.y
if __name__ == '__main__':
dt = 1.0
temp_ist = 0
temp_soll = 20
ctrl = TempController(dt, Test.tc_ctrl_params, Test.tc_model_params)
plant = Pot(dt, Test.tc_pot_params)
_temp_ist = np.empty(0)
_temp_soll = np.empty(0)
_y = np.empty(0)
_fb = np.empty(0)
_t = np.empty(0)
_heatrate_ist_kalman = np.empty(0)
_temp_ist_kalman = np.empty(0)
a = 0.5
fb = 0
rho = 0.02
temps = [{'Temp': 20, 'Duration': 1000}, {'Temp': 40, 'Duration': 1000}, {'Temp': 50, 'Duration': 1000}, {'Temp': 60, 'Duration': 1000}, {'Temp': 70, 'Duration': 1000}, {'Temp': 80, 'Duration': 1000}, {'Temp': 78, 'Duration': 1000}]
t = 0
for temp in temps:
temp_soll = temp['Temp']
hold_counter = temp['Duration']
hold = False
ctrl.set_theta_soll(temp_soll)
ctrl.set_heatrate_soll(1.0)
while True:
if hold:
if hold_counter == 0:
break
hold_counter -= 1
plant.process()
temp_ist = plant.get_temperature()
ctrl.set_theta_ist(temp_ist)
ctrl.process()
y = 3500*ctrl.get_power()
power = max(0, y)
plant.set_power(power)
fb = plant.get_power()
if abs(temp_ist - temp_soll) < 0.1:
hold = True
temp_ist -= rho
_temp_ist = np.append(_temp_ist, temp_ist)
_temp_soll = np.append(_temp_soll, temp_soll)
_y = np.append(_y, y)
_fb = np.append(_fb, fb)
_t = np.append(_t, t)
_heatrate_ist_kalman = np.append(_heatrate_ist_kalman, max(-1, min(3, ctrl.heatrate_ist)))
_temp_ist_kalman = np.append(_temp_ist, ctrl.theta_ist_set)
t += 1
figure(1)
subplot(3, 1, 1)
plot(_t, _temp_ist, _t, _temp_soll, 'r-', linewidth=1)
legend(["ist", "soll"])
grid(True)
subplot(3, 1, 2)
plot(_t, _y, '-b', _t, _fb, '-r', linewidth=1)
legend(["y", "pot"])
grid(True)
subplot(3, 1, 3)
plot(_t, _heatrate_ist_kalman, '-b', linewidth=1)
legend(["heatrate"])
grid(True)
show()
print("End of program")