Refresh README and design-backlog docs against current code
Both TODO.md backlogs and the README's architecture section still described the Kalman-filter/heat-diffusion design that's since been replaced by the delay-line Pot model and Kalman-free Smith predictor. Check off the items that rewrite already fixed (heat-loss units, transport delay vs. single-pole lag, three-Kalman-tuning), note how the kn/sensor-noise item was resolved differently than proposed, and add newly-spotted issues: brewpi.py wiring set_model_power unconditionally (breaks pid_type "Normal"), kalman.py being dead code in production, stale Kalman/kn keys in the config templates, and debug print()s left in Pot.process(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+40
-42
@@ -1,51 +1,49 @@
|
||||
# components/plant design backlog
|
||||
|
||||
Findings from reviewing `pot.py`'s water-bath model against real-plant
|
||||
behavior (sim/real control mismatch reported by user). Not yet acted on.
|
||||
behavior (sim/real control mismatch reported by user), refreshed against
|
||||
the current `delay`-line rewrite of `Pot`.
|
||||
|
||||
- [ ] **Heat-loss term has wrong units.** `pot.py:37`:
|
||||
`leak = 1/self.M * self.L * (self.theta_amb - self.temp)/self.theta_amb`.
|
||||
The power term right above it is `power/(self.M * self.C)` (W /
|
||||
(J/K) -> K/s). `leak` should follow the same pattern —
|
||||
`self.L * (self.theta_amb - self.temp) / (self.M * self.C)` — but
|
||||
instead it's missing `/C` and divides by `theta_amb` (~20) instead,
|
||||
which isn't a physically meaningful normalization. With
|
||||
`C=4190` this suppresses ambient heat loss by ~3-4 orders of
|
||||
magnitude versus what the power term implies, so the simulated pot
|
||||
barely cools at all. The Smith predictor's internal model is also a
|
||||
`Pot` instance with the identical bug, so this never showed up in
|
||||
sim-vs-sim testing — only against the real plant. Likely the
|
||||
primary cause of the sim/real control mismatch.
|
||||
- [x] **Heat-loss term has wrong units.** Fixed by the `Pot` rewrite:
|
||||
`process()` now computes `p_loss = self.L * self.M * (self.temp -
|
||||
self.theta_amb)` in Watts and applies `self.temp += (self.delay.get()
|
||||
- p_loss) / (self.M * self.C) * self.dt` — the same `/(M*C)`
|
||||
normalization for both the gain and loss terms, instead of the old
|
||||
mismatched `leak = 1/self.M * self.L * (...)/ self.theta_amb`.
|
||||
|
||||
- [ ] **`kn` is loaded but never applied.** `pot.py:18` reads
|
||||
`params['kn']` but nothing in `process()`/`get_temperature()` uses
|
||||
it. Git history shows it used to add Gaussian sensor noise
|
||||
(`self.kn * np.random.normal(...)`) to the output, since commented
|
||||
out and then dropped during the heat-diffusion refactor. Sim is
|
||||
currently fully deterministic/noise-free, making the Kalman filter
|
||||
and PID derivative term look better-behaved in sim than they will
|
||||
against a real noisy sensor. Re-enable as measurement noise (and
|
||||
decide if process noise is also wanted).
|
||||
- [x] **Thermal lag modeled as single-pole lag, not transport delay.**
|
||||
Fixed: `Pot` no longer uses `HeatDiffusion`'s `alpha=dt/Td`
|
||||
exponential smoothing (that module was deleted). It now delays the
|
||||
input power through `plant/delay.py`'s `Delay` (true dead time)
|
||||
before integrating it, matching what this item recommended.
|
||||
|
||||
- [ ] **`theta_amb` is hardcoded, not configured.** `brewpi.py:40`
|
||||
passes a literal `20` instead of reading it from config or a live
|
||||
ambient sensor. Real ambient temperature drifts and is never
|
||||
modeled as a disturbance, so the controller's robustness to it is
|
||||
untested.
|
||||
- [x] **`kn` is loaded but never applied.** Resolved differently than
|
||||
originally proposed: `kn` was removed from `Pot` entirely rather than
|
||||
wired up as plant-side measurement noise. Sensor noise is now
|
||||
modeled one layer up, in `TempSensorSim` (`temp_offset`/`variance`,
|
||||
wired from `brewpi.py`'s `TempSensorFactory.create(..., temp_offset=
|
||||
-0.15, variance=0.01)`), not in the plant model. Note: `config.json.templ`
|
||||
and `config.json.sim` still carry stale `Model.kn`/`Plant.kn` keys
|
||||
that nothing reads anymore — harmless but worth deleting (see
|
||||
`components/pid/TODO.md`'s config-validation item).
|
||||
|
||||
- [ ] **Thermal lag modeled as single-pole lag, not transport delay.**
|
||||
`HeatDiffusion` (`heat_diffusion.py`) implements `alpha=dt/Td`
|
||||
exponential smoothing, not true dead time. There's already a
|
||||
`Delay` class (`plant/delay.py`) for pure transport delay, dropped
|
||||
in favor of `HeatDiffusion` in commit `16cb3d3`. If the real pot's
|
||||
heater-to-sensor coupling behaves more like dead time (heat must
|
||||
propagate through the water before reaching the sensor) than a
|
||||
single lag, `Td` won't represent the same dynamic in sim vs. reality,
|
||||
and the Smith predictor's delay compensation (which assumes `Td`
|
||||
matches the real plant) will be off.
|
||||
- [ ] **`theta_amb` is hardcoded, not configured.** Still true —
|
||||
`brewpi.py`: `pot = Pot(DT, pot_params, 20)` passes a literal `20`
|
||||
instead of reading it from config or a live ambient sensor. Real
|
||||
ambient temperature drifts and is never modeled as a disturbance, so
|
||||
the controller's robustness to it is untested.
|
||||
|
||||
- [ ] **No actuator/process realism.** No heater power saturation
|
||||
enforced inside `Pot` itself (handled ad hoc in test harnesses), no
|
||||
evaporation/lid heat loss, no varying thermal mass `M` as volume
|
||||
- [ ] **No actuator/process realism.** Partially improved: `process()`
|
||||
now clamps `self.temp` to `min(100, ...)` (won't simulate past
|
||||
boiling), but there's still no heater power saturation enforced
|
||||
inside `Pot` itself (handled ad hoc in demo harnesses), no
|
||||
evaporation/lid heat loss, and no varying thermal mass `M` as volume
|
||||
changes (e.g. boil-off, adding water/grain) — `M` is a fixed
|
||||
constant for the whole simulation.
|
||||
constant for the whole simulation.
|
||||
|
||||
- [ ] **Debug `print()`s left in `Pot.process()`.** Every call prints
|
||||
`p_loss`, `theta_amb`, and `temp` unconditionally — this runs once
|
||||
per simulated/real tick (e.g. every `Controller.dt`), spamming stdout
|
||||
in both the server and every demo script. Looks like leftover
|
||||
debugging from the heat-loss-units fix; remove or gate behind a
|
||||
debug flag.
|
||||
|
||||
Reference in New Issue
Block a user