Generated reference › PID Controller — Control Systems/Continues
kind: generated#block#control-systems-continues

PID Controller — Control Systems/Continues

PID

Control_Systems/Continues/PID_Controller · 1 input / 1 output port(s) at insert · exports to Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Description#

The block's own DESCRIPTION_HTML, rendered verbatim — the same text the config dialog's info panel and the library navigator show. Fix a wrong sentence in the block's .cpp (R-D9), never here.

PID Controller

Control Systems / Continues

A continuous-time PID controller with a filtered derivative. It takes the error signal e and produces the control signal u:

Parallel:  u = P·e + (I/s)·e + D·N/(1 + N/s)·e
Ideal:     u = P·( e + (I/s)·e + D·N/(1 + N/s)·e )

The derivative is never taken bare: D·N/(1 + N/s) is a first-order filtered derivative whose bandwidth is set by N, so the block stays proper and has a state-space form. N → ∞ approaches the ideal D·s.

Ports

  • Input – the error e, of any size [p,m].
  • Output – the control signal u, of the same size.

The controller is SISO, but it is applied independently to every entry of the input signal, each entry carrying its own two states.

Parameters

  • Proportional (P) – the proportional gain, a scalar.
  • Integral (I) – the integral gain, a scalar. Zero removes the integral action.
  • Derivative (D) – the derivative gain, a scalar. Zero removes the derivative action, whatever N is.
  • Filter Coefficient (N) – the derivative filter bandwidth, a positive scalar. The filter pole sits at −N, so a large N tracks the ideal derivative closely and a small one smooths it heavily. Zero removes the derivative action outright, since the filter gain N/(1 + N/s) is then zero.
  • Controller Form – how P enters the sum:
    • Parallel – P, I and D are independent gains on the three branches: u = P·e + (I/s)·e + D·N/(1 + N/s)·e.
    • Ideal – P multiplies the whole controller, so it scales the integral and derivative action too: u = P·( e + (I/s)·e + D·N/(1 + N/s)·e ).
  • Initial Condition (Integrator) – the integrator state at the start of the run, a scalar used for every entry. The I gain sits before the integrator, so this is the initial value of the integral term (I·∫e dt), not of ∫e dt.
  • Initial Condition (Filter) – the derivative filter's state at the start of the run, a scalar used for every entry.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. A deployable target runs a discrete model, so the export carries the controller discretized by the run's method and the generated code matches the in-app simulation.

P, I, D and N are not tunable on the generated core: the discretization mixes them into the A/B/C/D it embeds, so retuning would mean re-discretizing on the target. Change them here and export again. The two initial conditions are baked in as the state seed for the same reason.

Simulink bridge

Import and export, mapped to simulink/Continuous/PID Controller. "Proportional (P)" to P, "Integral (I)" to I, "Derivative (D)" to D, "Filter Coefficient (N)" to N, "Controller Form" to Form (Parallel/Ideal, a 1:1 and therefore lossless pair), "Initial Condition (Integrator)" to InitialConditionForIntegrator, "Initial Condition (Filter)" to InitialConditionForFilter, and "Sampling Time (s)" to SampleTime, as on every block.

Three Simulink parameters are always implied rather than offered as a choice: Controller = PID, TimeDomain = Continuous-time and UseFilter = on. The full PID structure covers Simulink's PI, PD, P and I controllers numerically – set D to zero for PI, I to zero for PD – so nothing is lost by always emitting it, and an imported block whose values differ is reported rather than silently mistranslated.

What does not cross: output saturation with anti-windup (LimitOutput), external reset (ExternalReset), external initial conditions and external tracking all add input ports in Simulink, and no config value here can add or remove a port. An unfiltered derivative (UseFilter = off) does not cross either: D·s is improper, so it has no state-space form and none of the ten targets could carry it.

Notes

  • Stateful and continuous: two states per input entry – the integrator and the derivative filter – which the solver integrates between steps.
  • Being linear, the block is directly usable by the model reduction and linear-analysis commands.
  • The two states are Simulink's own, which is why its two initial conditions carry over without rescaling.

Code facts#

FactValue
registered typeControl_Systems/Continues/PID_Controller
familyControl_Systems/Continues
solver environment classICoreBlock_0_Control_Systems_1_Continues_2_PID_Controller
sourcesrc/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/Continues/PID_Controller/ICoreBlock_0_Control_Systems_1_Continues_2_PID_Controller.cpp
headersrc/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/Continues/PID_Controller/ICoreBlock_0_Control_Systems_1_Continues_2_PID_Controller.h
default size on canvas110 × 80 px
ports at insert1 in, 1 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDouble
2outICoreDouble

Ports the constructor creates. A block whose port list changes with its configuration adds or removes ports at load time; the count above is the one a freshly inserted block has.

Configuration variables#

Config variableDefaultSimulink parameter
Proportional (P)1P
Integral (I)1I
Derivative (D)0D
Filter Coefficient (N)100N
Controller FormParallel%~%Ideal~~ParallelForm
Initial Condition (Integrator)0InitialConditionForIntegrator
Initial Condition (Filter)0InitialConditionForFilter

Every block also carries Sampling Time (s) from ICoreBlockSolverEnvironment: zero or less inherits the solver's rate, a positive value runs the block at that period.

supportSupport::Both
Simulink pathsimulink/Continuous/PID Controller
port-count rulePortsParam::None
SampleTime parameteryes
always setController = PID, TimeDomain = Continuous-time, UseFilter = on
ICore configSimulink parameterValue translation
Proportional (P)Ppasses through
Integral (I)Ipasses through
Derivative (D)Dpasses through
Filter Coefficient (N)Npasses through
Controller FormFormParallelParallel, IdealIdeal
Initial Condition (Integrator)InitialConditionForIntegratorpasses through
Initial Condition (Filter)InitialConditionForFilterpasses through

Caveat (shown to the user): output saturation/anti-windup, external reset, external initial conditions and tracking mode are not supported (each adds an input port in Simulink); the derivative is always filtered, since an unfiltered D*s is improper and has no state-space form

Catalog contract: src/ICoreSDK/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h

Description vs code#

The lists agree. check_block_descriptions.py finds no disagreement between the description's Ports, Parameters, Code export and Simulink bridge lists and the code's.

The verdict above is tools/docs/check_block_descriptions.py (P7.1), which compares LISTS. It cannot read a sentence: "stateless" on a block with a state, an initial-value semantic the recursion does not implement, a "not synthesizable" caveat the HDL banner contradicts. That is the agent audit (P7.3) on BLOCK_DESCRIPTION_AUDIT.md, and this tool's green is not a substitute for one.

File banner (developer view)#

The top comment of the block's .cpp — the maths, the realization and the export strategy, addressed to whoever changes it. It must not contradict the description above (P7.5).

PID Controller block — filtered-derivative PID, element-wise One input (the error e), one output (the control signal u):

Parallel: u = P*e + I/s*e + D*N/(1 + N/s)e Ideal: u = P( e + I/s*e + D*N/(1 + N/s)*e )

THE REALIZATION IS SIMULINK'S, not an arbitrary equivalent one. Simulink puts the I gain BEFORE the integrator and realizes the derivative filter as xF' = N*(D*e - xF), so its two initial conditions are the states of those two 1/s blocks. Choosing the same two states is what lets "Initial Condition (Integrator)" and "Initial Condition (Filter)" map across 1:1 instead of needing a scaling nobody would guess:

xI' = I*e xF' = N*(D*e - xF) u = P*e + xI - N*xF + N*D*e (Parallel) u = P*(e + xI - N*xF + N*D*e) (Ideal)

so with x = [xI; xF]: A = [0 0; 0 -N] B = [I; N*D] C = [1 -N] D = P + N*D (Parallel) C = [P -P*N] D = P*(1 + N*D) (Ideal)

Being LINEAR, the block carries a continuous state space (initializeStateSpace_Continues), which is what makes it mergeable by the model-reduction commands and lets every export carry the run's own discretization rather than a hardcoded Euler step.

Element-wise, like Transfer Function and Integrator The controller is SISO but the input may be a [p,m] signal, and the same PID runs on every entry independently. The block state is X[2, K] with K = p*m — column e is entry e's own [xI; xF] — and the input is fed as a row U[1, K], so the plain state-space products A*X + B*U and C*X + D*U evaluate all K entries at once (B/C/D being SISO). Flatten and reshape go through rawData(), so the entry <-> entry mapping is purely positional.

Code export (Python/MATLAB/Java/Rust/C/C++ and HDL/PLC) Targets cannot integrate an ODE, so every generator realizes the DISCRETIZED recursion, structurally identical to the in-app discrete simulation, per entry: y[k] = C x[k] + D u[k] (output from the CURRENT state) x[k+1] = A x[k] + B u[k] (state update; uses the same x[k]) pidDiscreteExport() discretizes per the MODEL's method and hands back A,B,C,D,x0 — so the export matches the in-app simulation for EVERY method, not just forward Euler.

Sample results#

PID Controller — Step: 0 -> 1 at t = 1 sPID Controller — Step: 0 -> 1 at t = 1 s0246012345t (s)in ICoreDouble-Out-0out ICoreDouble-Out-0

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)0 … 1.05
rampRamp: slope 1 from t = 00 … 22.62
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias-0.6186 … 1.615
tableRepeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample-2.2 … 4.85

Plotted: step — Step: 0 -> 1 at t = 1 s

Category dynamic · sample time 0.1 · 60 steps · commit ccf005c8 · produced by docsSample --out <folder> --steps 60 · data docs/generated/samples/Control_Systems__Continues__PID_Controller.json · the SVG is generated from those numbers by tools/docs/plot_svg.py, so it is a run and not a drawing (R-D10).