PID

PID Playground

A FOPDT process and parallel-form PID controller, integrated with exact zero-order-hold, fractional dead time, ISA-style derivative filtering, and three selectable anti-windup strategies. Apply Ziegler-Nichols, Cohen-Coon, AMIGO, or lambda tuning rules to the configured plant. Reference simulation only - not for production tuning or process-safety decisions.

A tuning that is stable here can be unstable on the plant This simulates an ideal first-order-plus-dead-time process against an ideal controller. Real loops have valve stiction, actuator limits, measurement noise, drifting gain and neighbours that fight back, and none of that is modelled here. Never carry a gain set from this page to a live loop. Open for the full scope limits.

What this tool actually simulates

A first-order-plus-dead-time process driven by a parallel-form PID controller with zero-order hold and anti-windup, plus the classical tuning rules: Ziegler-Nichols, Cohen-Coon, AMIGO and lambda. It is a faithful simulation of that model, and it is a good way to build intuition for what each term does.

FOPDT is a deliberate idealisation. It is a useful caricature of a real process, not a description of one.

Not modelled, at all

  • The valve. Stiction, backlash, dead band, hysteresis and non-linear installed characteristic are absent. Stiction in particular produces a sustained limit cycle that no amount of retuning fixes, and it is the most common cause of a loop that oscillates in service and behaves in simulation.
  • Actuator limits. No stroke time, slew rate, or saturation beyond the anti-windup path. A controller that asks for a step the actuator cannot deliver is a different loop.
  • Measurement reality. No sensor noise, no filter lag, no transmitter span or calibration error, no aliasing from a sample rate that is slow relative to the dynamics. Derivative action amplifies noise, and there is no noise here for it to amplify.
  • A process that changes. Real gain and dead time move with throughput, composition, fouling and ambient. A tuning that is optimal at one operating point can be marginal at another.
  • Non-linearity. No pH curves, no heat-transfer non-linearity, no split range, no integrating or open-loop-unstable processes.
  • Interaction. One loop in isolation. Real plants have coupled loops, cascades, feedforward and constraint controllers that fight each other.
  • Everything around the loop. No bumpless transfer, no mode handling, no output tracking, no failure or fallback behaviour, no interlocks.

The classical tuning rules are starting points, not answers. Ziegler-Nichols in particular is deliberately aggressive and is a poor choice for most real loops as published.

Never use this for

  • Tuning, retuning or commissioning a live control loop.
  • Any safety instrumented function or protective layer under IEC 61511 or IEC 61508.
  • Process safety decisions, HAZOP or LOPA inputs, or alarm rationalisation.
  • Sizing or selecting a valve, actuator or transmitter.
  • Acceptance testing, validation, or any regulated qualification record.

A loop that is unstable on a running plant does not produce a bad graph. It produces a process excursion, and on the wrong unit that is a safety event.

Before touching a real controller

Identify the process from plant data rather than assuming a model. Detune from any classical rule and verify gain and phase margin. Test in manual and step cautiously, with the operator informed and an abort plan. Have the change reviewed by a control engineer who knows the unit, under the site's management of change process.

Loading WASM simulation engine...

Ideal FOPDT model. No valve stiction, actuator limits, measurement noise, changing process gain or loop interaction. A tuning that is stable here can be unstable on the plant, so never carry these gains to a live loop. See the full disclaimer at the top of the page.

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.

SP target
PV plant state
u actuator command
d load disturbance

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

Overshoot --
Rise time (10-90) --
Settling time --
Final error --
PM (phase margin) --
GM (gain margin) --
Ms (sensitivity peak) --
wgc (crossover) --
IAE --
ISE --
ITAE --
Saturation --
Max PV --
Min PV --
Max output --
Min output --
Settled --
Final PV --
Final output --

3Process Variable

Setpoint PV Measurement

4Controller Output

Output Raw Disturbance

5Open-Loop Frequency Response

|L| |S| wgc marker annotations

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 / Tt to 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