S函数
S函数就是系统函数的意思。在控制理论研究中,经常需要用复杂的算法设计控制器,而这些算法经常因其复杂度又不适合用普通Simulink模块来搭建,则需要用编程的形式设计出S函数模块来搭建系统。复杂系统用Simulink模块搭建较为困难,而使用编程的形式设计出S函数模块搭建则较为简单。正确使用S函数可以搭建任意复杂的系统。
S函数有固定的程序格式,用MATLAB语言可以编写S函数,此外还允许采用C语言、C++等。
S函数有固定的程序格式,用M语言编写的S函数的引导语句为
function [sys, x0, str, ts]=fun(t, x, u, flag, p1, p2, ...)
前4个输入参数t, x, u, flag是必须的,要添加输入参数只需在参数flag后面添加即可。例如添加Kp,Ki参数,添加后需在S函数模块设置参数值。
输入参数t,x,u分别为时间、状态量和输入信号(注意:状态个数最少为2否则容易报错);flag为标志位,标志位的取值不同,S函数执行的任务与返回数据也是不同的。S函数内部还包括6个子函数,根据S函数的任务有些函数不会被执行。
flag=0时执行mdlInitializeSizes函数进行初始化。
flag=1时执行mdlDerivatives函数作连续状态变量的更新。
flag=2时执行mdlUpdate函数作离散状态变量的更新。
flag=3时执行mdlOutputs函数求取系统的输出信号。
flag=4时执行mdlGetTimeOfNextVarHit函数计算下一步的仿真时刻,并作为输出参数返回。
flag=9时执行mdlTerminate函数终止仿真过程。
初始化函数如下
function [sys,x0,str,ts]=mdlInitializeSizes(A,B,C,D)
sizes = simsizes;
sizes.NumContStates = 2;%连续状态数
sizes.NumDiscStates = 0;%离散状态数
sizes.NumOutputs = 1;%输出信号数
sizes.NumInputs = 1;%输入信号数
sizes.DirFeedthrough = 0;%输入信号是否直接在输出端出现,=0不出现
sizes.NumSampleTimes = 1;%采样周期个数
sys = simsizes(sizes);
x0 = zeros(2,1);%初始状态
str = [];
ts = [0 0];%采样周期,
变量ts应该为双列的矩阵,每一行对应一个采样周期,S函数支持多个采样周期,对单个采样周期的系统来说,ts=[t1,t2],其中t1为采样周期,如果t1=-1则将继承输入信号的采样周期,t2为偏移量,一般取为0。
在实际仿真过程中,Simulink会自动将flag设置成0进行初始化过程,然后将flag的值设置成为3,计算该模块的输出。一个仿真周期结束后,Simulink先将flag的值设置为1和2,更新系统的连续和离散状态,再将其设置为3,计算模块的输出值,如此一个周期接一个周期的计算,直至仿真结束条件满足,Simulink将把flag的值设置成9,终止仿真过程。
编写S函数的过程一般为:确定系统是连续状态还是离散状态,有方程的改写成状态方程和输出方程,根据状态方程和输出确定状态数、输入信号数和输出信号数。首先是设置初始化子函数,然后给其他子函数添加内容。
S函数模板
S函数有几种模板,能写出矩阵形式的状态方程用csfunc.m或dsfunc.m模板,其中csfunc.m为连续系统的模板,dsfunc.m为离散系统的模板;不能写出矩阵形式的状态方程用sfuntmpl.m模板。命令栏输入open csfunc.m 或open dsfunc.m或open sfuntmpl.m打开S函数模板。
例如
function [sys,x0,str,ts] = csfunc(t,x,u,flag)
function [sys,x0,str,ts,simStateCompliance] = sfuntmpl(t,x,u,flag)
注意函数输入参数为4个,需要添加输入参数时在原参数后面添加就行。
注意
状态数为0的模块也可以编写S函数,例如编写阶跃信号模块的S函数。
列写连续系统状态方程后,将状态方程改写为程序添加在mdlDerivatives函数中,在mdlDerivatives函数中作连续状态变量的更新。因为状态方程的计算结果为状态变量的导数,所以mdlDerivatives函数输出参数为状态变量的导数,有个疑问那状态变量似乎没有更新,其实对于连续系统Simulink会根据该时刻的状态和状态导数自动计算出下一时刻的状态,从而自动更新状态变量。对于离散系统,mdlUpdate函数输出参数为下一时刻的状态变量,mdlUpdate函数作离散状态变量的更新。