Until recently, I always had trouble when dealing with MATLAB
function handles
. I cannot really explain why, there was just something I could not wrap my head around.
To help a user, I finally took the time to demystify that in my head and decided to share what I learned with you today.
In Simulink, the use cases requiring function handles that I run the most frequently into are:
In today's post, I will use a post-simulation callback for
sim
as an example.
The Example
For this example, I will use this simple model where I output signals
x
and
v
using root-level
Outport
blocks:
in = Simulink.SimulationInput(mdl);
out.yout
ans =
Simulink.SimulationData.Dataset 'yout' with 2 elements
Name
BlockPath
____
__________________1 [1x1 Signal] x massSpringDamper/x 2 [1x1 Signal] v massSpringDamper/v - Use braces { } to access, modify, or add elements using index.
A Simple Post Simulation Callback
在最简单的形式中,配置一个post-simulation callback is straightforward. Here is a screenshot from the documentation:
The input
func
is a handle to a function that receives the original
SimulationOutput
object as input, and returns a structure with the fields to be included in a new
SimulationOutput
object. For example, I define this function:
Then to specify that this function needs to execute after the simulation, I pass to
setPostSimFcn
a handle to
myPostSim
using the "
@
" character:
in = Simulink.SimulationInput(mdl);
in = in.setPostSimFcn(@myPostSim);
out = sim(in)
out =
Simulink.SimulationOutput: mean: [1x1 double] max: [1x1 double] min: [1x1 double] SimulationMetadata: [1x1 Simulink.SimulationMetadata] ErrorMessage: [0x0 char]
One thing to note, the
SimulationOutput
object returned in this case does not contain the original signal
yout
, it only contains the new fields created in the post-simulation function, along with the
SimulationMedata
and the
ErrorMessage
fields. This is especially useful if your simulation is producing a lot of data, but you are only interested in post-processed statistics, like in this case the mean, min and max.
If you want to keep the original data, you can simply re-assign it to the output. For the above example, this looks like:
When simulating, the SimulationOutput object contains the original
yout
and the newly computed values
in = Simulink.SimulationInput(mdl);
in = in.setPostSimFcn(@myPostSim2);
out = sim(in)
out =
Simulink.SimulationOutput: yout: [1x1 Simulink.SimulationData.Dataset] mean: [1x1 double] max: [1x1 double] min: [1x1 double] SimulationMetadata: [1x1 Simulink.SimulationMetadata] ErrorMessage: [0x0 char]
Additional Input Arguments
When scrolling through the documentation for
setPostSimFcn
, we also see this syntax:
This syntax is called an anonymous function. To learn more on those, I recommend going through
this documentation page
. This syntax allows you to pass inputs to the post-simulation function. For example, let's add two inputs to our previous function:
With those additional inputs, you first create an anonymous function that will receive the Simulation Output object
out
as input, and pass it to
myPostSim3
along with values for
bias
and
gain
:
myAnonymousFcn = @(out) myPostSim3(out,biasValue,gainValue);
in = Simulink.SimulationInput(mdl);
in = in.setPostSimFcn(myAnonymousFcn);
out = sim(in)
out =
Simulink.SimulationOutput: meanWithBias: [1x1 double] SimulationMetadata: [1x1 Simulink.SimulationMetadata] ErrorMessage: [0x0 char]
Functionally, this one line of code defining
myAnonymousFcn
is equivalent to create a wrapper function around
myPostSim3
that would look like this:
Here is an additional image that I hope will help differentiate the argument for which the value will be passed to the function when it will be called at the end of the simulation (Specified using
@(argumentName)
in front of the function signature), versus the arguments for which the value is passed immediately:
Specifying a method of an App or MATLAB Class as Callback Function
To illustrate this, I added two variables
x0
and
v0
specifying the initial position and velocity of the
Second Order Integrator
block.
To keep things simple, I will not use an App Designer app, but a simple class simulating the model multiple times with different initial values. The same principle would apply to an App developed in
App Designer
.
Here is what my simple class does:
- In the constructor, I store the name of the model to be simulated and userngto initialize the random number generator
- In therunSimmethod, I define a random valuex0that will be used as the same initial position for all simulations
- I define different random valuesv0to be used in each simulation
- I define the class methodthePostSimFcnas post-simulation callback and I pass to it the values ofx0andv0using additional arguments
- In thethePostSimFcn, I storex0andv0在模拟输出对象,以供将来使用
Things to note here are:
- The first argument of the post-simulation functionthePostSimFcnisobj, the object itself.
- When configuring the post-simulation callback, I refer to it asobj.thePostSimFcn因为它是一个这个类的方法。
I can then use my simple class with this code:
myObj = myClass('massSpringDamper');
plot(out(i).yout.get('x').Values);
plot(out(i).yout.get('v').Values);
legendTxt{i} = ['Run 'num2str(i)' - x0='num2str(out(i).x0)' - v0='num2str(out(i).v0)];
subplot(2,1,1); legend(legendTxt)
Now it's your turn
I hope these examples are useful for you. If you end up using post-simulation functions, I also recommend visiting this
previous post
where I provide guidelines to handle errors properly.
Do you have other use cases for function handles and anonymous functions in a Simulink context? Let us know in the comments below.
Comments
To leave a comment, please clickhereto sign in to your MathWorks Account or create a new one.