从上图来看,S-function可支持多种语言编写,本文为MATLAB版使用笔记。
博主个人认为,S-function可以看成一个单独的系统,在Simulink中,如果使用MATLAB Function模块,每次执行,仅仅是调用函数而已,无法记录上一次调用后产生的状态,而S-function则可以保存上一时刻状态,因此我认为这种情况下使用S-function能极大程度简化仿真过程。当然,是在不使用Simscape等一些可视化仿真的情况下~
个人使用过程的总结,如有错误还请指出!
S-function,即系统函数(System Function)的简称。S-函数由一种特定的语法构成,用来描述并实现连续系统、离散系统以及复合系统等动态系统;S-函数能够接受来自Simulink求解器的相关信息,并对求解器发出的命令作出适当的响应,这种交互作用非常类似于Simulink系统模块与求解器的交互作用。
[sys,x0,str,ts]=functionName(t,x,u,flag,p1,p2,…)
% t为当前时间
% x为相应S-function模块的状态向量
% u是块的输入
% flag 用来指定被需要执行的任务
% p1,p2,...是模块参数
在模型仿真过程中,Simulink反复调用functionName
,对于特定的调用使用flag
来指示需执行的任务。
MATLAB安装目录R2019b\toolbox\simulink\blocks\sfuntmpl.m
给出了S-function的模板。
标准格式如下:
仿真阶段 | 被执行的程序 | 对应的flag |
---|---|---|
初始化 | mdlInitializeSizes | 0 |
计算下一步的采样步长(仅用于变步长模块) | mdlGetTimeOfNextVarHit | 4 |
计算输出 | mdlOutputs | 3 |
更新离散状态 | mdlUpdate | 2 |
计算导数 | mdlDerivatives | 1 |
结束仿真时的任务 | mdlTerminate | 9 |
Simulink传递如下参数给S-function:
对于ts
:
ts=[0,0]
ts=[-1,0]
ts=[0.25,0.1]
可创建一个S-function按不同速率执行不同任务!
在mdlInitializeSizes
子函数开头必须先调用simsizes
,将S-function信息加载到sizes中:
sizes = simsizes;
sizes结构说明如下:
结构 | 说明 |
---|---|
sizes.NumContStates | 连续状态的数量 |
sizes.NumDiscStates | 离散状态的数量 |
sizes.NumOutputs | 输出的数量 |
sizes.NumInputs | 输入的数量 |
sizes.DirFeedthrough | 直接馈通标志 |
sizes.NumSampleTimes | 采样时间的数量 |
设置完成以上信息后,需要再次调用simsizes
,将其传递给保持Simulink所用信息的向量sys
sys = simsizes(sizes);
function [sys,x0,str,ts,simStateCompliance] = sfuntmpl(t,x,u,flag)
% 主函数
switch flag,
% Initialization %
case 0,
[sys,x0,str,ts,simStateCompliance]=mdlInitializeSizes;
% Derivatives %
case 1,
sys=mdlDerivatives(t,x,u);
% Update %
case 2,
sys=mdlUpdate(t,x,u);
% Outputs %
case 3,
sys=mdlOutputs(t,x,u);
% GetTimeOfNextVarHit %
case 4,
sys=mdlGetTimeOfNextVarHit(t,x,u);
% Terminate %
case 9,
sys=mdlTerminate(t,x,u);
% Unexpected flags %
otherwise
DAStudio.error('Simulink:blocks:unhandledFlag', num2str(flag));
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% 以下为子函数定义 %%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [sys,x0,str,ts,simStateCompliance]=mdlInitializeSizes
sizes = simsizes;% 必须首先调用且必须存在
sizes.NumContStates = 0;% 连续状态数
sizes.NumDiscStates = 0;% 离散状态数
sizes.NumOutputs = 0;% 输出个数
sizes.NumInputs = 0;% 输入个数
sizes.DirFeedthrough = 1;% 1为存在直接馈通,0为不存在
sizes.NumSampleTimes = 1;% 采样时间个数,至少是1
sys = simsizes(sizes);
x0 = [];% 初始状态
str = [];% str 置空
ts = [0 0];% 初始化采样时间数组
function sys=mdlDerivatives(t,x,u)
%该函数可无
sys = [];%表示状态导数,即dx
function sys=mdlUpdate(t,x,u)
%每个仿真步都会调用该函数,在此描述离散状态方程和其他每个仿真步长必须执行的过程
sys = [];
function sys=mdlOutputs(t,x,u)
%该函数必须存在
sys = [];
function sys=mdlGetTimeOfNextVarHit(t,x,u)
%计算下一个采样时间,仅在系统为变采样时间系统时调用
sampleTime = 1; % 设置下一个采样时间为1s以后
sys = t + sampleTime;%
function sys=mdlTerminate(t,x,u)
%仿真结束时调用,在此完成仿真结束的收尾工作
sys = [];
,
隔开Edit
进入编辑模式,以增益模块为例:
function [sys,x0,str,ts,simStateCompliance] = mySfunDemo1(t,x,u,flag,gain)
switch flag,
case 0,
[sys,x0,str,ts,simStateCompliance]=mdlInitializeSizes;
case 1,
sys=mdlDerivatives(t,x,u);
case 2,
sys=mdlUpdate(t,x,u);
case 3,
sys=mdlOutputs(t,x,u,gain);
case 4,
sys=mdlGetTimeOfNextVarHit(t,x,u);
case 9,
sys=mdlTerminate(t,x,u);
otherwise
DAStudio.error('Simulink:blocks:unhandledFlag', num2str(flag));
end
function [sys,x0,str,ts,simStateCompliance]=mdlInitializeSizes
sizes = simsizes;
sizes.NumContStates = 0;
sizes.NumDiscStates = 0;
sizes.NumOutputs = 1;
sizes.NumInputs = 1;
sizes.DirFeedthrough = 1;
sizes.NumSampleTimes = 1;
sys = simsizes(sizes);
x0 = [];
str = [];
ts = [0 0];
simStateCompliance = 'UnknownSimState';
function sys=mdlDerivatives(t,x,u)
sys = [];
function sys=mdlUpdate(t,x,u)
sys = [];
function sys=mdlOutputs(t,x,u,gain)
sys =gain*u;
function sys=mdlGetTimeOfNextVarHit(t,x,u)
sampleTime = 1;
sys = t + sampleTime;
function sys=mdlTerminate(t,x,u)
sys = [];
封装变量输入框到模块:
注意,封装后,双击模块无法再打开设定输入参数的界面,此时按如下操作:
快捷键:Ctrl+U
设置模块输入的变量:
参考《MATLAB Simulink系统仿真超级学习手册》