Taxes and Regulations
Control and regulation with the math module
Given the extensive function library of the math module, it makes sense to also implement control and regulation functions in this script language.
This is indeed possible with a few restrictions and taking the following points into account:
Signal processing, time behavior
The process of control and regulation requires hard real-time calculations, i.e., a fixed signal propagation time that is as short as possible from the detection of the measured variable to the output of the control variable to the process. In control systems, this is typically around 1 to 10 ms. This hard constraint cannot be met with the smartCORE architecture!
Measurement data is acquired by specific plug-ins and written to the channels of the smartCORE. To do this, the formula set of the math module is calculated asynchronously in approximately constant, large time intervals on the available data, provided that all required channels also provide data for a given point in time. The results are written to smartCORE channels at the rate of this calculation. For this purpose, data from the smartCORE channels is output to the process asynchronously, e.g., via CAN.
There are therefore enough places where the real-time requirement is violated by the asynchronicity in smartCORE, the channels as buffers or ring memory, and the batch processing of the math module.
If the processes to be influenced are slow1 enough, you can at least adjust the calculation process and the output so that the discrete sampling time discreteSampleTimeMs matches the execution time of the batch process evaluationTimeMs and the output to the process. Possible values here are in the range of 50 to 1000 ms. Then the asynchrony and buffering of the data streams become tolerable.
Timely calculation
As essential as real-time calculation of measurement data is for subsequent analysis and process monitoring, it is just as much of a hindrance to control and regulation processes. The basic rule in the math module is that extrapolation into the future is not permitted. Accordingly, a sequence of measured values is only valid and usable up to the point in time at which it was last updated.
Assuming that certain setpoints or switching commands are delivered as shared attributes from a dashboard, it can take 2-5 seconds before a new value is available. Suppose that weather data required for parameterizing the control process is only delivered by the weather station once per minute.
In both cases, the control or regulation would remain unchanged until new values were available. However, this is undesirable. Using the macro #timeless, corresponding channels can be removed from the time-correct calculation. Then the last value in the channel remains valid into the future until a new value is added. This behavior comes at the cost of no longer taking into account (short-term) value changes that are still below the last result value calculated based on the forecast.
Further possibilities are offered by the functions
You can query various states of an input variable. If no smartCORE channel could be connected or if this channel does not yet provide any data, a default value is assigned, which can be set using the macro #property <channel>, default=<var>.
Controller
PID controller "pidCtrl"
This function is currently being implemented and is not yet intended for productive operation.
The pidCtrl() function implements a controller with configurable proportional (P), integral (I), and derivative (D) behavior.
The input parameters of the function are at least the reference variable (setpoint) sp and the process variable to be controlled (actual value, current value) cur. The output is the control variable.
Optionally, the operating mode can be selected via the input mode:
| mode | Description |
|---|---|
| 0, false | Normal control mode (Enable) |
| 1, true | Hold control variable (Hold) |
| 2 | Control variable follows manual control input u_man (Bypass) |
Optionally, the control parameters [K, Ti, Td] can also be assigned dynamically using the input vector parVec. The vector can contain 1 to 3 parameter values. These overwrite the specifications in the configuration object.
The calculation is always performed using the discrete sampling time discreteSampleTimeMs. To ensure that the control variable can be transferred to the process in a timely manner, the execution time of the batch process evaluationTimeMs must be set to the same value.
u1 = pidCtrl(sp, cur, {...});
u2 = pidCtrl(sp, cur, mode, {...});
u3 = pidCtrl(sp, cur, mode, u_man, {...});
u4 = pidCtrl(sp, cur, mode, u_man, parVec, {...});
// Mandatory configuration for all variants
ux = pidCtrl(..., { K: <dbl>
, Ti: off|<dbl
, Td: off|<dbl>
, reverse: <bool>
, commonK: <bool>
, dWidth: <uint>
, lower: off|<dbl>
, upper: off|<dbl>
});
The properties are described using examples from the time domain, although the function calculates internally in discrete sampling steps.
| Property | Value | Description |
|---|---|---|
| K | <dbl> | Amplification of the controller |
| Ti | off|<dbl> | Activation and integration time for I component |
| Td | off|<dbl> | Activation and differentiation time for D component |
| reverse | <bool> | Reversal of control direction false: , (def.) true: |
| commonK | <bool> | Interpretation of the gain: false: true: , (def.) |
| dWidth | <uint> | Number of sampling intervals for weighted averaging of the differential value (def.: 3) |
| lower | off|<dbl> | Lower limit of the control variable, the integral component is tracked to enable shock-free resumption. |
| upper | off|<dbl> | Upper limit of the control variable, |
Controller structure: The commonK property switches the interpretation of the gain factor K. In control engineering, K usually acts equally on all three paths P, I, and D (commonK: true, def.!) and also converts the physical dimensions between control deviation (e.g., in hPa) and control variable (e.g., in %). Thus, K does not influence the dynamic behavior of the controller, which is determined by its poles and zeros, but only the agility and stability of the closed control loop. Many tuning rules for PID controllers are based on this structure.
It is rare to find the interpretation that K actually only influences the proportional path of the controller (commonK: false). This shifts the task of unit conversion to the time parameters Ti and Td, and changing K directly changes the poles and zeros of the controller and thus its dynamic behavior. This makes it considerably more difficult to calibrate the controller.
Using the D component: Since the controller function is tied to the very slow discrete clocking of the math module, the evaluation of the gradient of the control deviation in the D component should also be used with caution. Due to the "on-change" philosophy used to provide measurement data in a smartCORE channel, there may be long periods of time during which the signal does not change. In order to be able to detect a slow drift in temperature, for example, the values are calculated over several sampling steps using a weighting function to obtain the value of the derivative. The width of this window is specified by the dWidth property. However, this also creates additional dead time (dWidth/2 * discreteSampleTimeMs) in the controller's D path, which can have a negative impact on the control process. dWidth should therefore be as small as possible but as large as necessary. This depends on the process to be controlled.
Time reference: If the reference variable sp comes from a (shared) attribute and is only updated rarely, this channel should definitely be detached from the time reference using the macro #timeless. The same may also apply to the process variable cur.
Finite State Machine
State machine "states"
This function is currently being implemented and is not yet intended for productive operation.
Implementation of a finite state machine (FSM)
This states() function can be used to model behavior consisting of states, state transitions, and actions.
A general description can be found in the article Finite-state machine - Wikipedia or here (DE).
In this implementation, evaluation is clocked with the global discrete sampling time discreteSampleTimeMs. In each clock cycle, exactly one transition can cause a state change.
This finite state machine has a finite number of states, which are identified by a unique identifier sx. Each state is stable until a state transition triggers a change. This means that a state can contain information about the past, since the system has reached it on its previous path, i.e., it reflects to a certain extent the changes in the input since the system started up until the current point in time.
s1, … sN: define the identifiers of the states sx of the state machine as const <uint/int/str> (!) The first state is the start state, unless another is specified via the property init: sx. The output of states() is the respective value of sx of the active state, unless the output of the state index is forced as [1, 2, … N] with the property outIdx: true. All sx must be unique, as they are used in the transit() functions to specify the next state.
A state transition is a transition from the current state to a new (different) state. This transition occurs when the specified logical conditions / "inputs" are present, which must be fulfilled to enable the transition. In this implementation, a Boolean condition, a definable time window, a repeat counter, and a priority value for release are available for this purpose.
trXY: define transit() objects that are evaluated for the aforementioned state. The first transit() function for which the configured conditions apply triggers the state transition.
State transitions defined before the first state play a special role. They are evaluated in any state before the transitions defined in the state itself are evaluated. They thus take on the role of "from any" transitions, e.g., to catch error conditions, implement timeouts, or force resets.
The parameters of the states() function thus describe the automaton in which a const <uint/int/str> parameter either defines a new state or defines state transitions from this state using transit().
act = states(tr00, tr01, …
, s1, tr10, tr11, …
, s2, tr20, tr21, …);
// Optional configuration
actX = states(..., { init: <sx>
, outIdx: <bool>
, ageVar: <str>
, dbgVar: <str>
});
| Property | Value | Description |
|---|---|---|
| init | <var> | Start state for the automaton, unless defined as the first |
| outIdx | <bool> | true: Output of the index of the active state <uint> false: Output of the identifier defined for the state <uint/int/str> |
| ageVar | <str> | The "age" of the active state in seconds can be published in the variable designated here. |
| dbgVar | <str> | The node number of the triggering transition is written to the variable specified here. "0" if no transition is pending. |
| storage | <str> | (in preparation) |
An action is the "output" of the FSM that occurs in a specific situation. There are four types of actions:
-
Input action: The action is executed/output when entering a state (regardless of which state transition was used to reach the state, if there are several).
-
Exit action: The action is generated when leaving a state (regardless of which state transition was used to leave the state).
-
Input action: The action is generated depending on the current state and the input. This means that several actions can be assigned to a state, which are executed depending on the state transition via which it is reached/left.
-
Transition action: The action is executed during a state transition.
If act = state(…) is a variable that maps the active state, the state-dependent actions can be implemented as follows:
-
Input action:
posedge(act == K) -
Output action:
negedge(act == K) -
Input action:
(act == K)
With the actVar: "TR" that can be defined in the transition, ultimately also
- Transition actions: TR
be implemented.
Transitions "transit"
This feature is currently being implemented and is not yet intended for productive use.
The transit() function generates the transition objects that are passed as parameters to the state machine states(). The result of this function can only be processed in the states() function.
-
sx: is the next state, provided that the transition condition is met. If sx is missing, the transition returns to the currently active initial state without restarting it. sx can also come from a calculation or a variable. If sx does not refer to a known state, the replacement value sFail is used.
-
cond: Condition for the state transition, def.: true
-
tBegin, tEnd: Time interval relative to the active time of the state in which the condition is evaluated. Def.:
states(..., transit({...}) // Transition to the initial state
, transit(sx, {...}) // Transition to sx
, transit(sx, cond) // ...with dynamic condition
, transit(sx, cond, tBegin) // ...valid from time
, transit(sx, cond, tBegin, tEnd) // ...valid in the time interval
, ...);
// Optional configuration for all variants, unless
// explicitly requested
transit(..., { tBegin: <dbl>
, tEnd: <dbl>
, sFail: <var>
, nMax: <int>
, nVar: <str>
, actVar: <str>
, edge: <int>
, prio: <int>
})
| Property | Value | Description |
|---|---|---|
| tBegin | <bool> | Time in seconds relative to the active time of the state from which the condition is evaluated, def.: 0.0 |
| tEnd | <uint> | Time in seconds relative to the active time of the state until which the condition is evaluated, def.: |
| sFail | <var> | Replacement identifier / target state if sx does not designate a valid state |
| nMax | <int> | Maximum number of consecutive activations (loop counter), reset if the state is exited with any other transition. |
| nVar | <str> | The loop counter can be published in the designated variable and is updated when the transition is triggered. |
| actVar | <str> | As long as a transition triggers, true is output on the designated variable. |
| edge | <int> | Determines whether the transition is triggered statically (0), with posedge (1) or negedge (-1) of the condition. |
| prio | <int> | Optional priority if several transitions trigger simultaneously. In principle, the first with the highest prio level wins in the order of definition. |