Files
brewpi/docs/temp_control_calibration.md
T
jensandClaude Sonnet 4.6 abcd011a9f Add docs/temp_control_calibration.md
Covers Smith predictor parameter identification (C, M, L, Td) from log
data, model-plant replay verification, and controller performance metrics.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NSo8R6GjoBQdStB3j67zSs
2026-06-29 22:05:37 +02:00

3.7 KiB
Raw Blame History

Temperature controller calibration

Controller overview

components/pid/temp_controller_smith.py implements a Smith predictor. Each tick it computes:

theta_ist = theta_ist_model + (theta_ist_plant  theta_ist_model_delay)

theta_ist_model comes from a fast internal Pot (transport delay Td = 0); theta_ist_model_delay from a second Pot with the full configured Td. The correction term (theta_ist_plant theta_ist_model_delay) removes the dead time from the feedback path.

The plant model (components/plant/pot.py) integrates:

temp += (delayed_power_in  L·M·(temp  theta_amb)) / (M·C) · dt

Four parameters must be calibrated:

Parameter Unit Meaning
C J/(kg·K) Specific thermal capacity
M kg Mass of pot + water + grain
L W/(kg·K) Heat loss coefficient
Td s Transport (heater-to-sensor) delay

1. Verify model-plant match: replay simulation

Feed the recorded power_eff from a log file into a fresh Pot instance with the same parameters and compare the simulated temperature to temp_ist.

from components.plant.pot import Pot

pot = Pot(dt=1.0)
pot.set_plant_params({'C': C, 'M': M, 'L': L, 'Td': Td})
pot.set_ambient_temperature(theta_amb)
pot.initial(samples[0]['temp_ist'])

sim_temp = []
for s in samples:
    pot.set_power(s['power_eff'])
    pot.process()
    sim_temp.append(pot.get_temperature())

Plot sim_temp (model) against temp_ist (real) and the residual temp_ist sim_temp. What divergence tells you:

Symptom Likely cause
Ramp slopes differ C·M wrong
Phase shift between power step and temperature rise Td wrong
Wrong equilibrium temperature during HOLD L wrong
Residual grows over a long run Model drift / L temperature-dependent

2. Parameter identification from log data

Td — transport delay

Cross-correlate power_eff with temp_ist. The lag at peak correlation is the actual Td. Alternatively, find a sharp power step (start of a ramp) and measure the visible delay before temp_ist begins rising.

L — heat loss coefficient

At steady-state HOLD, d(temp)/dt ≈ 0, so all input power compensates losses:

P_hold = L · M · (T  theta_amb)
→  L = P_hold / (M · (T  theta_amb))

Repeat across hold phases at different temperatures and fit a line through (T theta_amb) vs P_hold for a more robust estimate.

C — specific heat capacity

During a ramp where heat loss is small relative to input power:

C ≈ power_eff / (M · rate_ist_K_per_s)

rate_ist in the log is K/min; divide by 60 to get K/s. Use a mid-ramp window where power_eff and rate_ist are both stable.

3. Controller performance metrics

These can be read directly from the log without re-simulation:

Metric How to compute
Overshoot max(temp_ist) temp_soll after each setpoint step
Settling time First time `
Steady-state error Mean of temp_soll temp_ist during HOLD phase
Rate tracking error rate_ist rate_soll during RAMP

Smith predictor correction signal

The term theta_ist_plant theta_ist_model_delay is the Smith correction; a growing correction signal indicates model drift. This is not currently written to the log. To observe it, log theta_ist_model and theta_ist_model_delay from temp_controller_smith.py alongside the existing fields.

4. Suggested addition to analyze_log.py

A replay panel added to utils/analyze_log.py would show:

  • temp_ist (blue) vs sim_temp (red dashed) — model accuracy at a glance
  • Residual temp_ist sim_temp in a separate subplot — reveals systematic parameter errors vs noise floor