I would like to write a matlab function that wraps around a simulink block. The function should load data into the simulink model, run it, then return the data back from the function.
The only way I can think of doing this is by using the 'To Workspace' and 'From Workspace' blocks in simulink. The problem is that the 'From Workspace' block doesn't pick up variables from the function scope, only from the workspace scope.
Below is the only solution I could come up with, which basically converts the incoming vector to a string, and then creates a function which gets called when the model starts (effectively this is just as bad as eval).
Here is the code:
function [ dataOut ] = run_simulink( dataIn )
% Convert data to a string (this is the part I would like to avoid)
variableInitString = sprintf('simin = %s;', mat2str(dataIn));
% we need both the name and the filename
modelName = 'programatic_simulink';
modelFileName = strcat(modelName,'.slx');
% load model (without displaying window)
load_system(modelFileName);
% Set the InitFcn to the god awful string
% this is how the dataIn actually gets into the model
set_param(modelName, 'InitFcn', variableInitString);
% run it
sim(modelName);
% explicity close without saving (0) because changing InitFcn
% counts as changing the model. Note that set_param also
% creates a .autosave file (which is deleted after close_system)
close_system(modelName, 0);
% return data from simOut that is created by simulink
dataOut = simout;
end
And you run it like this: run_simulink([0 0.25 0.5 0.75; 1 2 3 4]') where the first part of the matrix is the time vector.
Finally here is the underlying simulink file with the workspace block properties open for completeness.
(If the image is fuzzy, click to enlarge)
Is there a more clean way to do this without the mat2str() and sprintf() ? The sprint line takes forever to run, even with vectors of size 50k.
It depends which version you are using. In recent version, you can specify the workspace to use as part of the call to the sim function, e.g.:
sim(modelName,'SrcWorkspace','current'); % the default is 'base'
For more details, see the documentation on sim. In older versions (not sure exactly when this changed, some time around R0211a or R0211b I think), you had to use simset, e.g.:
myoptions = simset('SrcWorkspace','current',...
'DstWorkspace','current',...
'ReturnWorkspaceOutputs', 'on');
simOut = sim(mdlName, endTime, myoptions);
Update
To return data back from sim in R2014b, you need to use an output argument when calling sim, which contains all the simulation outputs, e.g.:
simOut = sim(modelName,'SrcWorkspace','current'); % the default is 'base'
simOut is a Simulink.SimulationOutput object containing the time vector, the logged states and outputs of the model.
Related
For reading time-based data of variables from the Matlab workspace in Simulink (for time-based inter- and extrapolation), a typical solution is the use of the From Workspace block, by e.g. providing variable input data var in the following structure format:
var.time=[nx1]
var.signals.dimensions=m
var.signals.values=[nxm]
for providing the data of m variables with n time-based samples. Since variables' data is stored in a single matrix, it is implied that all variables are required to have the same (number of) time stamps.
If this is not the case, a solution could be the use of multiple From Workspace blocks, and corresponding variable input data structs (e.g. varA, varB, varC, etc.), like so:
However, this solution requires the number of variables to be fixed for all simulations. In my case, variables are numbered (rather than named) and the number of variables might not be the same from one simulation to the next. As such, for generalization purposes, I would like to find a solution without having to change the simulation file. A first step in this direction is the use of a struct array (i.e. var(1), var(2), var(3), etc.), which works:
A candidate next step is then to use a For Iterator to loop over N variables (and then to assign and concatenate the output in some output array, which I know can be done and is not the focus here):
The problem here is that the index id can't be fed to the From Workspace block! I ran into an identical issue when trying to solve the problem with a lookup table block. How to solve this problem of reading multiple but varying number of data variables from the workspace with different (number of) time stamps? Solutions that don't use a From Workspace or lookup table block are welcome too.
Here is equivalent code for running the whole thing in Matlab instead of Simulink:
% DEFINED IN MATLAB:
% Examplary input of variable data with different time stamps, according to
% Simulink structure conventions (.time, .signals etc)
N=3; % number of variables
for id=N:-1:1
var(id).time = linspace(0,2,id*2+2)';
var(id).signals.dimensions = 1;
var(id).signals.values = sin((var(id).time-id)*pi/2)';
end
% TO BE EXECUTED IN SIMULINK:
% the simulink clock time:
t = 1.234; % random time
for id=N:-1:1
var_t(id) = interp1(var(id).time,var(id).signals.values,t);
end
And here a figure to illustrate the results:
% Figure code:
figure(1),clf
colors=lines(N);
for id=1:N
plot(var(id).time,var(id).signals.values,'*-','color',colors(id,:)),hold on
plot(t,var_t(id),'o','color',colors(id,:))
end
xlabel('time'),ylabel('variable data')
I am trying to find a way to generate multiple intermediate SimState objects during a Simulink sim run, at predefined instants, letting the simulation run to its specified StopTime.
The documentation says it is only possible to save the complete final state, but maybe there's a way ?
If you need to run the model from the Simulink user interface then you'd need to write a custom block that would pause the model, save the simstate and then restart the simulation, at specific intervals. But the easier approach would be to run the model from the command line, doing something like the following:
% Define stop times and preallocate a cell array to save the simstates
stop_times = 1:10; % one second intervals upto 10 seconds
sim_states = cell(1,numel(stop_times));
% Run the model in a loop, saving the simstate at the required times
for tdx = 1:numel(stop_times)
if tdx == 1
% First simulation
sim_out = sim('mdl_name', 'StopTime', num2str(stop_times(tdx)), 'SaveFinalState', 'on', ...
'LoadInitialState', 'off', 'SaveCompleteFinalSimState', 'on',...
'FinalStateName', 'final_simstate');
else
% subsequent simulations
assignin('base', 'new_simstate', sim_states{tdx-1});
sim_out = sim('mdl_name', 'StartTime', num2str(stop_times(tdx-1)),...
'StopTime', num2str(stop_times(idx)), 'SaveFinalState', 'on', ...
'LoadInitialState', 'on','InitialState', 'new_simstate',...
'SaveCompleteFinalSimState', 'on',...
'FinalStateName', 'final_simstate');
end
% store the simstate
sim_states{tdx} = sim_out.get('final_simstate');
end
The above code assumes variables are loaded form the Base workspace but can easily be modified to get it from the model workspace or a function workspace.
Can I open a local Simulink MATLAB function block's code in the MATLAB editor via some command?
For example, let us say I have a Simulink model named mainModel.slx.
In it, there is a MATLAB function block named localFunction.
This is not defined in a .m-file.
I would be able to edit the function which path is mainModel/localFunction, without having to open the simulink window and double click on the function block. Is this possible?
I have of course already tried open mainModel/localFunction and edit mainModel/localFunction. I have access to the handle for its StateFlow.EMChart object.
EDIT: Minimal, (hopefully) Complete and Verifiable Example
My minimal Simulink model is shown in the picture below. Code is present below it. For readability, I have not addressed bugs or glitches. It is not for general usage.
The function code for the MATLAB function block localFunction is
function y = fcn(u)
y = 'findThis'; % I want to end up here, in the MATLAB editor!
end
I am using the following code to load the model, search through all MATLAB function blocks and find the ones containing the string 'findThis'. The MATLAB function block named 'localFunction' should then be found. Again, ignore the bugs. The code is saved in a script called tmpScript.m.
% User set
model = 'mainModel';
expression = 'findThis';
blockType = 'Stateflow.EMChart'; % MATLAB function block, right?
% Load model
load_system(model)
% Find all MATLAB function block handles
blockHandles = find(slroot, '-isa', blockType);
% Find first block containing the defined expression
for iHandle = 1:numel(blockHandles)
tmpFind = strfind(blockHandles(iHandle).Script, expression);
if ~isempty(tmpFind)
break
end
end
foundBlockPath = blockHandles(iHandle ).Path; % Function block path
foundCharIdx = tmpFind; % Character index
% Print results in command window
fprintf('Function path: %s\n', foundBlockPath)
fprintf('Character index: %d\n', foundCharIdx)
In this example, the path should be mainModel/localFunction and the character index 29 (Note the three leading white spaces on the function's second line, and that the line break '\n' is worth one characters). The command window shows
>> tmpScript
Function path: mainModel/localFunction
Character index: 29
>>
I can thus load models and search through their MATLAB function blocks for specific strings. When I have found this function, I would like to be able to open it in the matlab editor. What is called when I double click on the block in the Simulink window?
These do NOT work
open(foundBlockPath)
edit(foundBlockPath)
blockHandles(iHandle).openEditor
I cannot change the Simulink model itself. I do not want to change the function script. I just want to be able to open it in the MATLAB editor.
You can open the code in the Editor using,
view(blockHandles(iHandle))
You could change the Matlab function block to an Interpreted Matlab function block.
This does have the limitation that it only can have one input and one output (which can be vectors), so depending on your problem, you might have to mux/demux some data.
Alternatively you can change to an S-function, which gives more flexibility, but might be a bit more complex to setup.
I'm a super beginner in Simulink models and control systems.
I have .slx Simulink model for drone dynamics system.
It takes in two inputs (roll cmd, pitch cmd) and outputs velocity x, velocity y, position x, and position y.
From here, it seems like I can open the system by calling
open_system('myModel.slx', 'loadable');
But how do I put inputs and get output values?
Is there a way I can do this in a gui?
EDIT:
Here is the full layout of my model:
When I did
roll_CMD=10;
pitch_CMD=20;
I got a warning saying:
Input port 1 of 'SimpleDroneDynamics/...' is not connected.
How do I feed inputs using port numbers?
How do I get outputs with port numbers? I tried
[vx, vy, px, py] = sim('SimpleDroneDynamics.slx');
and got an error saying
Number of left-hand side argument doesn't match block diagram...
Is there a way to continuously feed inputs at every time step? This being controller module, I think I'm supposed to feed in different values based on output position and velocity.
EDIT2:
I'm using Matlab2017a
About the first two points of your question:
In simulink:
For the inputs you can use a constant block and when you double click the input block you can assign a value, which can be a workspace variable.
To get the outputs to your workspace you can use the simout block (make sure to put Save format to array).
Connect inputs to your simulink model
Connect outputs of your simulink model to the simout blocks.
MATLAB script
clc;
clear all;
roll = 10;
pitch = 20;
sim('/path_to_simulinkmodel.slx')
time = simout(:,1);
velocity_X = simout(:,2);
velocity_Y = simout(:,3);
position_X = simout(:,4);
position_Y = simout(:,5);
About the third point of your question
You can define the duration of your simulation in the block diagram editor. You can put a variable which is defined in the calling script. There are multiple ways of achieving time dependent input variables:
One option I personally don't recommend is using a for-loop and calling the simulink model with a different value of roll and pitch
for i = 1:numberOfTimesteps
roll = ...
...
sim('simulinkModel.slx')
end
A second and more efficient approach is changing the constant blocks to other source blocks like ramp signals or sinusoid signals
First of all Simulink model use main Matlab workspace. So you can change your variables values at command window (or just at your script) and run Simulink model.
There are several ways to initialize this constants for Simulink. One more useful way is to create script containing all your variables and load it at Simulink model starts. You can do it by adding script name in Simulink/Model Explorer/Callbacks. (There are different callbacks - on Loading, on Starting and etc.). Read more about this: here.
Now you can run your simulation using sim function:
sim('name_of_model')
name_of_model must contain path if model is not in the active MATLAB folder (active folder you can see in your matlab window just under the main menu).
There are different properties of sim function, read about them in help this can be useful for you. By the way: you can change some parameters of your model using sim. You even can find any block in your model and change it's properties. Read more about sim and about finding current blocks. Interesting that the last solution give you ability to change parameters during the simulation!
About getting output. After you run simulation you get tout variable in main workspace. It is an array of timesteps. But if you add outport block (like at my image) you also get another variable in workspace yout. yout is an Datasets. It contain all your outports values. For 2 outports for example:
yout
yout =
Simulink.SimulationData.Dataset
Package: Simulink.SimulationData
Characteristics:
Name: 'yout'
Total Elements: 2
Elements:
1 : ''
2 : ''
Get the values of any of outports:
yout.get(1).Values
it is a timeseries data type, so:
yout.get(1).Values.Time - give you times
yout.get(2).Values.Data - give you values of this outport at each time
We have one more method to take output values:
[t,x,y] = sim('model_name')
it returns double arrays. t- time array, y - matrix of all outports values (it already double and contain only values without times, but for each simulation time!)
So now you can create common Matlab GUI and work at this variables! There is no any difficulties. You can read more about GUI for Simulink here.
I have a function in Matlab which is getting continuous sensor values from a hardware. It gives a flag when new values are available and we can update the variables holding these values. Following is a dummy function to mimic what this function is doing.
function example( )
% Example function to describe functionality of NatNetOptiTrack
% Hardware initialization,
% Retriving real time information continuously
for i = 1:100 %in real time this loop runs for ever
data = rand(3,6);
% Send the updated data to gui in each iteration
end
end
i have made a gui using guide as shown in the figure:
So the data to be displayed is a 3x6 matrix with columns corresponding to X Y Z Roll Pitch and Yaw values while rows correspond to Objects.
I want to show the continuously updated values from this function on the gui. Is there a way i can initialize gui inside my example function and update the output value by using the handles inside my loop. I tried copying the gui code inside the example function as a script, it was able to initialize but was not recognizing the handles.
Also i want to show the current values on command window when i press the button.
Thanks
If you launch the GUI and then run the function, you should be able to get the handles to the controls on the GUI provided that you make the GUI figure handle visible and set its tag/name to something appropriate. In GUIDE, open the Property Inspector for the GUI and set the HandleVisibility property to on, and the Tag property to MyGui (or some other name). Then in your example.m file do the following
function example( )
% Example function to describe functionality of NatNetOptiTrack
% get the handle of the GUI
hGui = findobj('Tag','MyGui');
if ~isempty(hGui)
% get the handles to the controls of the GUI
handles = guidata(hGui);
else
handles = [];
end
% Hardware initialization,
% Retriving real time information continuously
for i = 1:100 %in real time this loop runs for ever
data = rand(3,6);
% update the GUI controls
if ~isempty(handles)
% update the controls
% set(handles.yaw,…);
% etc.
end
% make sure that the GUI is refreshed with new content
drawnow();
end
end
An alternative is to copy the example function code into your GUI - the hardware initializations could occur in the _OpeningFcn of your GUI and you could create a (periodic) timer to communicate with the hardware and get the data to display on the GUI.
As for displaying the current data when pressing the GUI button, you can easily do this by writing the contents of the GUI controls to the command line/window with fprintf. You will though need to make your example function interruptible so that the push button can interrupt that continuously running loop. You can do this by either adding a pause call (for a certain number of milliseconds) that gets executed at the end of each iteration of your loop, or just make use of the drawnow call from above (that is why I placed it outside of the if statement - so that it will be called on each iteration of your loop.
Try the above and see what happens!