Math expressions

This section builds upon the previous example and adds features that allow computing mathematical expressions on the MARTe2 signals.

The expressions are implemented using the MathExpressionGAM, which allows computing mathematical expressions on the input signals and providing the result in the output signals. The expressions are defined in a configuration file and can use any of the input signals as variables. It also supports Constants in numeric and literal form.

The backend of the MathExpressionGAM is the MathExpressionParser.

In this example, the MathExpressionGAM is used to compute the time taken by all the GAMs to execute their logic by subtracting the write time of the last GAM from the read time of the first GAM, that is: GAMsExecutionTime = GAMDisplay_WriteTime - GAMTimer_ReadTime.

Mass-spring-damper time to execute all GAMs.

Time required to execute all GAMs.

MathExpressionGAM configuration.
 1        +GAMMathExpr = {
 2            Class = MathExpressionGAM
 3            Expression = "GAMsExecutionTime = GAMDisplay_WriteTime - GAMTimer_ReadTime;"
 4            InputSignals = {
 5                GAMTimer_ReadTime = {
 6                    DataSource = DDB1
 7                    Type = uint32
 8                }
 9                GAMDisplay_WriteTime = {
10                    DataSource = DDB1
11                    Type = uint32
12                }
13            }
14            OutputSignals = {
15                GAMsExecutionTime = {
16                    DataSource = DDB1
17                    Type = uint32
18                }
19            }
20        }

Warning

The MathExpressionGAM is very sensitive to the types used. Carefully review the documentation of the MathExpressionGAM and ensure that values are cast as required.

Running the application

Start the application with:

./MARTeApp.sh -f ../Configurations/MassSpring/RTApp-MassSpring-8.cfg -l RealTimeLoader -s State1

Once the application is running, inspect the screen output and verify that the log shows the GAMsExecutionTime value. The log should show entries similar to the following:

$ [Information - LoggerBroker.cpp:152]: Thread1FreeTimeHistogram [0:10]:{ 0 0 0 0 0 0 0 0 0 50 23 }
$ [Information - LoggerBroker.cpp:152]: GAMsExecutionTime [0:0]:206
$ [Information - LoggerBroker.cpp:152]: ForceAverage [0:0]:20.342940

Exercises

Ex. 1: Implement the mass spring model with the MathExpressionGAM

The MathExpressionGAM can be used to implement the mass-spring-damper model by defining the equations of motion as mathematical expressions. This allows implementing the model without the need to write a custom GAM, while still benefiting from the features of the MathExpressionGAM.

Consider the continuous-time state-space model of the spring–mass system:

\[\dot{x}(t) = A x(t) + B u(t)\]

where the state vector is defined as:

\[\begin{split}x(t) = \begin{bmatrix} x_1(t) \\ x_2(t) \end{bmatrix} = \begin{bmatrix} \text{position} \\ \text{velocity} \end{bmatrix}\end{split}\]

and the system dynamics are:

\[\dot{x}_1 = x_2\]
\[\dot{x}_2 = -\frac{k}{m}x_1 - \frac{c}{m}x_2 + \frac{1}{m}u\]

Using forward Euler discretisation with sampling time \(T_s\):

\[x[k+1] = x[k] + T_s \dot{x}[k]\]

the discrete-time equations become:

\[x_1[k+1] = x_1[k] + T_s x_2[k]\]
\[x_2[k+1] = x_2[k] + T_s\left(-\frac{k}{m}x_1[k] - \frac{c}{m}x_2[k] + \frac{1}{m}u[k]\right)\]
  1. Edit the file ../Configurations/MassSpring/RTApp-MassSpring-9.cfg and modify the existing MathExpressionGAM, named GAMMathModel, to compute the equations above.

  2. Connect the output of the GAMMathModel to the GAMDisplay to log the position and velocity of the mass. In order to avoid conflicting name definitions, call the signals PositionM and VelocityM.

Solution

The solution is to modify the GAMMathModel and add the equations to the Expression field.

Updated configuration with the GAMMathModel.
 1        +GAMMathModel = {
 2            Class = MathExpressionGAM
 3            Expression = "m=1.0;
 4                          c=0.5;
 5                          k=10.0;
 6                          dt=0.01;
 7                          PositionM = PositionM_1 + dt * VelocityM;
 8                          VelocityM = VelocityM + dt * (-k/m * PositionM_1 - c/m * VelocityM + Force / m);
 9                          PositionM_1 = PositionM;"
10            InputSignals = {
11                Force = {
12                    DataSource = DDB1
13                    Type = float64
14                } 
15                VelocityM = {
16                    DataSource = DDB1
17                    Type = float64
18                }
19                PositionM_1 = {
20                    DataSource = DDB1
21                    Type = float64
22                }
23            }
24            OutputSignals = {
25                PositionM_1 = {
26                    DataSource = DDB1
27                    Type = float64
28                }
29                PositionM = {
30                    DataSource = DDB1
31                    Type = float64
32                }
33                VelocityM = {
34                    DataSource = DDB1
35                    Type = float64
36                }
37            }
38        }

Ex. 2: Compare the output from both models

When running the application, the output given by the GAMMathModel and the one computed by the GAMSpringMass are similar but not exactly the same.

  1. Edit the file ../Configurations/MassSpring/RTApp-MassSpring-10.cfg and add a MathExpressionGAM to compute the difference between the Position value computed by the two models.

  2. Why is the PositionM value never converging to the target value? What could be done to modify this behaviour?

Solution

The solution is to add a MathExpressionGAM to compute the difference.

Computation of the PositionErr.
 1        +GAMMathPositionErr = {
 2            Class = MathExpressionGAM
 3            Expression = "PositionErr = Position - PositionM;"
 4            InputSignals = {
 5                Position = {
 6                    DataSource = DDB1
 7                    Type = float64
 8                }
 9                PositionM = {
10                    DataSource = DDB1
11                    Type = float64
12                }
13            }
14            OutputSignals = {
15                PositionErr = {
16                    DataSource = DDB1
17                    Type = float64
18                }
19            }
20        }

Add the computed signal to the GAMDisplay to log the error between the two models.

Computation of the PositionErr.
 1        +GAMDisplay = {
 2            Class = IOGAM            
 3            InputSignals = {
 4                PositionErr = {
 5                    DataSource = DDB1
 6                    Type = float64
 7                }
 8            OutputSignals = {
 9                PositionErr = {
10                    DataSource = Display
11                    Type = float64
12                }

Add the GAM to the execution list.

GAM execution list.
1            +Threads = {
2                Class = ReferenceContainer
3                +Thread1 = {
4                    Class = RealTimeThread
5                    CPUs = 0x1
6                    Functions = {GAMPerfMonitor GAMTimer GAMReference GAMController GAMSpringMass GAMMathModel GAMMathPositionErr GAMStats GAMPatchForceDims GAMForceStats GAMHist GAMMathExpr GAMDisplay}
7                }
8            }
9        }        

The PositionM value is never converging to the target value because the GAMController is using the Position value as feedback. Try modifying it to use the PositionM value instead.