Files
brewpi/components/pid/temp_controller.py
jensandClaude Sonnet 4.6 b3923fb1c0 fix: smooth HOLD→HEAT power transition (bumpless transfer + cascade timing)
Two root causes for the visible power dip at every ramp-to-ramp
step boundary:

1. One-tick cascade delay: heatrate_soll was computed from
   pid_hold.get_y() before pid_hold.process() ran in that tick,
   so the first tick of a new ramp used the stale hold-phase
   output (≈0) as the rate setpoint, driving pid_heat to near
   zero.  Fix: move heatrate_soll computation inside process_pid(),
   after pid_hold.process().

2. Cold-start reset: pid_heat.reset() at HOLD→HEAT discarded
   the hold-phase integral, so pid_heat had to ramp up from
   zero on every new step.  Fix: remove the reset (bumpless
   transfer) — the hold-phase integral is a warm starting point
   that carries the baseline hold power into the new ramp.

Together these eliminate the ~1s dip to near-zero and replace
the slow 20–30s integral ramp-up with an immediate start at
roughly hold-level power, climbing monotonically to the target.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 20:33:12 +02:00

23 lines
738 B
Python

from components.pid.temp_controller_base import TempControllerBase
class TempController(TempControllerBase):
def __init__(self, dt):
TempControllerBase.__init__(self, dt)
# last_theta_ist / theta_ist_filtered / beta / dt all live in the base -
# see TempControllerBase.__init__ and _compute_heatrate().
def process(self):
self._require_params()
self.theta_ist = self.theta_ist_set
self._compute_heatrate(self.theta_ist)
# Compensate for max heat rate to reduce overshoot
hold_scale = 1.0/self.heatrate_soll_set if self.heatrate_soll_set > 0 else 1.0
theta_err = self.theta_soll_set - self.theta_ist
diff = self.theta_soll_set - self.theta_ist
self.process_fsm(diff)
self.process_pid(theta_err, hold_scale)