Skip to main content

Class PIDController<T>

Generic PID (Proportional-Integral-Derivative) controller base class. Computes a corrective output from a measured error signal to continuously drive a system toward a desired setpoint.

[Serializable]
public abstract class PIDController<T>

Type Parameters​

NameTypeDescription
TNumeric or vector type that the controller operates on.

Inheritance​

Examples​

Use the concrete FloatPIDController subclass to regulate vehicle throttle each FixedUpdate. The error is the difference between the desired speed and the current speed; GetOutput returns a throttle value that the car controller consumes:

[SerializeField] private FloatPIDController _throttlePID; // tuned in Inspector
[SerializeField] private PrometeoCarController _car;

private float _targetSpeed = 10f; // m/s

private void FixedUpdate()
{
float error = _targetSpeed - _car.CurrentSpeed;
float throttle = _throttlePID.GetOutput(error, Time.fixedDeltaTime);
_car.Move(new Vector2(0f, Mathf.Clamp(throttle, -1f, 1f)));
}

// Call Reset() whenever the setpoint changes abruptly — e.g. after a new waypoint
// is assigned — to flush the integral accumulator and prevent I-term windup from
// the previous tracking run.
private void OnNewWaypointAssigned()
{
_throttlePID.Reset();
}

Remarks​

A PID controller is used instead of simpler bang-bang control because it produces smooth, continuous outputs that avoid abrupt jerks in vehicle motion — important both for realistic simulation fidelity and for preventing motion sickness in VR participants.

The three terms serve distinct roles: the P term reacts proportionally to the current error (how far off-target the vehicle is now); the I term accumulates persistent error over time (removes steady-state offset, e.g. a constant speed deficit on an incline); the D term damps oscillations by opposing rapid error changes (prevents overshoot). Concrete subclasses FloatPIDController and Vector3PIDController implement the type-specific arithmetic.

Constructors​

PIDController()​

Initializes a new instance of the PIDController class with default coefficients.

public PIDController()

PIDController(float, float, float)​

Initializes a new instance of the PIDController class with specified coefficients.

public PIDController(float pFactor, float iFactor, float dFactor)

Parameters​

NameTypeDescription
pFactorfloatProportional gain.
iFactorfloatIntegral gain.
dFactorfloatDerivative gain.

Fields​

Coefficients​

Current set of PID tuning coefficients.

[SerializeField]
[FormerlySerializedAs("PIDCoefficients")]
public PIDController<T>.PIDFactors Coefficients

Field Value​

PIDController<T>.PIDFactors

derivativeInitialized​

Guards the derivative term against an infinite spike on the very first call. On frame zero previousError is the default value for T, so (error - previousError) / deltaTime would produce an enormous derivative output. This flag suppresses the D-term for the first tick and is cleared by Reset.

protected bool derivativeInitialized

Field Value​

bool

iTerm​

Accumulated integral term.

protected T iTerm

Field Value​

T

previousError​

The previous error value, used to compute the derivative term.

protected T previousError

Field Value​

T

Methods​

ComputeOutput(T, float)​

Performs the type-specific PID computation: calculates the proportional, integral, and derivative contributions and combines them according to the tuning coefficients.

protected abstract T ComputeOutput(T error, float deltaTime)

Parameters​

NameTypeDescription
errorTThe current error value.
deltaTimefloatTime in seconds since the last update.

Returns​

T — The raw PID output before any external scaling or clamping.

GetOutput(T, float)​

Computes the PID controller output based on the current error and elapsed time.

public T GetOutput(T error, float deltaTime)

Parameters​

NameTypeDescription
errorTThe difference between the desired setpoint and the current value.
deltaTimefloatTime in seconds since the last update; must be greater than zero.

Returns​

T — The control output calculated by the PID algorithm.

Reset()​

Resets the controller's internal state (derivative initialization flag and integral accumulator) while retaining the current tuning coefficients.

public void Reset()

Remarks​

Call this method whenever the tracking target changes abruptly — for example, after the vehicle is assigned a new waypoint, when the desired speed setpoint jumps discontinuously, or when a PID-controlled loop is re-enabled after being paused. Without a reset, the integral term retains error accumulated during the previous tracking run and will drive the output in the wrong direction immediately after the target change (I-term windup). Resetting also suppresses the spurious derivative spike that would otherwise occur on the first tick after re-activation, because previousError would hold a stale value from the prior run.