Simulink To C Python
Simulink To C Python
This section is oriented to give the steps for model in a discrete time simulation.
1. Simplification of the model. You should have a clear view of what part of your model is going
to be necessary for your simulations, and you should work towards having only the absolutely
necessary for your goals. The right inputs, outputs and integrators/state-holders. They should
also be properly named, as the code will keep your notation and this will make the code more
readable.
2. Discretisation of the model. Go to the Configuration Parameters Menu, change the solver to a
discrete time solver and choose the adequate time step. You’ll need a time step that is small
enough to grasp the dynamics of the system at hand, for a given final application. To do that,
you’ll need to transform any integrator or continuous-time transfer function into its discrete
equivalent and make them inherit their time step from the solver in their own options (-1). Any
slower sampling times (measures, controller actions) can be handled later, based on the
fundamental time step.
Before generating the code, you can check sample times by going to Display>Sample Times>Colors.
They should all be the same colour.
1
Author: Albert Bonet
ISAE-SUPAERO
3. Code Generation Options. There are plenty of options throughout this process, and it is
important not to change options from which we don’t understand the effects. Before
generating the code we have to go through the options menu.
a. System target file: Choosing “ert.tlc” will generate an understandable code, that will
allow us to pass parameters to specific Simulink functions, and this tutorial will follow
with this type of generation in mind.
You can either keep or change the build configuration, objectives, and check the
model. Checking the model will help you working towards the Prioritized objectives.
b. Interface: Here you should pay attention to the data inside your signals to choose all
the Support options. What you don’t have, you should not check since it will affect
readability and execution time.
For the Code interface you HAVE TO have Reusable Function and Pass root-level I/O as
individual arguments. For the error management you can choose as you please.
2
Author: Albert Bonet
ISAE-SUPAERO
Utility files
This section is intended to be the one where all the elemental data types and Matlab constants are
defined. Matlab has its own native data types and structures; these files allow referring to them and still
having C code. This means that you will no longer see native C data types as they are all redefined in this
section.
Data Files
Here you may find any user defined constants or other data inside the model. If there are any regression
curves or thresholds of any kind, they will be declared and introduced in these files.
3
Author: Albert Bonet
ISAE-SUPAERO
Model files
This is the core of the simulation. The ert-type generation, creates explicit functions in the
Model_name.c file for the Simulink’s 3 main simulation phases, which are:
Model_name_initialize: All variables are declared, states loaded and memory is allocated. It
might have other functionalities depending on the application/generation.
Model_name_step: This makes the simulation advance one time step. This means that states
and signals will be updated accordingly. This phase is intended to be called as many times as
needed, until the simulation is finished.
Model_name_terminate: This ends and closes the whole simulation, frees the memory space
and might have other functionalities depending on the application/generation.
In the Model_name.h file, you will find the structure and type definition, as well as the data types for
the system’s states (they will always come as structured data).
Having a clear view of the code and its structure is necessary for any C or Python application. Data types,
structures and variables need to be known.
Main file
ert_main.c is the simulation handler. It is the code in charge of calling all the functions and monitoring
the simulation. In this source file we will find the template for any final application that will use the
generated code. It is here where signals, timers and errors should be handled, implemented and
declared.
By default, this file does nothing. Nonetheless, it has all the vital structural parts with everything else
clearly indicated with auto-generated comments, and it is up to the user to mould the code to make the
simulation as desired. (“Auto-generate comments” is a default option of the generation that can be
unchecked).
Any future Python application should come to replace this source code, and should be able to call all the
Model_name.c functions with the proper data types.
For all intends and purposes, this tutorial is conceived with the idea of a Python application, so the
compiling procedure will be described for that objective in a UNIX platform.
We need to create a library that includes and links all the main functions and all the source and header
files, except for the main, since we will be making our own Python version. In UNIX, those libraries are
.so extension. We will be using the GCC built-in C compiler and we will build a SHARED C LIBRARY.
For that, set your path in the folder where all the generated code is. There, write the following lines in
the command line:
4
Author: Albert Bonet
ISAE-SUPAERO
In the fileX.c part you should really add ALL the .c files generated by Simulink. A big model might
potentially generate many .c files that will all be needed for the simulation to work as expected.
There are plenty of ways of having a library or application compiled, and you are free to choose the one
that better suits your purposes.
Python Integration
You should now have at your disposal your .so library. This will be the only file you are going to need in
the Python program path from now on.
In order to be able to call compiled C functions from foreign libraries into python, we are going to need
another library called “ctypes”. Please refer to the doc:
https://docs.python.org/3/library/ctypes.html
The best way to convey how the Python code should be implemented is via a detailed example. This
example will be equivalent to the ert_main.c, but instancing and declaring everything from python
through ctypes. Howerver, some important points should be made:
It is important to note that the simulation model works with two main structured variables.
One is going to hold the value of every state in the system. The other one is a pointer to that
first variable. Please note that they can also include some error handling variables.
In the ert_main.c file those variables are declared like:
To be able to declare the same structures in a ctypes manner we have to know the data types
and definitions and also its fields’. This information can be found in the Model_NAME.h file:
typedef struct {
real_T state1_DSTATE; /* '<S8>/state1 measure' */
real_T state2_DSTATE; /* '<S3>/state2 measure' */
real_T DiscreteTimeIntegrator1_DSTATE;/* '<S4>/Discrete-Time
Integrator1' */
uint8_T DiscreteTimeIntegrator_LOAD;/* '<S27>/Discrete-Time
Integrator' */
boolean_T G1_PreviousInput; /* '<S39>/G1' */
} DW_Model_NAME_T;
...
By going to these structures, we know the Matlab data types involved in the structured data fields. To
know the actual C data type, we should go to rtwtypes.h file, where we would see that “real_T” is in
fact type “double”, “uint8_T” is an “unsigned char”, and so on.
5
Author: Albert Bonet
ISAE-SUPAERO
As made explicit in the documentation of the ctypes library, the equivalence is between data types is as
follows:
class RT_MODEL_Model_NAME_T(Structure):
_fields_ = [("dwork",POINTER(DW_Model_NAME_T))]
Model_NAME_M = pointer(Model_NAME_M_)
Model_NAME_M_.dwork = pointer(Model_NAME_DW)
6
Author: Albert Bonet
ISAE-SUPAERO
#Initialize model
initialize(Model_NAME_M,
byref(Model_NAME_U_ref),
byref(Model_NAME_U_In2),
byref(Model_NAME_U_In3),
byref(Model_NAME_Y_Out1),
byref(Model_NAME_Y_measure));
#Input update
Model_NAME_U_ref = c_double(1)
Model_NAME_U_In2 = c_double(34.5)
Model_NAME_U_In3 = c_int(2)
#Step timer
timer = timer+time_step
7
Author: Albert Bonet