diff --git a/components/pid/temp_controller.py b/components/pid/temp_controller.py index 6474359..9cde7f4 100644 --- a/components/pid/temp_controller.py +++ b/components/pid/temp_controller.py @@ -16,10 +16,7 @@ class TempController(TempControllerBase): # 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 - self.heatrate_soll = self.heatrate_soll_set * self.pid_hold.get_y() theta_err = self.theta_soll_set - self.theta_ist - heatrate_err = self.heatrate_soll - self.heatrate_ist - diff = self.theta_soll_set - self.theta_ist self.process_fsm(diff) - self.process_pid(theta_err, heatrate_err, hold_scale) + self.process_pid(theta_err, hold_scale) diff --git a/components/pid/temp_controller_base.py b/components/pid/temp_controller_base.py index 20bbf20..f58d68d 100644 --- a/components/pid/temp_controller_base.py +++ b/components/pid/temp_controller_base.py @@ -130,8 +130,14 @@ class TempControllerBase(TempControllerFsm, APid): def get_heatrate_soll_set(self): return self.heatrate_soll_set - def process_pid(self, theta_err, heatrate_err, hold_scale=1.0): + def process_pid(self, theta_err, hold_scale=1.0): self.pid_hold.process(theta_err, -self.theta_ist, hold_scale) + # Compute heatrate_soll HERE, after pid_hold ticks, so it uses + # this tick's output rather than last tick's — eliminates the + # one-tick cascade delay that caused p_set to dip near zero on + # the first tick of a new ramp (HOLD→HEAT transition). + self.heatrate_soll = self.heatrate_soll_set * self.pid_hold.get_y() + heatrate_err = self.heatrate_soll - self.heatrate_ist # Only the PID actually driving y is advanced - otherwise the # inactive one (e.g. pid_heat while COOL has pid_cool driving) diff --git a/components/pid/temp_controller_fsm.py b/components/pid/temp_controller_fsm.py index 08b386f..f641d43 100644 --- a/components/pid/temp_controller_fsm.py +++ b/components/pid/temp_controller_fsm.py @@ -96,7 +96,9 @@ class TempControllerFsm: elif self.state == States.HOLD: if diff >= self.thresholds['HoldHeat']: state_next = States.HEAT - self.pid_heat.reset() + # No pid_heat.reset() here — bumpless transfer: carry the + # hold-phase integral into the new ramp so power doesn't + # drop to near-zero and crawl back up from scratch. elif diff <= -self.thresholds['HoldCool']: state_next = States.COOL self.pid_cool.reset() diff --git a/components/pid/temp_controller_smith.py b/components/pid/temp_controller_smith.py index bae6e97..3ed2604 100755 --- a/components/pid/temp_controller_smith.py +++ b/components/pid/temp_controller_smith.py @@ -64,10 +64,7 @@ class TempController(TempControllerBase): # 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 - self.heatrate_soll = self.heatrate_soll_set * self.pid_hold.get_y() theta_err = self.theta_soll_set - self.theta_ist - heatrate_err = self.heatrate_soll - self.heatrate_ist - diff = self.theta_soll_set - self.theta_ist self.process_fsm(diff) - self.process_pid(theta_err, heatrate_err, hold_scale) + self.process_pid(theta_err, hold_scale)