logo资料库

MATLAB中S函数.pdf

第1页 / 共24页
第2页 / 共24页
第3页 / 共24页
第4页 / 共24页
第5页 / 共24页
第6页 / 共24页
第7页 / 共24页
第8页 / 共24页
资料共24页,剩余部分请下载后查看
第十章 用 S-函数扩展 Simulink 通过第八、第九章的学习,用户对用 Simulink 建模的基本思想已经有了清晰的认识,Simulink 为用 户提供了许许多多的内置库模块,用户只需使用这些库模块构建系统即可。但在实际应用中,用户通常 会发现有些过程用 Simulink 的库模块不容易建模。这时,可以使用 S-函数来扩展 Simulink。S-函数 结合了 Simulink 框图图形化的特点和 MATLAB 编程灵活方便的优点,从而给用户提供增强和扩展 Simulink 的强大机制,同时它也是使 RTW(Real Time Workshop)实现实时仿真的关键。 10.1 S-函数概述 10.1.1 S-函数的基本概念 S-函数是 System function 系统函数的简称,是指采用非图形化(即计算机语言,而非 Simulink 系统模块)的方式描述的功能模块。在 MATLAB 中,用户除了可以使用 MATLAB 代码编写 S-函数以外,还 可以使用 C、C++、FORTRAN 或 Ada 语言编写 S-函数,只不过用这些语言编写程序时需要用编译器生成 动态连接库(DLL)文件,然后在 Simulink 中直接调用。 S-函数是由一种特殊的语法构成的,用来描述并实现动态系统的。它采用一种特殊的调用语法,使 函数和 Simulink 求解器进行交互。这种交互与求解器和 Simulink 仿真模型间的交互相类似:S-函数接 受来自 Simulink 求解器的相关信息,并对求解器发出的命令做出适当的响应。 S-函数作为与其它语言结合的接口,可以使用这个语言所提供的强大功能。例如,使用 MATLAB 语 言编写的 S-函数称为 M 文件 S-函数,它可以充分利用 MATLAB 所提供的丰富资源,方便地调用各种工 具箱函数和图形函数;而使用 C 语言编写的 S-函数被称为 C-MEX 文件 S-函数,则可以实现对操作系 统和外部设备等的访问,也可以提供与操作系统的接口。另外,S-函数可以使用其他多种语言编写,因 此可以实现代码的移植,即将已有的代码结合进来,而不需在 Simulink 中重新实现算法。 S-函数中采用非图形化的方式描述系统,其内部采用文本方式输入描述系统的公式、方程,这种方 式非常适合复杂动态系统的数学描述,且可以在仿真过程中对仿真进行精确的控制。 10.1.2 如何使用 S-函数 在动态系统仿真中,要想将 S-函数加入 Simulink 仿真模型中,用户需要将 User Defined Functions 模型库中的 S-Function 模块拖进该模型中。S-Function 模块是一个单输入单输出模块,如果有多个输 入与输出信号,用户需要使用 Mux 模块和 Demux 模块对信号进行组合或分离。S-Function 模块仅仅是以 图形的方式提供给用户一个 S-函数的使用接口,在它的参数设置对话框中仅包含 S-函数的名称及函数 所需的参数列表(见图 10.1),而 S-函数所实现的功能则由 S-函数源文件描述,S-函数源文件必须由 用户自行编写。 使用 S-函数的步骤如下: 一、在系统的 Simulink 仿真框图中添加 S-function 模块,并进行正确的设置; 二、创建 S-函数源文件。创建 S-函数源文件的方法有多种。用户可以按照 S-函数的语法格式自行编 写代码,但是这样做很麻烦,且容易出错。Simulink 在 S-function Examples 模型库中为用户提供了针 对不同语言的很多 S-函数模板和例子,用户可以根据自己的需要修改相应的模板或例子即可完成 S-函 数源文件的编写工作; 三、在系统的 Simulink 仿真框图中按照定义好的功能连接输入输出端口。 这里需要说明的是,S-function 模块中 S-函数名称必须和用户建立的 S-函数源文件的名称完全 相同,S-function 模块中的 S-函数参数列表必须按照 S-函数源文件中的参数顺序赋值,且参数之间 需要用逗号隔开。另外,用户也可以使用子系统封装技术对 S-函数进行封装,这样做的好处是可以增强 系统模型的可读性。 122
S-函数块 S-函数参数设置对话框 S-函数源文件 图 10.1 S-函数模块、参数设置对话框及其源文件的关系 为了方便用户编写 C MEX S 函数,Simulink 的 User Defined Functions 模型库中为用户编写 C MEX S 函数提供了一个图形化的集成的图形开发环境 S-function builder。用户只需在 S-function builder 中相应的位置写入相应的名称和代码即可编译成相应的 MEX 文件。 为了使读者尽快掌握 S-函数的使用步骤,先举一个简单的例子。 例 10.1 使用 S-函数实现系统: 2* = y u 。 解:一、在 Simulink 模型框图中添加 S-function 模块,打开 S-funfction 模块的参数设置对话框, 参数 S-function name 需设置为 timestwo。 二、创建 S-函数源文件。 1、打开 M 文件 S-函数模板文件 Sfuntmpl.m,并在指定目录下另存为 timestwo.m。打开 M 文件 S-函数 模板文件 Sfuntmpl.m 的方法由两种: 方法一,在 MATLAB 命令窗口键入 edit Sfuntmpl 即可; 方法二,在 Simulink 浏览器中寻找 S-function demos 模型库,其中包含各种语言编写的 S-函数 例子和模板。对于本例,用户只需点击 M-file S-functions 就可以看到 M 文件 S-函数模板文件 Sfuntmpl.m 和几个编程例子,双击模板文件 Sfuntmpl.m 即可。 2、修改模板。找到函数 mdlInitializeSizes,修改以下代码: size.NumOutputs=1; size.NumIutputs=1; 找到函数 mdOutputs,将代码 sys=[ ]改为 sys=2*u 至此,已经写好了该 S-函数的源文件,保存修改即可。 三、在 Simulink 模型框图中按要求添加并连接各个模块。系统 Simulink 仿真模型如图 10.2 所示。 四、运行仿真。仿真结果可以从 Scope 模块中观察。结果如图 10.2 所示。 10.1.3 与 S-函数相关的术语 用户对 S-函数相关的术语的理解对于了解 S-函数的工作原理、编写 S-函数源文件是非常有用的。 一、仿真例程(Routines) 123
图 10.2 例 10.1 系统 Simulink 仿真模型及其结果 Simulink 在仿真的不同阶段调用 S-函数的不同的功能函数以完成不同的任务。对于 M 文件 S-函数, Simulink 通过传递一个 flag 参量给 S-函数,通知 S-函数当前所处的仿真阶段,以便执行相应的功能 函数。S-函数的功能函数包括初始化、计算输出、更新离散状态、计算导数、结束仿真等。这些功能函 数称为仿真例程或回调函数(call back function)。在写 M 文件 S-函数时,用户只需用 MATLAB 语言为 每个 flag 对应的功能函数编写代码即可。表 10.1 列出了各仿真阶段的功能函数及其对应的 flag 值。 表 10.1 各仿真阶段的仿真例程及其 flag 值 仿真阶段 S-函数仿真例程(回调函数) Flag 值(M 文件 S-函数) 初始化 mdlInitializeSizes 计算下一个采样点 mdlGetTimeofNextVarHit 计算输出值 更新离散状态 计算导数 结束仿真 mdlOutputs mdlUpdate mdlDerivatives mdlTerminate 二、直接馈入(Direct Feedthrough) 0 4 3 2 1 9 直接馈入是指模块的输出或采样时间(变速率模型)直接由其某个输入端口控制。判断一个 S-函数 是否具有直接馈入的标准是: 某时刻系统的输出 y 包含该时刻系统的输入 u,即计算系统输出的方程中包含输入变量 u; 若系统是一个变采样时间系统,且下一个采样点的计算与输入 u 有关。 馈入标志的设置不仅关系到系统模型中的系统模块的执行顺序,而且关系到对代数环的检测和处理。 因此正确设置馈入标志是非常重要的。 三、采样时间和偏移量(Sample time & offsets) M 文件和 C MEX 文件 S-函数都允许用户十分方便地设定 S-函数被调用的时间。 采样时间在离散系统中控制采样点的间隔,偏移量则用于延迟采样点。一个采样点对应的时间值由 下列公式计算: 其中,n 表示当前仿真步,是整数。 TimeHit=n×period+offset 如 果 用 户 定 义 了 一 个 离 散 采 样 时 间 , Simulink 就 会 在 所 定 义 的 每 个 采 样 点 调 用 S - 函 数 的 mdlOutputs 和 mdlUpdate 例程。 对于连续时间系统,采样时间和偏移量的值均应设置为零。采样时间还可以继承来自驱动模块、目 124
标模块或系统最小的采样时间,这种情况下,采样 时间值设置为-1。 四、动态输入(Dynamically sized inputs) S-函数支持动态可变维数的输入。S-函数的 输入变量的维数取决于驱动 S-函数模块的输入信 号维数。仿真开始时,通过 size 或 length 函数确 定输入信号的维数。然后就可以利用这个维数来估 计连续状态数目、离散状态数目和输出向量的维数。 在 M 文件 S-函数中动态设置输入维数时,应该 把 sizes 数据结构的对应成员设置为-1。 比 如 在 例 10.1 中 , 如 果 将 函 数 mdlInitializeSizes 中 的 代 码 设 置 成 size.NumOutputs=-1; size.NumIutputs=-1; 则 当输入是两维信号,分别为幅值为 1 和 3 的正弦信 号,系统的输出也是两维信号,结果如图 10.3 所示。 图 10.3 例 10.1 进行动态输入设置后的仿真结果 10.2 S-函数工作原理 了解 S-函数的工作原理对于用户掌握 S-函数的编写方法是非常有用的,对用户对于 Simulink 的 仿真原理的理解也是很有帮助的。本节介绍 S-函数的工作原理。 在具体介绍 S-函数的工作原理之前,首先需要回顾一下 Simulink 模块的工作原理。 Simulink 中的每个模块都有三个基本元素:输入向量、状 态向量和输出向量,分别表示为u , x 和 y 。图 10.4 反映了它 们之间的关系。在 Simulink 模块的三个元素中,状态向量是最 重要的,也是最灵活的概念。在 Simulink 中状态向量可以分为 连续状态、离散状态或两者的结合。输入、输出及状态的关系 可以用状态方程描述: 图 10.4 Simulink 模块的基本模型 输出方程: y = f o t ( , x u , ) 连续状态方程: d =x f d t ( , x u , ) 离散状态方程: 1 k x + = uf t ( , x,u ) 其中 [ =x d x ]1k x 。 + Simulink 在仿真时将上述方程对应不同的仿真阶段,它们分别是计算模块的输出、更新离散状态、 计算连续状态的微分。在仿真开始和结束,还包括初始化和结束仿真两个阶段。在每个阶段,Simulink 都反复地调用模块。 至此,读者已经接触到了几个关于仿真的概念:仿真步长(Simulation step)、仿真阶段(Simulation stage)。为了深入了解 S-函数的工作原理,还需了解一个概念:仿真循环(Simulation loop)。一个仿 真循环就是由仿真阶段按一定顺序组成的执行序列。对于每个模块,经过一次仿真循环就是一个仿真步 长,而在同一个仿真步长中,模型中各模块的仿真按照事先排好的顺序依次执行。这个过程可以用图 10.5 表示。 从图中可以看出,在仿真开始时,Simulink 首先对模型进行初始化,此阶段不属于仿真循环。在所 有模块都初始化后,模块进入仿真循环,在仿真循环的每个阶段,Simulink 都要调用模块或者 S-函数。 125
由于在积分时,对仿真步长有要求,所以此时需要 将仿真步长细化。完成一个仿真循环就进入下一个 仿真步长,如此循环直至仿真结束。 在调用模型中的 S-函数时,Simulink 会调用 用户定义的 S-函数的例程来实现每个仿真阶段要 完成的任务。这些任务包括: 一、初始化:仿真开始前,Simulink 在这个阶段初 始化 S-函数,完成的主要工作包括: 1 、 初 始 化 包 含 S - 函 数 所 有 信 息 的 结 构 体 SimStruct; 2、确定输入输出端口的数目和大小; 3、确定模块的采样时间; 4、分配内存和 Sizes 数组。 二、计算下一个采样时刻。如果模型使用变步长求 解器,那么就需要在当前仿真步长内确定下一个采 样点的时间,也即下一个仿真步长的大小; 三、计算输出:计算所有输出端口的输出值。 四、更新离散状态:此例程在每个仿真步长处都要 执行一次,为当前时间的仿真循环更新离散状态; 五、数值积分:这个阶段只有模块具有连续状态和 非采样过零点时才会存在。如果 S-函数存在连续 状态,Simulink 就在细化的小时间步长中调用 S- 函数的输出(mdlOutputs)和微分(mdlDerivatives) 例程。如果存在非采样过零点,Simulink 将调用 S - 函 数 中 的 输 出 ( mdlOutputs ) 和 过 零 检 测 (mdlZeroCrossngs)例程,以定位过零点。 初始化模型 计算下一个采样时间点 (仅适用变采样速率模块) 环 循 真 仿 计算输出 更新离散状态 数值积分 计算导数 计算输出 计算导数 检测过零事件 p e t s e m i t r o n i M p e t s e m i t r o j a M 仿真结束 图 10.5 S-函数仿真流程 10.3 编写 M 文件 S-函数 由前面的介绍可以知道,S-函数是由一系列仿真例程组成的。这些仿真例程就是 S-函数特有的语 法结构,用户编写 S-函数的任务就是在相应的例程中填写适当的代码,供 Simulink 及 MATLAB 求解器调 用。M 文件 S-函数结构明晰,易于理解、书写方便,可以调用丰富 MATLAB 函数,所以在实际工作中得 到了广泛的应用。 M 文件 S-函数利用 Flag 标志控制调用例程函数的顺序。各仿真阶段的仿真例程及对应的标志值如 表 10.1 所示。 M 文件 S-函数的仿真流程同图 10.5 介绍的 S-函数的仿真流程。在初始化阶段,通过标志 0 调用 S -函数,并请求提供输入输出个数、初始状态和采样时间等信息。然后,仿真开始。下一个标志为 4,请 求 S-函数提供下一步的采样时间(这个例程在单采样速率系统下不被调用)。接着 flag=3 计算模块的 输出,flag=2 更新离散状态,当需要计算连续状态导数时 flag=1。然后求解器使用积分例程计算状态 的值。计算状态导数和更新离散状态之后通过标志 3 计算模块的输出。这样就完成了一个仿真步长的工 作。当到达结束时间时,采用标志 9 完成结束前的处理工作。 10.3.1 M 文件 S-函数模板 126
Simulink 为用户提供了各种语言编写 S-函数的模板文件。这些 S-函数的模板文件中定义了 S-函 数的框架结构,用户可以根据自己的需要修改。 编写 M 文件 S-函数时,需要使用 M 文件 S-函数模板文件 sfuntmpl.m 文件。该文件包含了所有的 S -函数的例程,及包含 1 个主函数和 6 个子函数。在主函数中,程序使用一个多分支语句(Switch-case) 根据标志将执行流程转移到相应的例程函数。主函数的参数 Flag 标志值是由系统(Simulink 引擎)调用 时给出的。读者可以打开并阅读该 M 文件 S-函数模板文件。 一、打开模板文件的方法由两种,用户可以在 MATLAB 命令窗口中键入: >> edit sfuntmpl 或者双击 User-defined Function \S-function Examples\M-file S-functions\Leveal-1 M-file S-functions1\ Leveal-1 M-file template 模块。 二、M 文件 S-函数模板文件代码 M 文件 S-函数模板文件的代码如下: %主函数 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 数据结构所包含的信息 127
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 = []; 三、M 文件 S-函数模板文件的几点说明 主函数包含四个输出参数:sys 数组返回某个子函数,它的含义随着调用子函数的不同而不同;x0 为所有状态的初始化向量;str 是保留参数,总是一个空矩阵;Ts 返回系统采样时间。 主函数的四个输入参数分别是采样时间 t,状态 x,输入 u 和仿真流程控制标志变量 flag。 输入参数后面还可以附加一系列用户仿真需要的参数。 编写用户自己的 S-函数时,应将函数名改为 sfuntmpl 改为 S-function 模块中设置的函数名。 读者可能已经发现一个令人困惑的问题:不论在哪个仿真阶段,例程子函数的返回变量都是 sys。要 128
搞清楚这个问题,还要回到 Simulink 如何调用 S-函数上来。前面讲过,Simulink 在每个仿真步长的仿 真循环中的每个仿真阶段都要调用 S-函数。在调用时,Simulink 不但根据所处的仿真阶段为 flag 传入 不同的值,还会为返回变量 sys 指定不同的角色。即是说尽管是相同的 sys 变量,但在不同的仿真阶段 其意义是不相同的,这种变化由 Simulink 自动完成。 10.3.2 M 文件 S-函数的应用举例 了解了 M 文件 S-函数模板文件的代码、代码中各个部分完成的功能及各参数的含义后,用户可以着 手利用 S-函数进行系统仿真了。下面我们使用 M 文件 S-函数实现几种不同的系统。 一、含用户参数的简单系统 M 文件 S 函数除了模板文件中要求的几个必需的参数,还可以加入用户自定义的参数,自定义参数需 要在 S-函数的输入参数中列出。在含用户自定义参数的 S 函数中,主函数要做适当的修改以便将自定义 参数传递到子函数中,子函数也需要相应的修改以便接受自定义参数。在编写 S-函数时,应能区分哪些 参数会影响哪一个子函数的执行,要针对这些参数做相应的修改。还需注意的一点是,S-function 模块 中的参数设置对话框中的参数输入顺序应与 S-函数中自定义参数的顺序相同。 例 10.2 用 S-函数实现 gain 模块:增益值作为 S-函数用户自定义参数输入。 解:(1)编写 S-函数的源文件 修改 M 文件 S-函数的主函数:增加自定义参数,采用新的函数名: function [sys,x0,str,ts] = sfun_var_gain(t,x,u,flag,gain) 由于增益参数只是用来计算输出值的,因而对初始化例程和计算输出例程子函数做修改,其他例程 均不需调用,不用做修改 case 0, [sys,x0,str,ts]=mdlInitializeSizes(gain); case 3, sys=mdlOutputs(t,x,u,gain); 修改初始化例程子函数: function [sys,x0,str,ts]=mdlInitializeSizes(gain) sizes.NumContStates = 0; sizes.NumDiscStates = 0; sizes.NumOutputs = 1; sizes.NumInputs = 1; sizes.DirFeedthrough = 1; 定义计算输出例程子函数 function sys=mdlOutputs(t,x,u,gain) sys = gain*u; % 输出=增益×输入 (2)建立如图 10.6 所示的系统仿真模型,将自定义参数设置为 3,运行仿真,仿真结果如图 10.6 所示, 验证了 S-函数的正确性。 129
分享到:
收藏