fix: allow gentle negative HOLD floor to fix steady-state overshoot

Testing the windup-fix rework live against sude/sud_0030.json surfaced a
distinct bug: a HOLD overshoot from grain-fill-in cooling never decayed -
process_pid()'s pid_outer_y floor clamped to exactly 0.0, so pid_inner
fought the pot's own ambient loss to hold the overshot temperature flat
instead of declining back to setpoint (see docs/overshoot2.png).

Replace the hardcoded 0.0 floor with a configurable Outer.y_hold_min
(default 0.0, backward compatible), set to -0.1 in config.json, both
.tpl templates, and the demo scripts - small enough to avoid
reintroducing the bb5af3c limit cycle while letting HOLD request a
gentle decline matching passive ambient cooling.

Adds TestHoldOvershootRecoversToSetpoint and documents the finding in
docs/overshoot_hold_windup.md's Follow-up section and
components/pid/TODO.md. Confirmed against a live sud_0030 re-run, not
just the unit test.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M2ierBoxW3v7nUbDw3M2pE
This commit is contained in:
2026-07-06 08:19:10 +02:00
co-authored by Claude Sonnet 5
parent 11c3d92f25
commit 2d032bf9b2
11 changed files with 274 additions and 13 deletions
+19
View File
@@ -99,6 +99,25 @@ git history for `temp_controller.py`/`temp_controller_smith.py`).
cases) was added under `tests/components/pid/` — see the "No automated
tests" item above.
- [x] **HOLD overshoot was a permanent steady-state offset, not just a
transient windup.** Found testing the rework against a real Sud run
(`sude/sud_0030.json`): a grain-fill-in disturbance during a `HOLD`
overshot by ~0.4°C (under the `HoldCool` threshold, so the FSM stayed in
`HOLD`) and then never converged back — `temp_ist` sat in a 55.3-55.4°C
band for the rest of the 20-minute hold instead of returning to 55.0.
Cause: `process_pid()`'s HOLD-state floor on `pid_outer_y` clamped to
exactly `0.0`, so once overshot, `heatrate_soll` = 0 ("hold flat") and
`pid_inner` actively fought the pot's own ambient loss to keep the
overshot temperature flat instead of declining back to setpoint. Fixed:
the floor is now a configurable `Outer.y_hold_min` (default `0.0`,
backward compatible), set to `-0.1` in `config.json`/both `.tpl`
templates, letting HOLD request a gentle decline that roughly matches
passive ambient cooling without reopening the `bb5af3c` limit cycle
(fully unclamped negative). See the "Follow-up" section in
`docs/overshoot_hold_windup.md` and
`TestHoldOvershootRecoversToSetpoint` in
`tests/components/pid/test_temp_controller_closed_loop.py`.
- [ ] **`kalman.py` is now dead code in production.** Neither
`temp_controller.py` nor `temp_controller_smith.py` uses `Kalman`
anymore; the only remaining references are