From the principal window, choose File / Save as and save your eighth application under a new folder of yout tutorial folder (e.g. my_example8) with the name example8.
In the seven previous examples we have learnt how to use SynDEx’s GUI to create architectures, algorithms, launch adequation, obtain executive files... Now, we have sufficient knowledge to perform a simple automatic control application that will be executed on a multiprocessor architecture.
First the application is described and the system is defined in Scicos (the block diagram editor of the Scilab software1). Second the corresponding SynDEx application is created (using the Example 1 to 3 of the tutorial). This needs the generation of some C code following the method discussed in Example 7. Finally, we compile the application to obtain executable for several processors as it has been shown in Examples 6 and 7. SynDEx generates the code necessary to the communication between the processors.
We consider a system of two cars. The second car C2 follows the car C1 trying to maintain the distance l while the acceleration and the deceleration of C1. We call: x1(t) the position of the first car; x2(t) the position of the second car plus l; x_1(t) and x_2(t) the speeds of two cars. We denote k1 and k2 the inverse of the car masses. We call r(t) the reference speed chosen by the first driver. We suppose that we are able to observe the speed of the first car and the distance between the cars.
We have the following fourth order (four degrees of liberty) system:
x1= k1 u1 x2= k2 u2 y1= x_1 y2= x1 − x2 (1) |
We will decompose the system into mono-input mono-output system S1 (u1,y1) and S2 (u2,y2). Denoting by uppercase letter the Laplace transform of the variables, we have Y1=k1 U1/s and Y2=(k1 U1 − k2 U2)/s2 where U1 is seen as a perturbation that we want reject in the second system.
A first proportional feedback U1=ρ1(R−Y1) will insure the first car to follow the reference speed. The second controller will be proportional derivative U2=ρ2 Y2+ρ3 s Y2 (in fact we will suppose in the following diagram that the derivative of y2 is also observed). The coefficient ρ1 is obtained by placing the pole of the first loop:
Y1=ρ1k1R/(s+ρ1k1). |
The coefficient ρ2 and ρ3 are obtained by placing the pole of the transfer from U1 to Y2 in the closed loop system which is given by:
Y2=U1k1/(s2+k2ρ3s +k2ρ2). |
The purpose of the controller of the C1 car is to follow the reference in speed given the first driver. It stabilizes the C1 speed around its reference speed by using pole placement. For example, gains are respectively: (0, -5, 0, 0, -5). The controller of second car stabilizes the distance between the two cars. It stabilizes y2 around 0 by pole placement. For example, gains are respectively: (4, 4,-4,-4).
The controler of C2 knows these informations and sends them electronically to C1. This remark is available for C1.
Our controllers are simple. They are represented in figures 8.1 and 8.3 in Scicos and figures 8.2 and 8.4 in SynDEx:
We associate C source code to each function definition: gain and nary-sums. The code is inserted for the default processor.
A gain is a function that multiplies its input by a coefficient given as a parameter, named GAIN. After adding this parameter, open the code editor of the gain definition and write the following code in the loop phase of the default processor:
@OUT(out_1)[0] = @IN(in_1)[0] * @PARAM(GAIN);
We have three different forms of sum depending of its arity: two, four or five input ports:
@OUT(out_1)[0] = @IN(in_1)[0] + @IN(in_2)[0];
@OUT(out_1)[0] = @IN(in_1)[0] + @IN(in_2)[0] + @IN(in_3)[0] + @IN(in_4)[0];
@OUT(out_1)[0] = @IN(in_1)[0] + @IN(in_2)[0] + @IN(in_3)[0] + @IN(in_4)[0] + @IN(in_5)[0];
In a real application, our job stops with the SynDEx’s adequation of the two controllers on their associated architectures. Nevertheless, for pedagogic reasons, we will simulate the whole system (with the dynamics of the cars) in the aim to verify that our application does the same job that Scicos.
SynDEx is only used in discrete time model (not continuous time) and is not able to manage implicit algebraic loop. That is, in SynDEx, any loop contains at least a delay 1/z. Therefore, our application which is a continuous time dynamic system described in Scicos, must be discretized in time to be used in SynDEx.
The differential equation ẋ=u is discretized using the simplest way: the Euler scheme. Let us denote by h the step of the discretization and x0 an arbitrary initial value, the discretized system can be written as:
xn+1 − xn= uh (2) |
Finally, the system is given in Scicos in the figure 8.5 and 2 is given in SynDEx in the figure 8.6. Notice that the variable h is stored in the Scicos context, and used in the input of the gain and the clock definition. In SynDEx, h is defined as parameter in the definition of a gain and the clock definition is directly used in the source code associated with operations.
Create the integrale_discrete_sup algorithm (cf. figure 8.6). Notice that pas is of type gain_def with parameter GAIN equal to 0.001, sommateur is of type sommateur2_def, and retard is of type float/delay<{0};1>.
The car dynamics are given with Scicos block diagrams
in the figure 8.7
and with SynDEx operations
in the figure 8.8,
where the input 1 (ref)
is the acceleration of the car.
The first integral gives the speed of the car and the second its position.
Create the mecanique_sup algorithm (cf. figure 8.8). Notice that puissancemoteur is of type gain_def with parameter GAIN equal to 1 whereas integrale1 and integrale2 are of type integrale_discrete_sup.
In the following diagrams (from 8.9 to 8.12), the blocks (operations) denoted by meca are the car dynamics. Let us get the controllers of the two cars.
Create the voiture1_sup and voiture2_sup algorithms (cf. figures 8.10 and 8.12). Notice that meca1 and meca2 are references to mecanique_sup whereas control1 (resp. control2) is a reference to controleur1_sup (resp. controleur2_sup).
Create the following definitions (definitionName<PARAM>):
Then create algomain. Create the reference speed ref_vit of definition senseur_def<0>, the reference vitesse x_1 of definition vitesse_def<1>, the reference distance between the two cars of definition scope_def<2>, the reference vehicule1 of definition voiture1_sup and the reference vehicule2 of definition voiture2_sup, connect them according to the figure 8.13.
We associate C source code to each function definition: input and two kinds of output.
In our Scicos application an input is a square wave generator. As a rule, we will simulate a square wave generator by reading values in a text file (named ref_vitesse.txt). We will use the fopen, the fclose and the fscanf functions (stdio.h library). We will also use assertions (assert.h library) to ensure that the opening of a file has been successful.
For the moment, let suppose that it exists an array of FILE* (the structure returned by the fopen function) called fd_array and a variable called timer to simulate a pseudo-timer. Our sensor has a parameter called POSI_ARRAY to remember the position of the FILE* structure in the array.
Now, open the code editor of the senseur_def sensor and write the following code in the init phase of the default processor:
timer = 0; fd_array[@PARAM(POSI_ARRAY)] = fopen("ref_vitesse.txt", "r"); assert(fd_array[@PARAM(POSI_ARRAY)] != NULL);
In the Scicos application, we have defined the clock period of the square wave generator to the value 5 and the step of discretization h to the value 0.001. Thus we need, in the SynDEx application, to send 5000 times the same value. To count, we use the variable timer. All the 5000-th times, we read a new value in the file.
Write the following code in the loop phase of the default processor:
timer = (timer + 1) % 5000; if (timer == 1) fscanf(fd_array[@PARAM(POSI_ARRAY)], "%f\n", &data); @OUT(out_1)[0] = data;
We need to free memory by closing the file. Write the following code in the end phase of the default processor:
fclose(fd_array[@PARAM(POSI_ARRAY)]);
An output saves in a file the values of the system states. Thus, an output has a parameter called POSI_ARRAY to remember the position of the array where the stream has been saved. Open the code editor of the vitesse_def actuator and write the following code in the init phase of the default processor:
fd_array[@PARAM(POSI_ARRAY)] = fopen("actuator_@TEXT(@PARAM(POSI_ARRAY))", "w"); assert(fd_array[@PARAM(POSI_ARRAY)] != NULL);
The loop phase, allows to save the values:
fprintf(fd_array[@PARAM(POSI_ARRAY)], "%E\n", @IN(in_1)[0]);
We need to free the memory by closing the file. Write the following code in the end phase:
fclose(fd_array[@PARAM(POSI_ARRAY)]);
Contrary to the first type of output, this output has two input ports but the init and end source codes are identical. The loop phase differs. Open the code editor of the scope_def actuator and write the following code in the init phase of the default processor:
fprintf(fd_array[@PARAM(POSI_ARRAY)], "%E\n", (@IN(in_1)[0] - @IN(in_2)[0]));
SynDEx’s code generation will create the example8_sdc.m4x file (as explained in Example 7):
define(‘example8_algomain’,‘algomain’) define(‘algomain’,‘ifelse( processorType_,processorType_,‘ifelse( MGC,‘INIT’,“”, MGC,‘LOOP’,“WARNING: empty code for macro $0 in loop phase”, MGC,‘END’,“”)’)’) define(‘example8_controleur1_sup’,‘controleur1_sup’) define(‘controleur1_sup’,‘ifelse( processorType_,processorType_,‘ifelse( MGC,‘INIT’,“”, MGC,‘LOOP’,“WARNING: empty code for macro $0 in loop phase”, MGC,‘END’,“”)’)’) define(‘example8_controleur2_sup’,‘controleur2_sup’) define(‘controleur2_sup’,‘ifelse( processorType_,processorType_,‘ifelse( MGC,‘INIT’,“”, MGC,‘LOOP’,“WARNING: empty code for macro $0 in loop phase”, MGC,‘END’,“”)’)’) define(‘example8_gain_def’,‘gain_def’) define(‘gain_def’,‘ifelse( processorType_,processorType_,‘ifelse( MGC,‘INIT’,“”, MGC,‘LOOP’,“WARNING: empty code for macro $0 in loop phase”, MGC,‘END’,“”)’)’) define(‘example8_integrale_discrete_sup’,‘integrale_discrete_sup’) define(‘integrale_discrete_sup’,‘ifelse( processorType_,processorType_,‘ifelse( MGC,‘INIT’,“”, MGC,‘LOOP’,“WARNING: empty code for macro $0 in loop phase”, MGC,‘END’,“”)’)’) define(‘example8_scope_def’,‘scope_def’) define(‘scope_def’,‘ifelse( processorType_,processorType_,‘ifelse( MGC,‘INIT’,“”, MGC,‘LOOP’,“WARNING: empty code for macro $0 in loop phase”, MGC,‘END’,“”)’)’) define(‘example8_senseur_def’,‘senseur_def’) define(‘senseur_def’,‘ifelse( processorType_,processorType_,‘ifelse( MGC,‘INIT’,“”, MGC,‘LOOP’,“WARNING: empty code for macro $0 in loop phase”, MGC,‘END’,“”)’)’) define(‘example8_sommateur2_def’,‘sommateur2_def’) define(‘sommateur2_def’,‘ifelse( processorType_,processorType_,‘ifelse( MGC,‘INIT’,“”, MGC,‘LOOP’,“WARNING: empty code for macro $0 in loop phase”, MGC,‘END’,“”)’)’) define(‘example8_sommateur4_def’,‘sommateur4_def’) define(‘sommateur4_def’,‘ifelse( processorType_,processorType_,‘ifelse( MGC,‘INIT’,“”, MGC,‘LOOP’,“WARNING: empty code for macro $0 in loop phase”, MGC,‘END’,“”)’)’) define(‘example8_sommateur5_def’,‘sommateur5_def’) define(‘sommateur5_def’,‘ifelse( processorType_,processorType_,‘ifelse( MGC,‘INIT’,“”, MGC,‘LOOP’,“WARNING: empty code for macro $0 in loop phase”, MGC,‘END’,“”)’)’) define(‘example8_vitesse_def’,‘vitesse_def’) define(‘vitesse_def’,‘ifelse( processorType_,processorType_,‘ifelse( MGC,‘INIT’,“”, MGC,‘LOOP’,“WARNING: empty code for macro $0 in loop phase”, MGC,‘END’,“”)’)’) define(‘example8_voiture1_sup’,‘voiture1_sup’) define(‘voiture1_sup’,‘ifelse( processorType_,processorType_,‘ifelse( MGC,‘INIT’,“”, MGC,‘LOOP’,“WARNING: empty code for macro $0 in loop phase”, MGC,‘END’,“”)’)’) define(‘example8_voiture2_sup’,‘voiture2_sup’) define(‘voiture2_sup’,‘ifelse( processorType_,processorType_,‘ifelse( MGC,‘INIT’,“”, MGC,‘LOOP’,“WARNING: empty code for macro $0 in loop phase”, MGC,‘END’,“”)’)’)
You will not can use directly the SynDEx’s generated example8.m4x generic file because both the creation of local variable and the call of libraries is missing. After the code generation, you will must handwrite it to obtain the following code:
define(‘dnldnl’,“// ”) define(‘NOTRACEDEF’) define(‘NBITERATIONS’,“20000”) define(‘BINPWD’, ‘pwd’) define(‘RSHELL’, ‘ssh’) define(‘proc_init_’,‘ FILE *fd_array[10]; float data; int timer;’) include(‘example8_sdc.m4x’) divert divert(-1) divert‘’dnl
Where the macro proc_init_ allows the local variable declaration to be declared because it inserts its source code between the main function and the operations initialization. Notice that the main loop of the program is defined generically with a loop of NBITERATIONS where NBITERATIONS is initialized with the size of the input file (ref_vitesse.txt). Finally, the call of libraries is inserted after the include of the example8_sdc.m4x file.
Scicos software allows to simulate models in a window (cf. figure 8.14), where the values of three states are plotted (ordinate axle) according to the time (abscissa axle). We have:
Thanks the diagram, the system is stable (plots do not grow exponentially) and so it works. We do not continue to ameliorate the controllers job.
In this subsection, we suppose that the architecture is constituted of an only operator named root:
float/delay = 1 gain_def = 1 scope_def = 1 senseur_def = 2 sommateur2_def = 2 sommateur4_def = 1 sommateur5_def = 2 vitesse_def = 1
First, launch the adequation. It modifies example8.sdc and example8.sdx files.
Then, generate the executive and applicative files (setting Code / Generate m4x Files). It creates example8.m4, example8.m4x, example8_sdc.m4x, and root.m4 files.
Finally, handwrite the example8.m4x file as explained in 8.4.6.
First, generate manually a GNUmakefile containing:
A = example8 M4 = gm4 export ArchiMacros_Path = ../../../macros/archi_libraries export AlgoMacros_Path = ../../../macros/algo_libraries export M4PATH = $(ArchiMacros_Path):$(AlgoMacros_Path) CFLAGS = -DDEBUG VPATH = $(M4PATH) .PHONY: all clean all : $(A).mk $(A).run clean :: $(RM) $(A).mk *~ *.o *.a *.c actuator_* $(A).mk : $(A).m4 syndex.m4m U.m4m $(M4) $< >$@ root.libs = P1.libs = include $(A).mk
Where:
Then, copy-paste the ref_vitesse.txt file from the Example 8 folder to yours.
Then, type the command gmake in a shell commands interpreter. It creates actuator_1, actuator_2, example8.mk, root, root.c, and root.root.o files:
In this subsection, we suppose that the architecture is constituted of two operators named root and pc1, of type U and linked with a medium tcp1 of type TCP:
First, launch the adequation. It modifies example8.sdc and example8.sdx files.
Then, generate the executive and applicative files (setting Code / Generate m4x Files). It creates example8.m4, example8.m4x, example8_sdc.m4x, root.m4, and pc1.m4 files.
Finally, handwrite the example8.m4x file as explained in 8.4.6.
First, generate manually the GNUmakefile, the example8.m4m, and the root.m4x files:
$(A).mk : $(A).m4 syndex.m4m U.m4mis changed by:
$(A).mk : $(A).m4 syndex.m4m U.m4m $(A).m4m
Then, copy-paste the ref_vitesse.txt file from the Example 8 folder to yours.
Then, type the command gmake in a shell commands interpreter. It creates actuator_1, actuator_2, example8.mk, root, root.c, root.root.o, pc1, pc1.c, and pc1.pc1.o files:
In this subsection, we suppose that the architecture is constituted of five operators named root,cont1, cont2, dyna1, and dyna2, of type U and linked with a medium bus of type TCP:
First, launch the adequation. It modifies example8.sdc and example8.sdx files.
Then, generate the executive and applicative files (setting Code / Generate m4x Files). It creates example8.m4, example8.m4x, example8_sdc.m4x, root.m4, cont1.m4, cont2.m4, dyna1.m4, and dyna2.m4 files.
Finally, handwrite the example8.m4x file as explained in 8.4.6.
First, generate manually the Makefile.ocaml, the example8.m4m, the root.m4x, the cont1.m4x, the cont2.m4x, the dyna1.m4x, and the dyna2.m4x files:
define(‘cont1_hostname_’, HOSTNAME)dnl define(‘cont2_hostname_’, HOSTNAME)dnl define(‘dyna1_hostname_’, HOSTNAME)dnl define(‘dyna2_hostname_’, HOSTNAME)dnlwhere HOSTNAME is substituted with the name of your remote station;
Then, copy-paste some files from the Example 8 folder to yours:
You will probably need to install camlp5 (see at http://pauillac.inria.fr/ ddr/camlp5/).
Then, type the command make -f Makefile.ocaml in a shell commands interpreter. It creates example8.cmi, example8.o, pa_example8.cmi, and <processor>.cmi, <processor>.cmx, <processor>.o, <processor>.opt for each processor.
Finally, launch separatly the five script files. At the end of their execution, the actuator_1 and actuator_2 files are created:
From the principal window, choose File / Close. In the dialog window, click on the Save button.