1Process Loop
A self-regulating first-order-plus-dead-time process under parallel-form PID control. Use the Process Narrative buttons to load realistic numbers for fast flow, jacketed temperature, heat exchanger, or composition loops, or stay on the normalized educational case. The dead time can be a non-integer multiple of the integration step; the delay buffer interpolates linearly between samples.
Physical Setup
Educational FOPDT
- What it is
- --
- What dominates
- --
- What to look for
- --
- Tuning recommendation
- --
- Cautions
- --
2Metrics; shift-click any underlined term for a glossary entry
3Process Variable
4Controller Output
5Open-Loop Frequency Response
Continuous-time loop L(s) = C(s) G(s) computed from the configured FOPDT plant and parallel-form PID gains. The sample-and-hold approximation is omitted; this is the textbook loop the discrete simulator is approximating.
Exports
Build metadata pending.
Method and Scope
Process model
The plant is a first-order-plus-dead-time (FOPDT) self-regulating process driven by a delayed manipulated variable plus an additive load disturbance:
tau * dPV/dt = -(PV - pv_baseline) + K * u(t - theta) + d(t)
The continuous solution under a piecewise-constant input is integrated exactly by zero-order hold each step:
PV[k+1] = PV[k] * exp(-dt/tau)
+ target * (1 - exp(-dt/tau))
target = pv_baseline + K * u_delayed + d
This is exact within floating-point precision and removes any Euler stability constraint on dt. Dead time is implemented as a delay buffer with linear interpolation between adjacent samples when theta is not an integer multiple of dt.
PID controller
Parallel form, evaluated at the controller sample time Ts and held with a zero-order hold between samples:
error = SP - measurement
P = Kp * error
I = I + Ki * error * Ts (with anti-windup, see below)
D = -Kd * filtered_derivative_of_measurement (derivative-on-measurement)
or
D = +Kd * filtered_derivative_of_error (derivative-on-error)
u_raw = bias + P + I + D
u = clamp(u_raw, u_min, u_max)
The derivative branch is the discrete equivalent of a first-order ISA-style filter D(s) = Kd s / (1 + Tf s). The discrete pole is alpha = exp(-Ts / Tf); Tf = 0 disables filtering. The equivalent ideal-form parameters are Ti = Kp / Ki and Td = Kd / Kp; both are shown live next to the gain inputs.
Anti-windup strategies
- Off. Integrator updates every sample regardless of saturation. Useful only to demonstrate windup behavior.
- Conditional (clamp). The integrator is frozen whenever the pre-integration command is already saturated and the proposed integral update would push it further into the limit. The other direction is allowed so the integrator can pull the actuator back into range.
- Back-calculation. Each tick adds
(u_clamped - u_raw) * Ts / Ttto the integral. When the controller is unsaturated this term is zero; when saturated, it drives the integrator back at a rate set by the tracking time Tt. This is the strategy used in most modern industrial PID blocks (Siemens, ABB, AB, Honeywell).
Tuning rules
The Tuning Rules panel applies open-loop FOPDT correlations to the configured K, tau, and theta. Each rule rewrites only the controller gains so you can compare them on the same plant:
- Ziegler-Nichols PI: Kc = 0.9 * tau / (K * theta), Ti = theta / 0.3.
- Ziegler-Nichols PID: Kc = 1.2 * tau / (K * theta), Ti = 2 * theta, Td = 0.5 * theta.
- Cohen-Coon PI/PID: classic 1953 correlations; less detuning needed for dead-time-dominated loops than Z-N.
- AMIGO PI/PID: Astrom-Hagglund robustness-focused rule derived from constrained optimization.
- Lambda PI: IMC tuning with closed-loop time constant lambda = tau. Decreasing lambda makes the loop faster but less robust.
The displayed Kp / Ki / Kd, plus the matching Ti and Td readouts, are exactly what the tuning rule produced; nothing is silently re-shaped.
Measurement noise
Noise is zero-mean Gaussian with standard deviation equal to the configured amplitude. The generator is Box-Muller built on a deterministic 32-bit LCG so that Rust and Python produce identical sequences from the same seed. Fixtures are reproducible bit-for-bit.
Frequency-domain analysis
The Bode plots evaluate the continuous-time open-loop transfer function on a fixed log frequency grid (1e-3 to 1e3 rad/s, 256 points):
C(s) = Kp + Ki/s + Kd * s / (1 + Tf * s)
G(s) = K * exp(-theta * s) / (1 + tau * s)
L(s) = C(s) * G(s)
S(s) = 1 / (1 + L(s))
- Phase margin (PM) is 180 deg + angle of L at the first omega where |L(jw)| = 1.
- Gain margin (GM) is -20 log10 |L(jwpc)| at the first omega where the unwrapped phase crosses -180 deg.
- Sensitivity peak Ms = max |S(jw)| over the grid. Industrial loops aim for Ms in 1.4-2.0; below 1.4 is conservative and slow, above 2.0 is fragile.
- Gain crossover wgc marks the closed-loop bandwidth and is annotated on both subplots.
The Bode plot is computed from the configured K, tau, theta, Kp, Ki, Kd, and Tf in closed form -- it updates instantly when any tuning rule or slider is moved, with no second simulation pass needed.
Metrics
- Overshoot is direction-aware (positive and negative steps both produce non-negative overshoot).
- Rise time is the 10% to 90% rise time of the PV trajectory after the setpoint step.
- Settling time is measured against a configurable tolerance band (default 2% of the step magnitude) and is the first time after which PV stays inside the band for the rest of the horizon.
- IAE / ISE / ITAE integrate absolute, squared, and time-weighted absolute error over the horizon. ITAE is weighted from the step time forward.
- Saturation percent is the fraction of samples where the actuator command was clamped.
- Max / Min PV / output are the extrema of the recorded trajectories.
- Settled is true when the loop reached the tolerance band before the horizon ended.
Verification boundary
The Python reference in verification/pid_reference.py is the numerical ground truth. It generates deterministic fixtures consumed by the Rust crate's regression test, and is also a readable side-by-side derivation of the controller and the integrator. The browser runtime uses the Rust/WASM port; passing tests mean Rust/Python agree to within 1e-8 absolute tolerance on every recorded series and metric.
Scope and limitations
Reference simulation only. FOPDT is a deliberate idealization; real plants have constraints, nonlinearity, valve hysteresis, and stochastic disturbances that this tool does not represent. Do not use these results for process-safety decisions or plant tuning without independent validation. The author makes no warranty as to fitness for any particular use.
Learning References
PID terms and response metrics
Anti-windup strategies
MathWorks: PID Controller block (clamping & back-calculation)
Tuning rules
OptiControls: Ziegler-Nichols and Cohen-Coon open-loop tuning
AMIGO and lambda tuning
Skogestad: Simple analytic rules for model reduction and PID controller tuning