Redesign Sud schedule format: separate ramp/hold keys with defaults

Steps now declare a "ramp" or "hold" key instead of a flat "type", and
unspecified fields (including nested stirrer settings) fall back to a
new top-level default.step structure. Stirrer config is now
interval_time/on_ratio, mapping directly onto AStirrer's cycle
time/duty cycle instead of separate on/off durations. Update
components/sud.py, tasks/sud.py, the Sud demo, sude/sud_0010.json, and
the README to match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CgR9tPaSzFkAwRAUyeaaCD
This commit is contained in:
2026-06-20 07:20:52 +02:00
co-authored by Claude Sonnet 4.6
parent 2dcf644bed
commit 123318ac85
5 changed files with 172 additions and 107 deletions
+17 -17
View File
@@ -27,7 +27,7 @@ if __name__ == '__main__':
},
"Heat": {
"kp": 0.08,
"ki": 0.008,
"ki": 0.02,
"kd": 0.0,
"kt": 1.5
}
@@ -38,7 +38,7 @@ if __name__ == '__main__':
plant_params = {
**sud.derive_plant_params(),
"L" : 0.2,
"Td" : 30
"Td" : 60
}
ctrl = TempController(dt, ctrl_params, plant_params, theta_amb)
@@ -53,29 +53,29 @@ if __name__ == '__main__':
heater.set_on_changed('power_eff', plant.set_power)
def apply_stirrer(step):
speed = step.get('stirrer_speed', 0)
on = step.get('stirrer_time_on', 0)
off = step.get('stirrer_time_off', 0)
cycle = on + off
if cycle > 0:
duty = on / cycle
stirrer_cfg = step.get('ramp', step.get('hold', {})).get('stirrer', {})
speed = stirrer_cfg.get('speed', 0)
interval_time = stirrer_cfg.get('interval_time', 0)
on_ratio = stirrer_cfg.get('on_ratio', 1.0)
if interval_time > 0:
stirrer.set_cycle_time(interval_time)
stirrer.set_duty_cycle(on_ratio)
else:
cycle = 1.0
duty = 1.0 if speed > 0 else 0.0
stirrer.set_cycle_time(cycle)
stirrer.set_duty_cycle(duty)
stirrer.set_cycle_time(1.0)
stirrer.set_duty_cycle(1.0 if speed > 0 else 0.0)
stirrer.set_speed(speed)
def on_step_changed(step):
if step is not None:
if step['type'] == 'heat':
ctrl.set_theta_soll(step['temp'])
ctrl.set_heatrate_soll(step['rate'])
if 'ramp' in step:
ctrl.set_theta_soll(step['ramp']['temp'])
ctrl.set_heatrate_soll(step['ramp']['rate'])
apply_stirrer(step)
def on_state_changed(state):
if state == SudState.DONE:
if state == SudState.WAIT_USER and sud.user_message:
print("Sud: {}".format(sud.user_message))
elif state == SudState.DONE:
stirrer.set_duty_cycle(1.0)
stirrer.set_speed(0)