Keep the plant/controller model in sync as the Sud advances

SudTask.on_step_changed() set the controller's target temp/rate on
every step, but never touched the real Pot's or the Smith predictor's
internal model's thermal mass (M/C) - those stayed frozen at
brewpi.py's startup DEFAULT_PLANT_PARAMS for the whole brew, even
though grain_mass/water_mass change per step (malt going in, water
boiling off) and demo_sud.py already recomputes them every step via
derive_plant_params(). Production SudTask just never got the same
treatment.

Adds SudTask.apply_plant_params(), called alongside the existing
theta_soll/heatrate_soll push, mirroring the demo. Needs the real Pot
now, so SudTask takes a `pot` constructor arg; set_model_params() is
only called if the configured controller actually defines one (the
"Normal" pid_type doesn't).
This commit is contained in:
2026-06-21 00:05:55 +02:00
parent 13e507e2b5
commit e171892804
2 changed files with 15 additions and 2 deletions
+14 -1
View File
@@ -3,6 +3,7 @@ from tasks import ATask
from ws.message import MsgIo
from utils.value import ChangedFloat
from components import APid, AStirrer
from components.plant import APlant
from components.sud import Sud, SudState
# How close theta_ist needs to be to theta_soll_set to count as "reached".
@@ -10,14 +11,25 @@ TEMP_REACHED_TOLERANCE = 0.2
class SudTask(ATask):
def __init__(self, sud: Sud, tc: APid, stirrer: AStirrer, interval, msg_handler: MsgIo):
def __init__(self, sud: Sud, tc: APid, stirrer: AStirrer, pot: APlant, interval, msg_handler: MsgIo):
ATask.__init__(self, interval)
self.sud = sud
self.tc = tc
self.stirrer = stirrer
self.pot = pot
self.msg_handler = msg_handler
msg_handler.set_recv_handler(self.recv)
def apply_plant_params(self, step):
"""Keeps the real plant's and the controller's internal model's
lumped (M, C) in sync with the step's grain_mass/water_mass, since
those vary over the course of a brew (malt going in, water boiling
off) - mirrors demo_sud.py's apply_plant_params()."""
params = self.sud.derive_plant_params(step.get('grain_mass', 0), step.get('water_mass', 0))
self.pot.set_thermal_params(params['M'], params['C'])
if hasattr(self.tc, 'set_model_params'):
self.tc.set_model_params(params['M'], params['C'])
def apply_stirrer(self, step):
stirrer_cfg = step.get('ramp', step.get('hold', {})).get('stirrer', {})
speed = stirrer_cfg.get('speed', 0)
@@ -35,6 +47,7 @@ class SudTask(ATask):
ramp = step.get('ramp') if step else None
hold = step.get('hold') if step else None
if step is not None:
self.apply_plant_params(step)
if ramp is not None:
self.tc.set_theta_soll(ramp['temp'])
self.tc.set_heatrate_soll(ramp['rate'])