top of page
Search
Writer's pictureIvaylo Fiziev

Practical use case: PID controller

Updated: Oct 17, 2023


PID (Proportional Integral Derivative) controllers are often used in feedback loops where temperature, pressure, speed flow and other inert variables are involved. They provide a simple and reliable way to control the process of driving the target variable to a well known state.

PID controllers work by evaluating the error (target value - current value) of a controlled variable. They take into account the error itself (P), the rate at which the error is changing (D) and also the statistically calculated error (I), combining all three in a single control (feedback) value. The control value is added to the current value of the controlled variable and then the process repeats until the controlled variable reaches a predefined value. At this point the error becomes zero and the controlled variable stays unchanged until a new target value is specified. During the process the current value often oscillates around the target value while the calculations are constantly trying to minimize the error. To make the process efficient PID controllers rely on the gain. For each of the individual components (K, P and D) in the calculation there is a separate gain. Usually these are known as KP, KI and KD. They are just multipliers that affect the controller's output. And this is where the biggest downside of the PID controllers is - they require tuning. e.g. you need to find specific values for each of the gains that will satisfy your use case. A badly tuned controller may lead to ever increasing oscillations of the current value around the target value. Inevitably this leads to device malfunction.


In Process Simulate we can model this behavior with (you guessed it) a SCL script (FB).

FUNCTION_BLOCK "PID_CTL"

VERSION:1.0

On input we provide the gains as well as the target value and the current value of the controlled variable. Also we provide the sampling time and min/max values for the target/control value:

VAR_INPUT

KP : REAL := 18.45; // Proportional gain

KI : REAL := 3.56; // Integral gain

KD : REAL := 0.0; // Differential gain

SP : REAL := 0; // Setpoint (target) value

PV : REAL := 0; // Provisioned (current) value

MINSP : REAL := 0; // Minimal setpoint value

MAXSP : REAL := 200; // Maximal setpoint value

MINCV : REAL := 0; // Minimum control value

MAXCV : REAL := 10; // Maximum control value

T : REAL := 0.1; // Sampling time (sec) equal to the logic update rate

END_VAR


On output we have the control value and the current error.

VAR_OUTPUT

CV : REAL := 0; // Control value

ERROR : REAL := 0; // Error value

END_VAR


Internally the code uses a number of other variables to store the last/total error:

VAR

LastError : REAL := 0; // Last error

TotalError : REAL := 0; // Total error

CumulativeError : REAL := 0; // Sum of all errors

END_VAR

VAR_TEMP

Temp3 : REAL := 0;

ScaledError : REAL := 0;

END_VAR


Now let's take a look at the PID logic:

BEGIN

#ERROR := #SP - #PV; // calculate error

#Temp3 := (#ERROR - #MINSP) / (#MAXSP - #MINSP); // normalize error

#ScaledError := #Temp3 * (#MAXCV - #MINCV) + #MINCV; // scale error

#TotalError := #ScaledError + #CumulativeError; // calculate total error

#CV := (#ScaledError * #KP) + // calculate control value

(#TotalError * #KI * #T) + //

((#KD / #T) * (#ScaledError - #LastError)); //

if #CV > #MAXCV then // limit control value

#CV := #MAXCV; //

elsif #CV < -#MAXCV then //

#CV := -#MAXCV; //

end_if; //

#CumulativeError := #TotalError; // update cumulative error

#LastError := #ScaledError; // update last error

END_FUNCTION_BLOCK

You can see that this is just a formula calculating the error and the control value. Pretty simple actually. Still it works pretty well for what it is.


Now. How do we use this?

Save the code to a PID_CTL.scl text file (UTF-8 encoded) and copy it to the SCL Vault (EMPower\Scripting\SCL\Vault). Then start Process Simulate and open SCL Editor on a resource and paste the code below. The resource should have a prismatic joint named 'j1'.

FUNCTION_BLOCK "MAIN"

VERSION:1.0

VAR_INPUT

sp : REAL; // target joint value

END_VAR

VAR

pid : "PID_CTL"; // PID controller instance

END_VAR

VAR_TEMP

cv : REAL; // control value

END_VAR

BEGIN

#pid(KP:=3.3, // proportional gain

KI:=15.14, // integral gain

KD:=0, // differential gain

MAXCV:=10, // max. control value

MINCV:=0, // min. control value

MAXSP:=200, // max. target value

MINSP:=0, // min. target value

SP:=#sp, // target value

PV:=JDS('j1'), // current joint value

CV=>#cv); // control value

if #cv <> 0 then

// move the joint based on the PID control value

JUMP_JOINT(NAME:='j1', VALUE:=JDS('j1') + #cv);

end_if;

END_FUNCTION_BLOCK


You need to wire a signal to the input parameter and then check the results in Robot Viewer during simulation. Once you get it running you can play with the proportional, integral and derivative gain to see the effect that they have on the PID output. Just modify the values and save the script.

Note: Here I am using a joint to visualize the PID output but any other variable can be used for the same. It is just easier with the joint.

Note: The implementation is not perfect and needs to be enhanced if needed. This is just the simplest code I could come up for this demo.


PID controllers can be found everywhere nowadays. From home appliances to the automotive industry. They are broadly applicable since they rely only on the response of the measured variable, not on specific knowledge or model of the underlying process.

What would you use it for?



28 views0 comments

Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
bottom of page