- remove HeatDiffusion
- use delay line for heat propagation modeling

[Temp Controller]
- remove Kalman
This commit is contained in:
2026-06-19 14:21:23 +02:00
parent 81e66dbcd1
commit 5c7cffa3a7
4 changed files with 84 additions and 49 deletions
+6 -13
View File
@@ -1,25 +1,18 @@
from components.pid import Kalman
from components.pid.temp_controller_base import TempControllerBase from components.pid.temp_controller_base import TempControllerBase
class TempController(TempControllerBase): class TempController(TempControllerBase):
def __init__(self, dt, params): def __init__(self, dt, params):
TempControllerBase.__init__(self, dt, params) TempControllerBase.__init__(self, dt, params)
self.kalman = Kalman(dt, params['Kalman']) self.dt = dt
self.last_theta_ist = 20
def init_kalman(self, value): self.heatrate_ist = 0
self.kalman.initial((value, 0))
def process(self): def process(self):
# Process Kalman # Process Kalman
if self.use_kalman:
Z = self.kalman.process_measurement((self.theta_ist_set, 0), 0.0)
xp = self.kalman.process(Z)
self.theta_ist = xp[0, 0]
self.heatrate_ist = xp[1, 0] * 60
else:
self.theta_ist = self.theta_ist_set self.theta_ist = self.theta_ist_set
self.heatrate_ist = self.heatrate_ist_set heatrate = (self.theta_ist - self.last_theta_ist)/self.dt*60
self.heatrate_ist = heatrate
self.last_theta_ist = self.theta_ist
# Compensate for max heat rate to reduce overshoot # Compensate for max heat rate to reduce overshoot
if self.heatrate_soll_set > 0: if self.heatrate_soll_set > 0:
-5
View File
@@ -19,14 +19,10 @@ class TempControllerBase(APid):
self.thresholds = {**DEFAULT_THRESHOLDS, **params.get('Thresholds', {})} self.thresholds = {**DEFAULT_THRESHOLDS, **params.get('Thresholds', {})}
self.y = -1 self.y = -1
self.state = States.INIT self.state = States.INIT
self.use_kalman = True
self.pid_hold.set_params(params['Hold']) self.pid_hold.set_params(params['Hold'])
self.pid_rate.set_params(params['Heat']) self.pid_rate.set_params(params['Heat'])
self.is_startup = True self.is_startup = True
def init_kalman(self, value):
raise NotImplementedError
def on_state_entered(self, state): def on_state_entered(self, state):
pass pass
@@ -37,7 +33,6 @@ class TempControllerBase(APid):
self.theta_ist_set = value self.theta_ist_set = value
if self.is_startup: if self.is_startup:
self.is_startup = False self.is_startup = False
self.init_kalman(value)
def get_theta_ist(self): def get_theta_ist(self):
return self.theta_ist return self.theta_ist
+41 -18
View File
@@ -1,6 +1,6 @@
from components.aplant import APlant from components.aplant import APlant
from components.plant.heat_diffusion import HeatDiffusion from components.plant.heat_diffusion import HeatDiffusion
from components.plant import delay
class Pot(APlant): class Pot(APlant):
def __init__(self, dt, params, theta_amb=20): def __init__(self, dt, params, theta_amb=20):
@@ -10,35 +10,57 @@ class Pot(APlant):
self.e = 0 self.e = 0
self.x = 0 self.x = 0
# Plant power gain (efficiency)
self.gain = params['gain'] self.gain = params['gain']
self.C = params['C']
self.M = params['M']
self.L = params['L']
self.Td = params['Td']
self.kn = params['kn']
self.temp = params['theta']
self.temp_intermediate = params['theta']
# Plant specific thermal capacity [W*s/(kg*K)]
self.C = params['C']
# Plant mass [kg]
self.M = params['M']
# Plant energy loss coefficient [W/(kg*K)]
# Negative input power as a function of plant mass and temperature difference T_plant and T_ambient
# P_loss = L * Mass * (T_plant - T_ambient)
self.L = params['L']
# Energy transport propagation delay
self.Td = params['Td']
# TBD
self.kn = params['kn']
# Plant temperature [°C]
self.temp = params['theta']
# Ambient temperature [°C]
self.theta_amb = theta_amb self.theta_amb = theta_amb
# Set power [W]
self.power_set = 0 self.power_set = 0
self.delay = HeatDiffusion(self.dt, self.Td, params['theta'])
# Plant delay [s]
self.delay = delay.Delay(dt, self.Td, 0)
self.p_in = 0
self.p_pot = 0
def initial(self, temp): def initial(self, temp):
self.temp_intermediate = temp
self.temp = temp self.temp = temp
self.delay.initial(temp)
def activate(self, enable): def activate(self, enable):
pass pass
def process(self): def process(self):
power = self.gain * self.power_set self.p_in = self.gain * self.power_set
leak = 1/self.M * self.L * (self.theta_amb - self.temp)/self.theta_amb self.delay.put(self.p_in)
self.temp_intermediate += (power / (self.M * self.C) + leak) * self.dt
# Delay p_loss = self.L * self.M * (self.temp - self.theta_amb)
self.temp = self.delay.process(self.temp_intermediate) self.p_pot = self.delay.get() - p_loss
print(f"p_loss: {p_loss}")
print(f"theta_amb: {self.theta_amb}")
print(f"temp: {self.temp}")
self.temp = min(100, self.temp + self.p_pot/(self.M * self.C) * self.dt)
def is_activated(self): def is_activated(self):
return True return True
@@ -52,5 +74,6 @@ class Pot(APlant):
def get_temperature(self): def get_temperature(self):
return self.temp return self.temp
def get_temperature_intermediate(self): def get_p_pot(self):
return self.temp_intermediate return self.p_pot
+36 -12
View File
@@ -2,23 +2,48 @@ import numpy as np
from matplotlib.pyplot import plot, figure, subplot, grid, show, legend from matplotlib.pyplot import plot, figure, subplot, grid, show, legend
from components.plant.pot import Pot from components.plant.pot import Pot
from components.pid.temp_controller import TempController from components.pid.temp_controller import TempController
from components.pid.tc_constants import Test
if __name__ == '__main__': if __name__ == '__main__':
ctrl_params = {
"Hold": {
"kp": 0.4,
"ki": 0.0,
"kd": 0.0,
"kt": 0.0
},
"Heat": {
"kp": 0.08,
"ki": 0.008,
"kd": 0.0,
"kt": 1.5
}
}
plant_params = {
"theta" : 20,
"C" : 4190,
"M" : 20,
"L" : 0.2,
"Td" : 30,
"kn" : 0.2,
"gain" : 1.0
}
dt = 1.0 dt = 1.0
k_noise = 0.001
temp_ist = 0 temp_ist = 0
temp_soll = 20 temp_soll = 20
ctrl = TempController(dt, Test.tc_ctrl_params) heatrate_soll = 1.25
plant = Pot(dt, Test.tc_pot_params) ctrl = TempController(dt, ctrl_params)
_temp_ist = np.empty(0) plant = Pot(dt, plant_params)
_temp_soll = np.empty(0)
_y = np.empty(0) _y = np.empty(0)
_fb = np.empty(0) _fb = np.empty(0)
_t = np.empty(0) _t = np.empty(0)
_heatrate_ist_kalman = np.empty(0) _temp_soll = np.empty(0)
_temp_ist_kalman = np.empty(0) _heatrate_ist = np.empty(0)
_temp_ist = np.empty(0)
a = 0.5 a = 0.5
fb = 0 fb = 0
rho = 0.02 rho = 0.02
@@ -30,7 +55,7 @@ if __name__ == '__main__':
hold_counter = temp['Duration'] hold_counter = temp['Duration']
hold = False hold = False
ctrl.set_theta_soll(temp_soll) ctrl.set_theta_soll(temp_soll)
ctrl.set_heatrate_soll(1.0) ctrl.set_heatrate_soll(heatrate_soll)
while True: while True:
if hold: if hold:
@@ -38,7 +63,7 @@ if __name__ == '__main__':
break break
hold_counter -= 1 hold_counter -= 1
plant.process() plant.process()
temp_ist = plant.get_temperature() + 0.0 * np.random.randn() temp_ist = plant.get_temperature() + k_noise * np.random.randn()
ctrl.set_theta_ist(temp_ist) ctrl.set_theta_ist(temp_ist)
ctrl.process() ctrl.process()
@@ -55,8 +80,7 @@ if __name__ == '__main__':
_y = np.append(_y, y) _y = np.append(_y, y)
_fb = np.append(_fb, fb) _fb = np.append(_fb, fb)
_t = np.append(_t, t) _t = np.append(_t, t)
_heatrate_ist_kalman = np.append(_heatrate_ist_kalman, max(-1, min(3, ctrl.heatrate_ist))) _heatrate_ist = np.append(_heatrate_ist, max(-1, min(3, ctrl.heatrate_ist)))
_temp_ist_kalman = np.append(_temp_ist, ctrl.theta_ist)
t += 1 t += 1
figure(1) figure(1)
@@ -69,7 +93,7 @@ if __name__ == '__main__':
legend(["y", "pot"]) legend(["y", "pot"])
grid(True) grid(True)
subplot(3, 1, 3) subplot(3, 1, 3)
plot(_t, _heatrate_ist_kalman, '-b', linewidth=1) plot(_t, _heatrate_ist, '-b', linewidth=1)
legend(["heatrate"]) legend(["heatrate"])
grid(True) grid(True)
show() show()