ModSlaveSim help v3.06  4.4.8. Feedback Controllers
Download manual: HTML
This section contains some templates for small programs that simulate some of the types of feedback control that may be used by Modbus slaves. They are intended simply as a quick guide to programming some familiar types of feedback control.
The feedback controllers in this section become progressively more adept at control. They can be distinguished by how well they are able to maintain a 'Controlled Variable' (CV), such as temperature, water or light, at a required 'Set Point' (SP) when adjusting some 'Controller Output' (CO), such as a heater, a valve, or the angle of louvres. Note that these terms and abbreviations (CV, SP, and CO) are used in the controller sections below.
The algorithm for the proportional controller is built upon in each successive controller with extra code that refines the behaviour, culminating with the PID controller. So if you are unfamiliar with the more complex controllers it is helpful to look at them in sequence. However, the program templates are intended to be used even without familiarity by adapting them in cookbook mode to the needs of a simulation.
The first three controllers are used in simulations 13 using a Room heating example.
The output of an on/off controller has only two possible values: "on" or "off". To prevent rapid switching of the output, it is usual to introduce "hysteresis" into the system by having two setpoints: one at which the output is turned on, and one at which the output is turned off.
An example might be a slave controlling a valve that simply opens and closes, or a thermostat turning a room heater on or off. CV would correspond to room temperature, and CO to the thermostat. This type of controller may overshoot ot undershoot the required set point SP  for example, the room may either overheat or become too cold.
Table 22. On/off controller
Register Address  Register Name  Statement 

$100  SP low  
$101  SP high  
$102  CV  
$103  CO 
if $102 > $101 then 0 else if $102 < $100 then 1 
Note that CO is left unchanged when CV is between SP low and SP high.
The output of a proportional controller is proportional to the error, which is the difference between the setpoint SP and the controlled variable CV.
A proportional controller has a gain parameter Kc, which is used to calculate the controller output from the error. The gain is that proportion of controller output that should be used to reduce the difference between an actual and a desired state. The lower the gain, the longer it will take to stabilize.
The controller output is stored without limiting its range (CO unlimited). A practical controller will also have a minimum output value (CO min) and a maximum output value CO max), which are used to constrain the output CO unlimited.
An example of a proportional controller is a slave controlling a valve that can be opened to any position, or controlling a cooling fan or heater with a continuously variable speed. This type of controller normally gets closer to the required SP without overshooting, but also without reaching it.
Table 23. Proportional controller
Register Address  Register Name  Statement 

$100  SP  
$101  CV  
$102  Kc  
$103  CO min  
$104  CO max  
$105  CO unlimited 
$102 * ($100  $101)

$106  CO 
if $105 > $104 then $104 else if $105 < $103 then $103 else $105 
CO unlimited is computed here using the proportion Kc multiplied by the difference between actual and desired values (e.g.temperatures) to provide a setting (e.g. a heater variable control setting).
CO uses the minimum or maximum settings, $103 or $104, if CO unlimited is below or above them respectively.
For many applications a proportional controller will never get CV to be equal to SP  there will always be a residual error. This residual error can be eliminated by using a "PI controller", which has an integral term as well as a proportional term, or gain (Kc). The integral term sums the errors from all previous cycles, and uses this sum (together with the current error itself) to determine the controller output. An additional parameter Ki is used to determine the contribution of the integral term to the output.
This form of controller controls its output well enough to reach the required SP exactly, even if the slave overshoots or undershoots the target in the process. Both this PI controller and and the PID controller enable this. Both still undershoot or overshoot the required SP in the process of reaching it, but the PID controller is better able to control the amount by which this happens.
Below are two templates for simulating a PI controller, either of which might be used in a Modbus slave. The first, as will become clear, suffers from problems that the second  using a velocity algorithm  deals with. All the simulation examples that use a PI controller use the velocity algorithm template.
Both of these PI controllers will overshoot their target SP.
Table 24. PI controller
Register Address  Register Name  Statement 

$100  SP  
$101  CV  
$102  Kc  
$103  Ki  
$104  CO min  
$105  CO max  
$106  SumError  $$ + ($100  $101) 
$107  CO unlimited 
$102 * (($100  $101) + $103 * CycleTime * $106) 
$108  CO 
if $107 > $105 then $105 else if $107 < $104 then $104 else $107 
The SumError register is used to calculate the sum of the errors. Note that the error can be positive or negative, so the sum of the errors can increase or decrease on each cycle.
The statement for CO unlimited combines the error (
$100  $101
) and the weighted error sum ($103 * CycleTime * $106
) to calculate the controller output. The rate at which SumError changes will depend on the cycle time  a smaller cycle time will cause the sum to change more quickly  the statement for CO unlimited multiplies SumError byCycleTime
to compensate for this.
This implementation of a PI controller suffers from the socalled "integral windup" problem, where the integrated error SumError can grow without bound when CO reaches its limit. An alternative implementation called the "velocity algorithm" avoids this problem.
Table 25. PI controller  velocity algorithm
Register Address  Register Name  Statement 

$100  SP  
$101  CV  
$102  Kc  
$103  Ki  
$104  CO min  
$105  CO max  
$106  PrevError  $100  $101 
$107  CO increment 
$102 * (($100  $101)  $106 + $103 * CycleTime * ($100  $101)) 
$108  CO 
if $$ + $107 > $105 then $105 else if $$ + $107 < $104 then $104 else $$ + $107 
In this implementation an increment CO increment is computed and then added on to CO. The summation of the errors is effectively done as part of the computation of CO instead of separately. The integral windup problem is avoided by simply not adding on the increment if it would take CO outside of its permitted range.
The PrevError register holds the error from the previous cycle, and is used in calculating the increment.
A PID controller uses integral and derivative terms as well as a proportional term in computing its output. Typically the derivative is the rate of change of the controlled variable CV, such as temperature, or the rate of change of the error, such as the difference between the desired and actual temperatures. The derivative is often included to reduce overshooting the target set point SP. The parameters Ki and Td determine the contributions of the integral and derivative terms to the output.
Table 26. PID controller
Register Address  Register Name  Statement 

$100  SP  
$101  CV  
$102  Kc  
$103  Ki  
$104  Td  
$105  CO min  
$106  CO max  
$107  CV1  $101 
$108  CV2  $107 
$109  PrevError  $100  $101 
$110  CO increment 
$102 * ( ($100  $101)  $109 + $103 * CycleTime * ($100  $101)  $104 / CycleTime * ($101  2 * $107 + $108)) 
$109  CO 
if $$ + $110 > $106 then $106 else if $$ + $110 < $105 then $105 else $$ + $110 
This implementation of a PID controller uses the socalled "velocity algorithm", where an increment to controller output (CO) is computed and then added on. The implementation also uses the derivative of CV instead of the derivative of the error, since this typically responds better to changes in SP.
Registers CV1 and CV2 use the delay line technique to save values of CV from previous cycles. Similarly, register PrevError holds the error from the previous cycle.