第6章S-函数6.1S-函数概述6.2S-函数的工作原理6.3编写M文件S-函数6.4编写CMEXS-函数6.1S-函数概述6.1.1S-函数的基本概念S-函数是系统函数(SystemFunction)的简称,是指采用非图形化的方式(即计算机语言,区别于Simulink的系统模块)描述的一个功能块。用户可以采用MATLAB代码,C,C++,FORTRAM或Ada等语言编写S-函数。S-函数由一种特定的语法构成,用来描述并实现连续系统、离散系统以及复合系统等动态系统;S-函数能够接收来自Simulink求解器的相关信息,并对求解器发出的命令做出适当的响应,这种交互作用非常类似于Simulink系统模块与求解器的交互作用。S-函数作为与其他语言相结合的接口,可以使用这个语言所提供的强大能力。例如,MATLAB语言编写的S-函数可以充分利用MATLAB所提供的丰富资源,方便地调用各种工具箱函数和图形函数;使用C语言编写的S-函数则可以实现对操作系统的访问,如实现与其它进程的通信和同步等。简单来说,用户可以从如下的几个角度来理解S-函数:(1)S-函数为Simulink的“系统”函数。(2)能够响应Simulink求解器命令的函数。(3)采用非图形化的方法实现一个动态系统。(4)可以开发新的Simulink模块。(5)可以与已有的代码相结合进行仿真。(6)采用文本方式输入复杂的系统方程。(7)扩展Simulink功能。M文件S-函数可以扩展图形能力,CMEXS-函数可以提供与操作系统的接口。(8)S-函数的语法结构是为实现一个动态系统而设计的(默认用法),其它S-函数的用法是默认用法的特例(如用于显示目的)。6.1.2如何使用S-函数前面简单介绍了S-函数的基本概念及其主要的功能。在动态系统设计、仿真与分析中,用户可以使用Functions&Tables模块库中的S-function模块来使用S-函数;S-function模块是一个单输入单输出的系统模块,如果有多个输入与多个输出信号,可以使用Mux模块与Demux模块对信号进行组合和分离操作。一般而言,S-函数的使用步骤如下:(1)创建S-函数源文件。创建S-函数源文件有多种方法,当然用户可以按照S-函数的语法格式自行书写每一行代码,但是这样做容易出错且麻烦。Simulink为我们提供了很多S-函数模板和例子,用户可以根据自己的需要修改相应的模板或例子即可。(2)在动态系统的Simulink模型框图中添加S-function模块,并进行正确的设置。(3)在Simulink模型框图中按照定义好的功能连接输入输出端口。/**my_sfunction**/#defineS_FUNCTION_NAMEmy_sfunction#defineS_FUNCTION_LEVEL2#includesimstruc.h/*====================**S-functionmethods**====================*//*Function:mdlInitializeSizes*/staticvoidmdlInitializeSizes(SimStruct*S){。。。S-函数对话框S-function块S-函数源文件图6.1S-函数块对话框【例6.1】使用S-函数实现系统:y=2u。解:(1)打开模板M文件S-函数模板文件sfuntmpl.m(版本不同timestwo),在\MATLABroot\work目录下另存为doublesfunction.m。(2)找到函数mdlInitializeSizes,修改以下代码:sizes.NumOutputs=1;sizes.NumInputs=1;图6.2一个用S-函数实现的简单系统(3)找到函数mdlOutputs,加入以下代码:sys=2*u;(到现在为止我们的第一个S-函数写完了。下面演示一下它的作用。)(4)在Simulink空白页中添加S-function块,打开S-function块对话框,参数S-functionname设置为doublesfunction。按照图6.2添加连接好其余的各个模块。(5)开始仿真,在Scope中观察输出结果,可以看到输入正弦信号被放大为原来的2倍,如图6.2所示。6.1.3与S-函数相关的一些术语理解下列与S-函数相关的一些基本术语对于用户理解S-函数的概念与编写都是非常有益的;而且这些概念在其它的仿真语言中也是会经常遇到的。1.仿真例程(Routines)Simulink在仿真的特定阶段调用对应的S-函数功能模块(函数),来完成不同的任务,如初始化、计算输出、更新离散状态、计算导数、结束仿真等,这些功能模块(函数)称为仿真例程或者回调函数(callbackfunctions)。表6.1列出了S-函数例程函数和对应的仿真阶段。关于仿真例程将在S-函数工作原理一节详细介绍。表6.1S-函数例程S-函数仿真例程仿真阶段mdlInitialization初始化mdlGetTimeofNextVarHit计算下一个采样点mdlOutput计算输出mdlUpdate更新离散状态mdlDerivatives计算导数mdlTerminate结束仿真2.直接馈通(Directfeedthrough)直接馈通意味着输出或可变采样时间与输入直接相关。在如下的两种情况下需要直接馈通:(1)某一时刻的系统输出y中包含某一时刻的系统输入u。(2)系统是一个变采样时间系统(variablesampletimesystem)且采样时间计算与输入u相关。timeoffsetvaluetimesamplentime_)__(其中n表示第n个采样点。Simulink在每一个采样点上调用mdlOutput和mdlUpdate例程。对于连续时间系统采样时间和偏移量的值应该设置为零。采样时间还可以继承自驱动模块、目标模块或者系统最小采样时间,这种情况下采样时间值应该设置为-1,或者INHERITED_SAMPLE_TIME。4.动态输入(Dynamicallysizedinputs)S-函数支持动态可变维数的输入。S-函数的输入变量u的维数决定于驱动S-函数模块的输入信号的维数。6.2S-函数的工作原理6.2.1状态方程在对动态系统建模时,总是能够采用广义的状态空间形式对无论是线性系统还是非线性系统进行描述。这个描述包含以下两个方程:状态方程:输出方程:状态方程描述了状态变量的一阶导数与状态变量、输入量之间的关系。n阶系统具有n个独立的状态变量,系统状态方程则是n个联立的一阶微分方程或者差分方程。对于一个系统,由于所选择的状态变量不同,会导出不同的状态方程,因此状态方程的形式不是唯一的。输出方程描述了输出与状态变量、输入量之间的关系。输出量根据任务的需要确定。一个典型的线性系统的状态方程可以用矩阵的形式描述为:状态方程:输出方程:其中A、B、C、D分别是状态矩阵、输入矩阵、输出矩阵、前馈矩阵。Simulink框图的大部分模块都具有一个输入向量u、一个输出向量y和一个状态向量x,如图6.3所示。图6.3Simulink模块u,x,y和时间t之间存在如下关系:输出方程:连续状态方程:离散状态方程:其中。S-函数同样是一个Simulink模块。它的以下几个例程函数清楚地体现了状态空间所描述的特性。(1) S-函数中的连续状态方程描述。状态向量的一阶导数是状态x、输入u和时间t的函数。在S-函数中,状态的一阶导数是在mdlDerivatives例程中计算的,并将结果返回供求解器积分。(2) S-函数中的离散状态方程描述。下一步状态的值依赖于当前的状态输入u和时间t。这是通过mdlUpdate例程完成的,并将结果返回供求解器在下一步时使用。(3) S-函数中的输出方程描述。输出值是状态、输入和时间的函数。6.2.2Simulink仿真的两个阶段理解S-函数首先要很好地了解Simulink的仿真过程。仿真包含两个主要阶段,第一个阶段是初始化,这时块的所有参数都已确定下来。初始化阶段完成了以下工作:(1)传递参数给MATLAB进行求值。(2)得到的数值作为实际的参数使用。(3)展开模型的层次,每个子系统被它们所包含的块替代。(4)检查信号的宽度和连接。(5)确定状态初值和采样时间。仿真运行阶段的工作可以概括为:(1)计算输出。(2)更新离散状态。(3)计算连续状态,连续状态的计算过程:①每个块按照预先确定的顺序计算输出。②每个块使用当前时间、块的输入和状态计算它的导数。③导数返回给求解器,通过积分得到下一步状态的值。(4)计算输出,过零可能被激活。图6.4求解器与系统的交互作用关系6.2.3S-函数仿真流程S-函数是Simulink的重要组成部分,它的仿真过程包含在Simulink仿真过程之中,所以上一节所述同样适用于S-函数。如图6.5所示,S-函数的仿真流程也包括初始化阶段和运行阶段两个阶段。图6.5中每个功能模块都对应于一个仿真例程或者回调函数。图6.5S-函数仿真流程初始化模型计算下一个采样时间点(仅适用于变采样时间块)计算输出更新离散状态计算导数检查过零事件计算输出计算导数仿真循环minortimestepmajortimestep仿真结束数值积分(1)初始化:在仿真开始前,Simulink在这个阶段初始化S-函数。①初始化结构体SimStruct,它包含了S-函数的所有信息。②设置输入输出端口数。③设置采样时间。④分配存储空间。(2)计算下一个采样时间点:只有在使用变步长求解器进行仿真时,才需要计算下一个采样时间点,即计算下一步的仿真步长。(3)计算输出:计算所有输出端口的输出值。(4)更新状态:此例程在每个步长处都要执行一次,可以在这个例程中添加每一个仿真步都需要更新的内容,例如离散状态的更新。(5)数值积分:用于连续状态的求解和非采样过零点。如果S-函数存在连续状态,Simulink就在minorsteptime内调用mdlDdrivatives和mdlOutput两个S-函数例程。6.3编写M文件S-函数6.3.1M文件S-函数的工作流程M文件S-函数和上节所介绍的S-函数仿真流程是一致的。它调用例程函数的顺序是通过标志Flag来控制的。图6.6给出了各仿真阶段的标志值、变量值及其对应仿真例程。图6.6M文件S-函数流程设置初始条件计算下一个采样时间(仅适用于变采样时间)计算输出更新离散状态计算输出计算导数结束仿真时需要进行的工作Flag=0mdlInitializeConditionsmdlInitializeSizesmdlInitializeSampleTimesFlag=4mdlGetTimeNextVarHitFlag=3mdlOutputsFlag=2mdlUpdateFlag=3mdlOutputsFlag=1mdlDerivativesFlag=9mdlTerminate6.3.2M文件S-函数模板Simulink为我们编写S-函数提供了各种模板文件,其中定义了S-函数完整的框架结构,用户可以根据自己的需要加以剪裁。编写M文件S-函数时,推荐使用S-函数模板文件sfuntmpl.m。这个文件包含了一个完整的M文件S-函数,它包含1个主函数和6个子函数。在主函数内程序根据标志变量Flag,由一个开关转移结构(Switch-Case)根据标志将执行流程转移到相应的子函数,即例程函数。Flag标志量作为主函数的参数由系统(Simulink引擎)调用时给出。了解这个模板文件的最好方式莫过于直接打开看看其代码。要打开模板文件,可在MATLAB命令行下输入:editsfuntmpl或者双击\S-functiondemos\M-fileS-functions\M-filetemplate块。下面是删除了其原有注释的代码,结构反而更为清晰紧凑。function[sys,x0,str,ts]=sfuntmpl(t,x,u,flag)%主函数%主函数包含四个输出:sys数组包含某个子函数返回