pid: replace IDLE-as-cooling with a real COOL state

IDLE conflated two unrelated things: "controller disabled" and "needs
to cool, which this heat-only actuator fakes via zero output." Split
them apart:

- IDLE now means disabled only - the FSM forces it whenever
  enabled=False, regardless of the temperature gap, and process_pid()
  zeroes y for it same as before.
- New COOL state (mirroring HEAT) takes over the negative-diff case,
  with its own pid_cool (separate "Cool" gains, alongside Hold/Heat)
  instead of reusing pid_rate. Its output can legitimately be negative
  - it's the actuator (tasks/heater.py's actor(), already max(0, ...))
  that clamps it to 0 because *this* plant (Pot) can only heat. A
  plant with real cooling capability could one day honor it directly.
- Thresholds renamed accordingly (HoldIdle->HoldCool, HeatIdle->
  HeatCool, new CoolHold/CoolHeat); config.json/.sim/.templ and the
  hand-rolled ctrl_params in scripts/demos/pid/ and demo_sud.py
  updated with a "Cool" params section (mirrors "Heat" for now, since
  there's no real cooling actuator to tune against yet).

Also fixes a regression in demo_sud.py/the other PID demos: none of
them ever called set_enabled(True), so since enabled defaults to
False they never drove the heater at all - only caught because this
change's demo_sud.py re-run got stuck in RAMPING forever.
This commit is contained in:
2026-06-21 13:41:20 +02:00
parent 3fea29cc58
commit 858249f1e7
7 changed files with 90 additions and 29 deletions
@@ -17,6 +17,12 @@ if __name__ == '__main__':
"ki": 0.008,
"kd": 0.0,
"kt": 1.5
},
"Cool": {
"kp": 0.08,
"ki": 0.008,
"kd": 0.0,
"kt": 1.5
}
}
@@ -38,6 +44,7 @@ if __name__ == '__main__':
temp_soll = 20
heatrate_soll = 1.25
ctrl = TempController(dt, ctrl_params)
ctrl.set_enabled(True)
plant = Pot(dt, plant_params, theta_amb)
_y = np.empty(0)
_fb = np.empty(0)
@@ -17,6 +17,12 @@ if __name__ == '__main__':
"ki": 0.008,
"kd": 0.0,
"kt": 1.5
},
"Cool": {
"kp": 0.08,
"ki": 0.008,
"kd": 0.0,
"kt": 1.5
}
}
@@ -38,6 +44,7 @@ if __name__ == '__main__':
temp_soll = 20
heatrate_soll = 1.25
ctrl = TempController(dt, ctrl_params, plant_params, theta_amb)
ctrl.set_enabled(True)
plant = Pot(dt, plant_params, theta_amb)
_y = np.empty(0)
_fb = np.empty(0)