MV-7005: Linking Matlab/Simulink Generated Code (Simulink Coder) with MotionSolve
In this tutorial, you will learn how to generate a dynamic link library (DLL) using Simulink Coder (formerly Real-Time Workshop) and link it with MotionSolve to run a co-simulation.
- A working installation of MotionSolve (v12 and above)
- A working installation of MATLAB, Simulink, MATLAB Coder, and Simulink Coder
- A working installation of Microsoft Visual Studio (MSVS) 2010
Check for supported versions of MATLAB and MSVS at Supported Versions - Third Party Software in the XML Format Reference Guide.
This example uses MATLAB R2011b with MSVS 2010.
Prepare the MotionSolve Model
<Control_PlantInput
id = "30100100"
num_element = "2"
variable_id_list = "30100400, 30100500"
sampling_period = "0.01"
offset_time = "0.0"
label = "for controller 1"
usrsub_param_string = "USER(987654321)"
usrsub_dll_name = "rtw_BusSuspension2PMIMODiscrete"
usrsub_fnc_name = "PINSUB"
hold_order = "2"
/>
<Control_PlantOutput
id = "30100200"
num_element = "2"
variable_id_list = "30100200, 30100300"
sampling_period = "0.01"
offset_time = "0.0"
label = "for controller 1"
usrsub_param_string = "USER(987654321)"
usrsub_dll_name = "rtw_BusSuspension2PMIMODiscrete"
usrsub_fnc_name = "POUTSUB"
hold_order = "2"
/>
<Control_PlantInput
id = "30100300"
num_element = "2"
variable_id_list = "30100800, 30100900"
sampling_period = "0.01"
offset_time = "0.0"
label = "for controller 2"
usrsub_param_string = "USER(987654321)"
usrsub_dll_name = "rtw_BusSuspension2PMIMODiscrete"
usrsub_fnc_name = "PINSUB"
hold_order = "2"
/>
<Control_PlantOutput
id = "30100400"
num_element = "2"
variable_id_list = "30100600, 30100700"
sampling_period = "0.01"
offset_time = "0.0"
label = "for controller 2"
usrsub_param_string = "USER(987654321)"
usrsub_dll_name = "rtw_BusSuspension2PMIMODiscrete"
usrsub_fnc_name = "POUTSUB"
hold_order = "2"
/>
- Attribute
- Description
- usrsub_param_string
- Set this parameter equal to "USER(id)", where the ID is an integer (for
example, 123) that you choose. The ID identifies the Simulink Coder
library, links all Control_PlantInput's and
Control_PlantOutput's that use the library, and
must be unique.Note: There can be more than one Control_PlantInput/Control_PlantOutput per library.
- usrsub_dll_name
- The name of the DLL that is used (for example, from Simulink Coder).
- usrsub_fnc_name
- The name of the user function/subroutine that MotionSolve calls. This has to necessarily be "PINSUB" for Control_PlantInput and "POUTSUB" for Control_PlantOutput.
In this case, this MotionSolve model has been prepared for you. Copy the MotionSolve and Simulink models, rtw_BusSuspension2PMIMODiscrete.xml and rtw_BusSuspension2PMIMODiscrete.mdl, located in the motionsolve\cosimulation folder to your <working directory>.
Prepare the Simulink Model - Generating Code
The order of these Inport's and Outport's must match the order of the Control_PlantOutput's and Control_PlantInput's, respectively, in the MotionSolve model.
Modify, Compile and Link the Code to Create the DLL
At this point, Simulink Coder has generated source code for the Simulink model, but this must be modified and recompiled to generate the DLL required by MotionSolve. In the next steps, you will use a script to automatically compile and link this code and generate the Simulink Coder DLL that will be used in the MotionSolve model.
An example of the above command for this model is:
ms_rtw_pre
rtw_BusSuspension2PMIMODiscrete "C:\Program Files\Altair\<version>" "C:\Program Files (x86)\Microsoft Visual Studio 10.0" "win64"
- Automatically modifies the project settings and source files of the original solution generated by Simulink Coder
- Compiles and links the source code to generate a DLL that can be used with MotionSolve
You can confirm that this process has completed successfully by looking at the output in the command window. On successful execution, you should see something like the following:
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
1 file(s) copied.
** RTW dll is ready **
Run a MotionSolve Model with the Generated DLL
At this point, you simply need to point MotionSolve to the Simulink library to complete the co-simulation. By default, it assumes that it is in the same directory as the MotionSolve model.
Appendix A
This section discusses the access functions CoSimAPI_SimulinkRTW_Update_U(api,model,input) and CoSimAPI_SimulinkRTW_Update_Y(api,model,output). These are added to the Simulink model source code in order to help perform the co-simulation via the DLL.
CoSimAPI_SimulinkRTW_Update_U(void *api, const RT_MODEL_x x_M, ExternalInputs_x &x_U)
This method updates the input data structure in the Simulink Coder generated code with the output from MotionSolve.
The first argument requests a pointer to the API ID. The API ID is passed from the model XML in the line usrsub_param_string = "USER(987654320)". The first parameter in the USER() string is always the ID of the API. The MotionSolve API provides the method void * CoSimAPI_Get_API_Ptr(int api_id) to get the API pointer, where the api_id is the number specified in the XML file in the USER() string.
The second argument requests data structure x_M related to the generated Simulink Coder model information where 'x' is the name of the model. The x_M data structure is inherent to the Simulink code.
CoSimAPI_SimulinkRTW_Update_Y(void *api, const RT_MODEL_x x_M, const ExternalOutputs_x x_y)
This method updates the input for the MotionSolve solver with output from the RTW generated code.
The first and second arguments are the same as described in the previous section.
The last argument requests RTW output x_Y which is deposited to MotionSolve for that current time step, where x_Y is the data structure used by the Simulink Coder code to store the external outputs (see Appendix B).
Appendix B
This section describes the data structure that Simulink Coder generated code uses for representing the external input/output ports. In the following lines, the name of the model is assumed to be rtw_MS (rtw = Real Time Workshop, former name of Simulink Coder).
Typically, the Simulink Coder generated code (SCGC) uses the following notations:
Input port to Simulink with single channel | rtw_MS_U.In1, rtw_MS_U.In2 etc. |
Output port from Simulink with single channel | rtw_MS_Y.Out1, rtw_MS_Y.Out2 etc. |
Input port with multiple channels | rtw_MS_U.In1[0], rtw_MS_U.In1[1] etc. |
Output port with multiple channels | rtw_MS_Y.Out1[0], rtw_MS_Y.Out1[1]
etc. |
So for example, for a model with two Control_PlantInput (CPI) elements where the first has three channels and the second has two channels, the corresponding data structure in Simulink Coder code would be:
CPI #1: rtw_MS_U.In1[0], rtw_MS_U.In1[1] and rtw_MS_U.In1[2]
CPI #2: rtw_MS_U.In2[0] and rtw_MS_U.In2[1]
The same scheme is applicable for the data structure that handles Control_PlantOutput ports.
For example, if you name the first input to be myIn instead of In1, you need to make the following change to that function template:
double *u_ptr = (double *)&u.
myIn
;
to replace the original code:
double *u_ptr = (double *)&u.
In1
;