Control Module: pid-loop¶
A single-loop PID controller. Reads a process variable from an analog input, holds the setpoint in an operator- or recipe-writable register, computes a PID control output, and writes that output to an analog output. Use this template for any self-contained regulatory control loop where the setpoint comes from an operator or a recipe step.
For cascade control where the setpoint is driven by another controller
or a supervisory system, use the pid-cascade template instead.
Where to find it¶
System endpoint → Equipment Library → Control Modules tab → select pid-loop.


What you see on the faceplate¶
Clicking a pid-loop instance on the unit detail view opens a faceplate with:
| Element | Meaning |
|---|---|
| PV | Process variable (read-only) |
| SP | Setpoint (writable in Manual mode) |
| CV | Control output percentage, 0-100 (read-only) |
The faceplate shows ISA-88 mode. In Manual mode an operator can write the setpoint directly. In Automatic mode a recipe step owns it.
Tags¶
The template declares deviceClass: controller and a role on each tag
(ADR 0016). The HMI renders
from these declarations and never infers meaning from moduleType or
tag names. SP (role: setpoint) fills
the HMI setpoint bar. PV (role: feedback) fills the feedback bar
and, with no role: state tag, is the card's prominent value. CV
carries no role and renders as a generic row.
| Tag | Type | Access | Role | Description |
|---|---|---|---|---|
PV |
Float | read | feedback |
Measured process variable, scaled to engineering units. Shown as the card's prominent value. |
SP |
Float | read/write | setpoint |
Setpoint. Operator or recipe writes this. |
CV |
Float | read | — | Control output (0-100%) driven to the analog output. |
ILCK |
Boolean | read | interlock |
true while the device interlock is forcing the output to its safe state. Reserve a TagTrue AlarmDefinition for interlocks whose trip is abnormal in itself (why). The historian records every trip either way. |
Parameters¶
| Parameter | Default | What it does |
|---|---|---|
kp |
1.0 |
Proportional gain |
ki |
0.1 |
Integral gain |
kd |
0.01 |
Derivative gain |
eng_min / eng_max |
(blank) | Process variable scale range |
eng_units |
(blank) | Display units for PV and SP |
interlockAddress |
(empty — interlock disabled) | Device address of a boolean trip signal, read by the output block every scan; while tripped (or unreadable — fail-safe) the output is forced to the safe value. |
interlockInvert |
false |
Trip while the signal is false (with the invert off, true trips). |
safeValue |
(empty) | Output value forced while interlocked. Empty means the output range minimum. |
Setting interlockAddress on an instance enables the device interlock.
See Alarms and Interlocks → Pattern 0.
The scan interval is fixed at 100 ms, faster than other analog templates because closed-loop PID needs tighter response.
How it works¶
Each scan, the runtime:
- Reads the process variable from the analog input.
- Computes PID output against the current setpoint using
kp,ki,kd. - Writes the resulting
CVto the analog output, clamped to 0-100%.
The PID block uses a standard parallel form. Tune gains in the detail view's Parameters section. There is no anti-windup or output rate limiting in this template. If you need those, derive a new template from this one.
When the device interlock is enabled (see interlockAddress above) and
trips, the analog output is forced to its safe value. The template wires
the output block's interlock state into the PID's TRK input (freezing
the integrator) and the output block's actually-written value into the
PID's TRK_VAL input (back-calculation output tracking). While forced,
the PID tracks the value the device really sees. When the interlock
releases, the loop resumes from the real output, a bumpless transfer.
Without the tracking it would resume from a wound-up state. This is
inert unless the interlock is enabled, so existing instances are
unaffected.
Tuning¶
The pid-loop template ships with conservative default gains that work
on the simulator but will need adjustment for any real loop. This section
covers the practical commissioning workflow: the steps you'd actually
follow on a new equipment area. It teaches no vendor-specific tuning
method.
Parallel form¶
The template implements the standard parallel form:
CV(t) = kp·e(t) + ki·∫e(t)dt + kd·de(t)/dt
where e = SP - PV. This is different from the ideal (also called
"dependent") form some vendors use, where ki and kd are expressed as
time constants relative to kp (e.g. Ti = kp/ki, Td = kd/kp). If
you're porting tuning constants from another system, check which form
the source used and convert before pasting.
What each gain does, in isolation¶
Walk a new loop one gain at a time:
| Gain | Bump it up if | Bump it down if |
|---|---|---|
| kp | The PV is consistently short of setpoint | The PV oscillates around setpoint |
| ki | The PV reaches setpoint slowly and parks just below | The PV overshoots and takes forever to settle |
| kd | A step disturbance causes a long, slow recovery | The PV jitters on noisy input |
For most loops, start with kp only (ki = 0, kd = 0), get the loop
to track without oscillating, then add a small ki to eliminate the
steady-state offset, then add kd only if you genuinely need faster
disturbance rejection. Many pharmaceutical loops never use kd.
Practical commissioning workflow¶
- Open the trend chart on this loop's
PV,SP, andCVtags via the Data endpoint → Trends view. Keep all three on the same chart. - Switch to Manual mode from the faceplate. The operator owns
CVdirectly. - Step the CV by 10–20 % and watch how
PVresponds:- Time to first response: how long before
PVstarts moving? That's your dead time. - Time to peak / steady state: how long until
PVsettles at a new value? - Process gain: by how much did
PVchange per 1 % ofCV?
- Time to first response: how long before
- Set initial
kpto roughly0.5 / process_gainandkito1 / (4 × time_to_steady_state). These are conservative starting values that almost never cause oscillation. - Switch back to Auto from the faceplate. Set the recipe-driven
setpoint and watch
PVtrack. Use the trend chart to verify:- No sustained oscillation. A few cycles of damped oscillation
after a setpoint change is fine. Never-decaying oscillation means
kpis too high. - Reaches setpoint within a reasonable time. "Reasonable" depends on the process: minutes for a reactor jacket, seconds for a flow loop.
- No persistent offset. If
PVparks consistently above or belowSP, you need moreki.
- No sustained oscillation. A few cycles of damped oscillation
after a setpoint change is fine. Never-decaying oscillation means
- Iterate in steps of ±25 % on each gain. Don't change two gains at once. You won't know which one caused the change.
When to escalate¶
The single-loop pid-loop template is intentionally simple. Move to a
different template (or derive a new one) when you need:
- Cascade control — outer-loop setpoint drives an inner-loop
setpoint. Use
pid-cascadeinstead. Common pattern: outer loop is reactor temperature, inner loop is jacket temperature. The outer-loop output becomes the inner-loop SP. - Anti-windup — the integral term keeps growing while
CVis saturated against the 0–100 % limits, then overshoots wildly on the way back. The basepid-loopdoesn't have anti-windup. You'll see a slow recovery after long saturation events. Derive a new template with a back-calculation or clamping anti-windup if this is hurting you. - Bumpless transfer on Manual/Auto — switching between Manual and
Auto causes a CV jump because the integrator hasn't been keeping up.
The default template doesn't preset the integrator on operator mode
change. (Bumpless transfer on device-interlock release is already
built in. The integrator is back-calculated from the output's
actually-written value via
TRK/TRK_VAL. See "How it works".) - Output rate limiting — for loops driving slow actuators, a CV rate limit prevents the controller from commanding faster moves than the actuator can follow.
- Feedforward — adding a measurable disturbance signal directly into the CV calculation, ahead of the feedback path.
For each of these, the right move is to derive a new control-module
template. Keep the same pid-loop interface (PV, SP, CV) so existing
phase templates that bind to a pid-loop role can use the new template
without modification.
What good tuning looks like on the trend chart¶
A well-tuned loop, after a setpoint change, shows:
- One small overshoot of 5–10 % of the SP step
- Damped oscillation that disappears within 2–3 cycles
- No persistent offset between
PVandSPafter settling CVmovement proportional to deviation, no rapid hunting
Bad signs to look for:
- Sustained oscillation —
kptoo high orkitoo high - Slow drift toward setpoint —
kitoo low CVsaturated at 0 % or 100 % for long periods — the loop is asking for more authority than the actuator can deliver. Either the process is undersized or there's a hardware fault. Adding morekpwon't help. Fix the equipment or escalate to cascade with a faster inner loop.- Noisy
CV—kdtoo high on a noisyPVsignal. Trykd = 0first. Addkdonly oncekpandkiare stable.
The faceplate trend view shows PV, SP, and CV together so you can
diagnose all of these visually without exporting data.
Reference instances¶
| Unit | Instance | Purpose |
|---|---|---|
| mix-tank-1 | N2 blanket pressure controller | Holds tank headspace pressure at target during fills and transfers |
The reference plant uses only one pid-loop instance. Most temperature and
agitator control in the reference plant is handled by analog-control (feedback +
deviation alarm). Closed-loop PID is the exception there.
Try it¶
- Process → riverbend → pharma-area → sim-cell → mix-tank-1
- Click the N2 blanket pressure symbol on the unit detail view
- On the faceplate, write a new setpoint (
SP) — e.g. change from0.2bar to0.3bar - Watch
PVconverge on the new setpoint andCVsettle at the steady-state output - Open a trend chart with
PV,SP, andCVon the same chart to see the loop response