📦 Archived documentation: r1 (committed 2026-10-07) View current documentation →

PID

Available on: Flexs Q5M

PID Loop Reference

ISA-style constants, derivative on measurement:

e  = SP − PV
CO = Kp·( e + (1/Ti)·∫e dt − Td·dPV/dt )

Kp is the overall gain; Ti and Td are times in seconds. Internally Kp/Ti and Kp·Td are cached when tuning is written (pidSetTuning() / pidRefreshTuning()), so nothing is divided at runtime.

What each constant does

A PID loop looks at the error — how far the measurement (PV) is from the target (SP) — three different ways, and adds the results together. Using a pump loop as the example: PV is flow in L/min, CO is drive frequency in Hz.

Kp — react to the error right now. The bigger the error, the harder the loop pushes: Kp = 5 means every 1 L/min of error adds 5 Hz to the output. This does most of the work, but it can't finish the job alone — as PV approaches SP the error shrinks, so the push shrinks too, and the loop settles just short of target (a permanent offset). Too low: sluggish, big offset. Too high: the loop overreacts to its own corrections and oscillates.

Ti — patiently work off whatever error remains. The integral term keeps nudging the output as long as any error persists, which is what finally lands PV exactly on SP. Ti is how patient it is: it's the time the I term takes to repeat the P term's correction on a constant error. Ti = 20 means "take about 20 seconds to double down." Smaller Ti = less patient = stronger action — note the direction, it trips people up. Too large: the last bit of error takes forever to clear. Too small: the loop keeps pushing after PV has started moving, overshoots, then hunts back and forth.

Td — anticipate where PV is heading. The derivative term watches how fast the measurement is moving and pushes against that motion — braking before arrival rather than after. Td = 2 means the loop acts as if PV were already where its current trend puts it 2 seconds from now. It tames overshoot on slow processes (temperature especially), but it amplifies sensor noise, so most flow and level loops simply run Td = 0 (PI control).

Rule of thumb: Kp responds to the present error, Ti cleans up the accumulated past, Td braces for the predicted future.

Parameters

Parameter Units Example Notes
kp CO units / PV unit 5 Primary control action. Higher = harder response, eventually instability.
ti s 20 Integral (reset) time; smaller = stronger. 0 = no reset, loop sits at permanent offset.
td s 2 Derivative lookahead. 0 = PI, correct for most flow/level loops.
outMin / outMax CO units 0 / 60 Output span in engineering units (e.g. Hz).
outRateLimit CO units / s 10 Max CO slew. 0 = off. Derive from the actuator, not preference.
dFilterTau s 0.05 D-term low-pass. Size as Td/10. 0 = off. Default 0.05.
setpoint PV units 25 Target.
mode — AUTO DISABLED / MANUAL / AUTO.
manualOutput CO units — CO forced (clamped) while in MANUAL.

Converting gains from other controllers

From To ours
ISA Kc, Ti, Td Drop straight in: Kp = Kc, Ti/Td unchanged
Parallel gains Kp, Ki, Kd Ti = Kp/Ki · Td = Kd/Kp
Proportional band Kp = 100/PB% (only if PV and CO are both % of span)
Ziegler-Nichols Ku, Pu Kp = 0.6·Ku · Ti = 0.5·Pu · Td = 0.125·Pu (aggressive; detune)

Built-in behavior (not configurable)

  • Derivative on PV — setpoint steps never kick the output; D always runs through the LPF when dFilterTau > 0.
  • Anti-windup — the integrator is clamped every cycle so P+I+D lands inside [outMin, outMax]; it cannot wind past the output span. The slew limiter runs downstream, so keep rate limits generous.
  • Bumpless transfer — in MANUAL the integrator tracks the output, so switching to AUTO starts from the CO already at the actuator.
  • Reverse-acting loops — no direction flag; negate Kp only. Ti and Td stay positive and inherit the direction automatically.
  • Fault handling — NaN/Inf PV holds the last output; a scan gap over 0.5 s resyncs state instead of integrating it.

Manual tuning quickstart

  1. Ti = Td = 0. Raise Kp until the loop just oscillates, then back off.
  2. Bring in Ti (start near the oscillation period) and shrink it until offset clears without hunting.
  3. Add Td last, only if overshoot is still a problem.

Functions

setPID()

setPID(id,target)

Set PID target

Sets the target value for the PID.

The PID monitors the PIDs process value (PV) and adjusts the controlled output (CO) as needed to reach the configured target value.

PIDs are generally updated every 100ms. Ideally the process value error status should be monitored to ensure correct sensor fault handling. e.g.

if(io.waterPressure._err !== 0){ setPID(0,false); // Disable the PID from modifying the CO io.pumpSpeed = 55; // Manually set the CO to a safe operating value }

In practice it would be better to use the alm handling class for fault handling to ensure proper de-bouncing, recovery handling and logging.

e.g. ` alm.g("Pump 1");

let err = alm.e(alm.crit, // alarm priority "Pressure Transducer Failure", // alarm title

// alarm condition io.waterPressure._err !== 0 || io.waterPressure.raw < 3.9 || // <4mA generally bad transducer io.waterPressure.inst > 21, // >21mA generally bad transducer

// alarm trigger time 3, // alarm condition must be true for 3 seconds to throw a fault // allows ignoring small hiccups from transient events

true, // the alarm can automatically clear once the condition is false 3600, // alarm condition must be false for 3600s (1h) before automatically clearing the fault io.waterPressure.inst // Log this value when the fault is triggered, useful to record the peak pressure );

if(err){ setPID(0,false); // Disable the PID from modifying the CO io.pumpSpeed = 55; // Manually set the CO to a safe operating value } `

Arguments

NameTypeRequiredDefaultDescription
id yes — PID Index #
value number | boolean yes — Setpoint | true/false for enable/disable

Returns

number CO value

Example

setPID(0,80); // Target 80% PID output