From 81e66dbcd17d12259065a15f7d488c46fb77b272 Mon Sep 17 00:00:00 2001 From: Jens Ahrensfeld Date: Fri, 19 Jun 2026 08:21:47 +0200 Subject: [PATCH] Move PID matplotlib demo harnesses out of production modules temp_controller.py, temp_controller_smith.py, and kalman.py imported matplotlib at module level just to support eyeballed-plot __main__ blocks, coupling the live server's import graph to a GUI plotting lib it never uses at runtime. Relocate those demos (and kalman_eval.py) to scripts/demos/pid/ and strip the now-unused imports/__main__ blocks from the production files. Co-Authored-By: Claude Sonnet 4.6 --- components/pid/kalman.py | 55 ------------ components/pid/temp_controller.py | 76 ---------------- components/pid/temp_controller_smith.py | 87 ------------------ scripts/__init__.py | 0 scripts/demos/__init__.py | 0 scripts/demos/pid/__init__.py | 0 scripts/demos/pid/demo_kalman.py | 56 ++++++++++++ .../demos/pid/demo_kalman_eval.py | 0 scripts/demos/pid/demo_temp_controller.py | 77 ++++++++++++++++ .../demos/pid/demo_temp_controller_smith.py | 90 +++++++++++++++++++ 10 files changed, 223 insertions(+), 218 deletions(-) create mode 100644 scripts/__init__.py create mode 100644 scripts/demos/__init__.py create mode 100644 scripts/demos/pid/__init__.py create mode 100644 scripts/demos/pid/demo_kalman.py rename components/pid/kalman_eval.py => scripts/demos/pid/demo_kalman_eval.py (100%) create mode 100644 scripts/demos/pid/demo_temp_controller.py create mode 100644 scripts/demos/pid/demo_temp_controller_smith.py diff --git a/components/pid/kalman.py b/components/pid/kalman.py index cb90568..c31358f 100644 --- a/components/pid/kalman.py +++ b/components/pid/kalman.py @@ -1,6 +1,5 @@ import numpy as np from numpy.linalg import inv -from matplotlib.pyplot import plot, figure, subplot, grid, show class Kalman: @@ -76,57 +75,3 @@ class Kalman: I = np.eye(self.N) self.P = (I - K * self.H) * P return self.X - - -# Main -if __name__ == '__main__': - dt = 1.0 - - params = { - 'var_P' : 1, - 'var_Q' : 0.0001, - 'var_R' : 0.5 - } - k = Kalman(dt, params) - - _x1 = np.empty(0) - _y1 = np.empty(0) - _x2 = np.empty(0) - _y2 = np.empty(0) - - N = int(1000/dt) - seqn = range(0, N) - - _seqn = range(0, 2*N) - X = np.array([dt, 0]) - for n in seqn: - Z = k.process_measurement(X, 0.5) - # Kalman.print("Z:", Z) - Xp = k.process(Z) - - _x1 = np.append(_x1, Z[0]) - _x2 = np.append(_x2, Z[1]) - _y1 = np.append(_y1, Xp[0]) - _y2 = np.append(_y2, Xp[1]) - - for n in seqn: - X[0] = X[0] + dt - Z = k.process_measurement(X, 0.5) - # Kalman.print("Z:", Z) - Xp = k.process(Z) - - _x1 = np.append(_x1, Z[0]) - _x2 = np.append(_x2, Z[1]) - _y1 = np.append(_y1, Xp[0]) - _y2 = np.append(_y2, Xp[1]) - - figure(1) - subplot(2, 1, 1) - plot(_seqn, _x1, 'bx', _seqn, _y1, '-r', linewidth=1) - grid(True) - subplot(2, 1, 2) - plot(_seqn, _x2, 'bx', _seqn, _y2, '-r', linewidth=1) - grid(True) - show() - - print("End of program") diff --git a/components/pid/temp_controller.py b/components/pid/temp_controller.py index f7146e4..becc777 100644 --- a/components/pid/temp_controller.py +++ b/components/pid/temp_controller.py @@ -1,9 +1,5 @@ -from components.plant.pot import Pot -from matplotlib.pyplot import plot, figure, subplot, grid, show, legend from components.pid import Kalman from components.pid.temp_controller_base import TempControllerBase -from components.pid.tc_constants import * -import numpy as np class TempController(TempControllerBase): @@ -36,75 +32,3 @@ class TempController(TempControllerBase): diff = self.theta_soll_set - self.theta_ist self.process_fsm(diff) self.process_pid(theta_err, heatrate_err) - - -if __name__ == '__main__': - dt = 1.0 - - temp_ist = 0 - temp_soll = 20 - ctrl = TempController(dt, Test.tc_ctrl_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() + 0.0 * np.random.randn() - 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) - 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") diff --git a/components/pid/temp_controller_smith.py b/components/pid/temp_controller_smith.py index e1dbe19..0fdd579 100755 --- a/components/pid/temp_controller_smith.py +++ b/components/pid/temp_controller_smith.py @@ -1,9 +1,7 @@ from components.plant.pot import Pot -from matplotlib.pyplot import plot, figure, subplot, grid, show, legend from components.pid import Kalman from components.pid.temp_controller_base import TempControllerBase from components.pid.tc_constants import * -import numpy as np class TempController(TempControllerBase): @@ -82,88 +80,3 @@ class TempController(TempControllerBase): self.process_fsm(diff) self.process_pid(theta_err, heatrate_err) - - -if __name__ == '__main__': - dt = 1.0 - - temp_soll = 20 - ctrl = TempController(dt, Test.tc_ctrl_params, Test.tc_model_params) - plant = Pot(dt, Test.tc_pot_params) - _y = np.empty(0) - _fb = np.empty(0) - _t = np.empty(0) - _temp_soll = np.empty(0) - _temp_ist_kalman = np.empty(0) - _heatrate_ist_kalman = np.empty(0) - _temp_ist_kalman_plant = np.empty(0) - _heatrate_ist_kalman_plant = np.empty(0) - _temp_ist_kalman_model = np.empty(0) - _heatrate_ist_kalman_model = 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 - - _y = np.append(_y, y) - _fb = np.append(_fb, fb) - _t = np.append(_t, t) - _temp_soll = np.append(_temp_soll, temp_soll) - _temp_ist_kalman_plant = np.append(_temp_ist_kalman_plant, ctrl.theta_ist_plant) - _heatrate_ist_kalman_plant = np.append(_heatrate_ist_kalman_plant, max(-1, min(3, ctrl.dtheta_ist_plant))) - _temp_ist_kalman_model = np.append(_temp_ist_kalman_model, ctrl.theta_ist_model_delay) - _heatrate_ist_kalman_model = np.append(_heatrate_ist_kalman_model, max(-1, min(3, ctrl.dtheta_ist_model_delay))) - t += 1 - - figure(1) - subplot(3, 1, 1) - plot(_t, _temp_ist_kalman_plant, _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_plant, '-b', linewidth=1) - legend(["heatrate"]) - grid(True) - - figure(2) - subplot(2, 1, 1) - plot(_t, _temp_ist_kalman_plant, '-b', _t, _temp_ist_kalman_model, 'r-', linewidth=1) - legend(["plant", "model"]) - grid(True) - subplot(2, 1, 2) - plot(_t, _heatrate_ist_kalman_plant, '-b', _t, _heatrate_ist_kalman_model, '-r', linewidth=1) - legend(["plant", "model"]) - grid(True) - show() - - print("End of program") diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/demos/__init__.py b/scripts/demos/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/demos/pid/__init__.py b/scripts/demos/pid/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/demos/pid/demo_kalman.py b/scripts/demos/pid/demo_kalman.py new file mode 100644 index 0000000..da11e36 --- /dev/null +++ b/scripts/demos/pid/demo_kalman.py @@ -0,0 +1,56 @@ +import numpy as np +from matplotlib.pyplot import plot, figure, subplot, grid, show +from components.pid.kalman import Kalman + + +if __name__ == '__main__': + dt = 1.0 + + params = { + 'var_P' : 1, + 'var_Q' : 0.0001, + 'var_R' : 0.5 + } + k = Kalman(dt, params) + + _x1 = np.empty(0) + _y1 = np.empty(0) + _x2 = np.empty(0) + _y2 = np.empty(0) + + N = int(1000/dt) + seqn = range(0, N) + + _seqn = range(0, 2*N) + X = np.array([dt, 0]) + for n in seqn: + Z = k.process_measurement(X, 0.5) + # Kalman.print("Z:", Z) + Xp = k.process(Z) + + _x1 = np.append(_x1, Z[0]) + _x2 = np.append(_x2, Z[1]) + _y1 = np.append(_y1, Xp[0]) + _y2 = np.append(_y2, Xp[1]) + + for n in seqn: + X[0] = X[0] + dt + Z = k.process_measurement(X, 0.5) + # Kalman.print("Z:", Z) + Xp = k.process(Z) + + _x1 = np.append(_x1, Z[0]) + _x2 = np.append(_x2, Z[1]) + _y1 = np.append(_y1, Xp[0]) + _y2 = np.append(_y2, Xp[1]) + + figure(1) + subplot(2, 1, 1) + plot(_seqn, _x1, 'bx', _seqn, _y1, '-r', linewidth=1) + grid(True) + subplot(2, 1, 2) + plot(_seqn, _x2, 'bx', _seqn, _y2, '-r', linewidth=1) + grid(True) + show() + + print("End of program") diff --git a/components/pid/kalman_eval.py b/scripts/demos/pid/demo_kalman_eval.py similarity index 100% rename from components/pid/kalman_eval.py rename to scripts/demos/pid/demo_kalman_eval.py diff --git a/scripts/demos/pid/demo_temp_controller.py b/scripts/demos/pid/demo_temp_controller.py new file mode 100644 index 0000000..60e3b42 --- /dev/null +++ b/scripts/demos/pid/demo_temp_controller.py @@ -0,0 +1,77 @@ +import numpy as np +from matplotlib.pyplot import plot, figure, subplot, grid, show, legend +from components.plant.pot import Pot +from components.pid.temp_controller import TempController +from components.pid.tc_constants import Test + + +if __name__ == '__main__': + dt = 1.0 + + temp_ist = 0 + temp_soll = 20 + ctrl = TempController(dt, Test.tc_ctrl_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() + 0.0 * np.random.randn() + 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) + 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") diff --git a/scripts/demos/pid/demo_temp_controller_smith.py b/scripts/demos/pid/demo_temp_controller_smith.py new file mode 100644 index 0000000..5dd70dc --- /dev/null +++ b/scripts/demos/pid/demo_temp_controller_smith.py @@ -0,0 +1,90 @@ +import numpy as np +from matplotlib.pyplot import plot, figure, subplot, grid, show, legend +from components.plant.pot import Pot +from components.pid.temp_controller_smith import TempController +from components.pid.tc_constants import Test + + +if __name__ == '__main__': + dt = 1.0 + + temp_soll = 20 + ctrl = TempController(dt, Test.tc_ctrl_params, Test.tc_model_params) + plant = Pot(dt, Test.tc_pot_params) + _y = np.empty(0) + _fb = np.empty(0) + _t = np.empty(0) + _temp_soll = np.empty(0) + _temp_ist_kalman = np.empty(0) + _heatrate_ist_kalman = np.empty(0) + _temp_ist_kalman_plant = np.empty(0) + _heatrate_ist_kalman_plant = np.empty(0) + _temp_ist_kalman_model = np.empty(0) + _heatrate_ist_kalman_model = 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 + + _y = np.append(_y, y) + _fb = np.append(_fb, fb) + _t = np.append(_t, t) + _temp_soll = np.append(_temp_soll, temp_soll) + _temp_ist_kalman_plant = np.append(_temp_ist_kalman_plant, ctrl.theta_ist_plant) + _heatrate_ist_kalman_plant = np.append(_heatrate_ist_kalman_plant, max(-1, min(3, ctrl.dtheta_ist_plant))) + _temp_ist_kalman_model = np.append(_temp_ist_kalman_model, ctrl.theta_ist_model_delay) + _heatrate_ist_kalman_model = np.append(_heatrate_ist_kalman_model, max(-1, min(3, ctrl.dtheta_ist_model_delay))) + t += 1 + + figure(1) + subplot(3, 1, 1) + plot(_t, _temp_ist_kalman_plant, _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_plant, '-b', linewidth=1) + legend(["heatrate"]) + grid(True) + + figure(2) + subplot(2, 1, 1) + plot(_t, _temp_ist_kalman_plant, '-b', _t, _temp_ist_kalman_model, 'r-', linewidth=1) + legend(["plant", "model"]) + grid(True) + subplot(2, 1, 2) + plot(_t, _heatrate_ist_kalman_plant, '-b', _t, _heatrate_ist_kalman_model, '-r', linewidth=1) + legend(["plant", "model"]) + grid(True) + show() + + print("End of program")