Simulink之——S函数

Simulink之——S函数

在电气工程专业的毕业设计,硕士论文以及课程报告中都少不了simulink模型仿真。而除了直接使用simulink模型库中的元器件之外,还有一种比较看起来高大上的S函数法来建造属于自己的模型。今天打算简单的介绍一下simulink中的S-函数相关内容,主要是以下几点:

  1. 什么是S函数?
  2. S-函数的工作方式
  3. 如何在模型中使用S函数
  4. 一个例子分析

一.什么是S函数?

S-函数是系统函数(System Function)的简称,在Simulink中用 非图形化的方式 来描述一个模块。一个完整的S-函数结构体系包含了 描述一个动态系统 所需要的全部能力。使用S-函数用户可以向Simulink模型中添加自己的模块,可以自由选择使用MATLAB、C、C++等语言来创建自己的模块。

二. S-函数的工作方式

若要创建属于自己的S-函数,必须先知道S-函数的工作方式,而要理解S-函数的工作方式,先得了解simulink仿真模型的过程。

Simulink 中的每个模块都有三个基本元素:输入向量、状态向量和输出向量。分别表示为 u x y 。它们的关系如下所示:

在 Simulink 模块的三个元素中, 状态向量是最 重要的 ,也是三者之中最灵活的一个概念。在 Simulink 中状态向量可以分为 连续状态、离散状态或两者的结合 。输入、输出及状态的关系可以用状态方程描述:

Simulink 在仿真时,将上述方程对应不同的仿真阶段,它们分别是计算模块的输出、更新离散状态、计算连续状态的微分。在仿真开始和结束,还包括 初始化和结束仿真 两个阶段。在每个阶段,Simulink都 反复地调用 模块。

为了深入了解 S-函数的工作原理,还需了解一个概念:仿真循环(Simulation loop)。

一个仿真循环就是由仿真阶段按一定顺序组成的执行序列。对于每个模块,经过一次仿真循环就是一个仿真步长, 而在同一个仿真步长中, 模型中各模块的仿真按照事先排好的顺序依次执行。

在仿真开始时Simulink 首先对模型进行初始化,此阶段不属于仿真循环的部分。在所有模块都初始化后,模块进入仿真循环, 在仿真循环的每个阶段,Simulink 都要调用模块或者 S函数

在调用模型中的S函数时,Simulink会调用用户定义的S函数的例程来实现每个仿真阶段要完成的任务。这些任务包括:

一、 初始化 :仿真开始前,Simulink 在这个阶段初始化S函数,完成的主要工作包括:
1 、初始化包含S函数所有信息的结构体SimStruct;
2、确定输入输出端口的数目和大小;
3、确定模块的采样时间;
4、分配内存和 Sizes 数组。
二、 计算下一个采样时刻 。如果模型使用变步长求解器,那么就需要在当前仿真步长内确定下一个采样点的时间,也即下一个仿真步长的大小;
三、 计算输出 :计算所有输出端口的输出值。
四、 更新离散状态 :此例程在每个仿真步长处都要执行一次,为当前时间的仿真循环更新离散状态;
五、 数值积分 :这个阶段只有模块具有连续状态和非采样过零点时才会存在。如果S函数存在连续状态,Simulink 就在细化的小时间步长中调用S函数的输出 mdlOutputs和微分 (mdlDerivatives)例程。如果存在非采样过零点,Simulink将调用 S函数中的输出 (mdlOutputs)和过零检测(mdlZeroCrossngs)例程,以定位过零点。

三. 如何在模型中使用S函数?

在编写M文件的S函数时,可以使用M文件的 S函数模板 sfuntmpl.m 文件。该文件包含了所有的S函数的例程, 包含1个主函数和6个子函数。 在主函数程序使用一个多分支语句 (Switch-case)根据标志将执行流程转移到相应的例程函数。主函数的参数 Flag 标志值是由系统(Simulink 引擎)调用时给出的。

打开模板文件的方法由两种,用户可以在 MATLAB 命令窗口中键入:
>> edit sfuntmpl
或者双击 User-defined Function \S-function Examples\M-file S-functions\Leveal-1 M-fileS-functions1\ Leveal-1 M-file template 模块。


%主函数
function [sys,x0,str,ts] = sfuntmpl(t,x,u,flag)
switch flag,
case 0,
[sys,x0,str,ts]=mdlInitializeSizes;
case 1,
sys=mdlDerivatives(t,x,u);
case 2,
sys=mdlUpdate(t,x,u);
case 3,
sys=mdlOutputs(t,x,u);
case 4,
sys=mdlGetTimeOfNextVarHit(t,x,u);
case 9,
sys=mdlTerminate(t,x,u);
otherwise
error(['Unhandled flag = ',num2str(flag)]);
end % 主函数结束,下面是各个子函数,即各个仿真例程


% 初始化例程子函数:提供状态、输入、输出、采样时间数目和初始状态的值。
function [sys,x0,str,ts]=mdlInitializeSizes
sizes = simsizes; % 生成 sizes 数据结构
sizes.NumContStates = 0; % 连续状态数,缺省为 0
sizes.NumDiscStates = 0; % 离散状态数,缺省为 0
sizes.NumOutputs = 0; % 输出量个数,缺省为 0
sizes.NumInputs = 0; % 输入量个数,缺省为 0
sizes.DirFeedthrough = 1; % 有无直接馈入,有取 1,无取 0,缺省为 1
sizes.NumSampleTimes = 1; % 采样时间个数,至少取 1
sys = simsizes(sizes); % 返回 sizes 数据结构所包含的信息

x0 = []; % 设置初始状态的值
str = []; % 保留变量,置为空矩阵,请忽略
ts = [0 0]; % 采样时间:[采样周期 偏移量],采样时间取 0 表示为连续系统


% 计算导数例程子函数: 计算连续状态的导数 ,用户需在此例程输入连续状态方程。
% 该子函数可以不存在。
function sys=mdlDerivatives(t,x,u)
sys = []; % sys 表示连续状态导数


% 状态更新例程子函数:计算离散状态的更新。
% 用户除了需在此输入离散状态方程外,还可以输入其它每个仿真步长都有必要执行的代码。
% 该子函数可以不存在。
function sys=mdlUpdate(t,x,u)
sys = []; % sys 表示下一个离散状态,即 x(k+1)


% 计算输出例程子函数: 计算模块输出。 该子函数必须存在 ,用户在此输入系统的输出方程。
function sys=mdlOutputs(t,x,u)
sys = []; % sys 表示系统输出 y


% 计算下一个采样时间, 只有变采样时间系统才调用此仿真例程
function sys=mdlGetTimeOfNextVarHit(t,x,u)
sampleTime = 1; % 设置下一次的采样时间是 1s 以后
sys = t + sampleTime; % sys 表示下一个采样时间点


% 仿真结束调用的例程函数: 用户需在此输入结束仿真所需要的必要工作
function sys=mdlTerminate(t,x,u)
sys = [];

我们可以看到主函数包含 四个输出参数

sys 数组返回某个子函数,它的 含义随着调用子函数的不同而不同

x0为所有状态的初始化向量;

str 是保留参数,总是一个空矩阵;

Ts 是返回系统采样时间。
主函数的四个输入参数分别是采样时间 t,状态 x,输入 u 和仿真流程控制标志变量 flag。


输入参数后面还可以 附加一系列用户仿真需要的参数 。编写用户自己的S函数时,应将函数名sfuntmpl 改为 S-function 模块中设置的函数名


可能大家已经发现一个 令人困惑 的问题:不论在哪个仿真阶段,例程子函数的返回变量都是 sys。要搞清楚这个问题,还要 回到 Simulink 如何调用S函数上来 。前面讲过,Simulink 在每个仿真步长的仿真循环中的每个仿真阶段都要调用S函数。在调用时,Simulink 不但根据所处的仿真阶段为 flag 传入不同的值,还会 为返回变量 sys 指定不同的角色 。即是说尽管是相同的 sys 变量,但在不同的仿真阶段其意义是不相同的,这种变化由 Simulink 自动完成。

四.一个例子分析

光说不练假把式,下面就具体举个例子。

对于一个积分器,状态方程为:

\dot{x} =u
y=x

为了验证仿真结果,先用数学知识解出 y=\int_{0}^{t} u(t)dt+y(0)

当u(t)=2时,y=2t+y(0)

在这个例子中有一个输入,一个输出,一个连续状态变量。需要编写mdlDeruvatives子函数,目的是把状态变量x的导数通过sys返回。simulink仿真模型如下:

将MATLAB工作当前路径改为积分器m函数和模所在路径下,点击运行。此时会出现一个错误:告诉我们没有定义函数或者变量:'initial_state'

查看我们的S函数的m文件:

变量:'initial_state'是我们自己定义的一个变量,是一个额外的参数,这些参数必须在S函数的第一行的输入参数中列出。此时双击S函数模块,在参数表中写入初值y(0)=5。

再运行之,y的波形为:y=2t+5;

和理论计算一致。说明我们仿真结果正确。

这里只是举了一个很简单的例子,实际的课题研究中可能涉及到的研究对象要更加复杂,这里只是抛砖引玉,希望给初学者一些帮助。

编辑于 2016-11-01 10:01

文章被以下专栏收录