s-function的介绍

定义:S-函数是 System function 系统函数的简称,是指采用非图形化(即计算机语言,而非Simulink系统模块)的方式描述的功能模块。在 MATLAB 中,用户除了可以使用MATLAB 代码编写 S-函数以外,还可以使用 C、C++、FORTRAN 或 Ada 语言编写 S-函数,只不过用这些语言编写程序时需要用编译器生成动态连接库(DLL)文件,然后在Simulink 中直接调用。

使用s-函数的一般步骤:
一、在系统的 Simulink 仿真框图中添加 S-function 模块,并进行正确的设置;
二、创建 S-函数源文件。创建 S-函数源文件的方法有多种。用户可以按照 S-函数的语法格式自行编写代码,但是这样做很麻烦,且容易出错。Simulink 在 S-function Examples 模型库中为用户提供了针对不同语言的很多 S-函数模板和例子,用户可以根据自己的需要修改相应的模板或例子即可完成 S-函数源文件的编写工作;
三、在系统的 Simulink 仿真框图中按照定义好的功能连接输入输出端口。
这里需要说明的是,S-function 模块中 S-函数名称必须和用户建立的 S-函数源文件的名称完全相同,S-function 模块中的 S-函数参数列表必须按照 S-函数源文件中的参数顺序赋值,且参数之间需要用逗号隔开。另外,用户也可以使用子系统封装技术对 S-函数进行封装,这样做的好处是可以增强系统模型的可读性。

常用的s-function主要有M文件和C MEX文件两种。M 文件 S-函数由于具有易于编写和理解的特点,在仿真计算中得到了广泛的应用。但是它有一些缺点:首先,M 文件 S-函数使得每个仿真步都必须激活 MATLAB 解释器,以致仿真速度变慢;其次,当需要利用 RTW 从 Simulink 框图生成实时代码时,框图中不能含有 M 文件 S-函数。而 C MEX S-函数不仅运算速度快,而且可以用来生成独立的仿真程序。现已的 C 语言编写的程序还可以方便地通过包装程序结合至 C MEX S-函数中。C MEX S-函数结合了 C 语言的优势,可以实现对操作系统和硬件的访问,实现与串口或网络的通信,编写设备驱动程序等。

一:M文件编写的s函数
附上mathworks公司的模板(在matlab的命令行窗口输入edit Sfuntmpl.m)

function [sys,x0,str,ts,simStateCompliance] = sfuntmpl(t,x,u,flag)
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);
    
  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     = 0;
sizes.NumInputs      = 0;
sizes.DirFeedthrough = 1;
sizes.NumSampleTimes = 1;   % at least one sample time is needed
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)
sys = [];

function sys=mdlGetTimeOfNextVarHit(t,x,u)
sampleTime = 1;    %  Example, set the next hit to be one second later.
sys = t + sampleTime;

function sys=mdlTerminate(t,x,u)
sys = [];

对上述模板进行解析:
function [sys,x0,str,ts,simStateCompliance] = sfuntmpl(t,x,u,flag)
先讲输入与输出变量的含义:t是采样时间,x是状态变量,u是输入(是做成simulink模块的输入),flag是仿真过程中的状态标志(以它来判断当前是初始化还是运行等);sys输出根据flag的不同而不同(下面将结合flag来讲sys的含义),x0是状态变量的初始值(可根据需要对其进行赋值),str是保留参数(一般都是在初始化中将它置空),ts是一个1×2的向量,ts(1)表示采样周期,ts(2)表示偏移量。
ps:如果是人为地自定义一些参量作为输入,则需要将模块改为:
function [sys,x0,str,ts,simStateCompliance] = sfuntmpl(t,x,u,flag,a,b,c) %a,b,c即为定义的输入变量

之后是switch,case模块:
根据flag的值判断执行哪一步。
case 0,
[sys,x0,str,ts,simStateCompliance]=mdlInitializeSizes; %该模块为初始化模块。在代码中,找到相应的执行模块:
function [sys,x0,str,ts,simStateCompliance]=mdlInitializeSizes
sizes = simsizes; %用于设置模块参数的结构体用simsizes来生成
sizes.NumContStates = 0; %模块连续状态变量的个数
sizes.NumDiscStates = 0; %模块离散状态变量的个数
sizes.NumOutputs = 0; %模块输出变量的个数
sizes.NumInputs = 0; %模块输入变量的个数
sizes.DirFeedthrough = 1; ;%模块是否存在直接贯通(直接贯通我的理解是输入能 直接控制输出)
sizes.NumSampleTimes = 1; % 模块的采样时间个数,至少是一个
sys = simsizes(sizes); %设置完后赋给sys输出
x0 = []; %根据需要对状态变量赋初值
str = []; %一般都设置为空
ts = [0 0]; %若系统为连续系统,则第一空为0;若第一空不为0,则该值为采样时间。 若第二空为0,表示整个系统无偏移,即为从初始时刻0开始动作。
simStateCompliance = ‘UnknownSimState’;

case 1,
sys=mdlDerivatives(t,x,u); %该模块为连续状态的微分模块。
在代码中,找到相应的执行模块:
function sys=mdlDerivatives(t,x,u)
sys = [];
此时要计算连续状态的微分。如果设置的连续状态变量个数为0,此处置空就可以了。

case 2,
sys=mdlUpdate(t,x,u); %该模块为离散转态的计算模块
在代码中,找到相应的执行模块:
function sys=mdlUpdate(t,x,u)
sys = [];

case 3,
sys=mdlOutputs(t,x,u); %该模块为计算输出的模块
在代码中,找到相应的执行模块:
function sys=mdlOutputs(t,x,u)
sys = [];

case 4,
sys=mdlGetTimeOfNextVarHit(t,x,u); %表示要计算下一次采样的时间。只有在离散采样系统中才有用
在代码中,找到相应的执行模块:
function sys=mdlGetTimeOfNextVarHit(t,x,u)
sampleTime = 1; % Example, set the next hit to be one second later.
sys = t + sampleTime; %对于连续系统,只需将sys置空就可以

case 9,
sys=mdlTerminate(t,x,u); %该模块为结束模块
在代码中,找到相应的执行模块:
function sys=mdlTerminate(t,x,u)
sys = []; %一般直接置空就可以了

进一步的学习内容可以参考
https://blog.csdn.net/weixin_42736130/article/details/89084225
https://blog.csdn.net/weixin_42736130/article/details/89178868
https://blog.csdn.net/dm12mail/article/details/66974956

二:C语言编写的s函数
C语言编写的s函数,需要先编译成可以在MATLAB内运行的二进制代码,然后才可以使用。
编译方法为:mex .c文件
C语言模板的打开方式:在matlab的命令行窗口中输入edit sfuntmpl_basic.c
同样附上mathworks公司的模板

#define S_FUNCTION_NAME  sfuntmpl_basic
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"

static void mdlInitializeSizes(SimStruct *S)
{
    ssSetNumSFcnParams(S, 0);  /* Number of expected parameters */
    if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
        /* Return if number of expected != number of actual parameters */
        return;
    }

    ssSetNumContStates(S, 0);
    ssSetNumDiscStates(S, 0);

    if (!ssSetNumInputPorts(S, 1)) return;
    ssSetInputPortWidth(S, 0, 1);
    ssSetInputPortRequiredContiguous(S, 0, true); /*direct input signal access*/
  
    ssSetInputPortDirectFeedThrough(S, 0, 1);

    if (!ssSetNumOutputPorts(S, 1)) return;
    ssSetOutputPortWidth(S, 0, 1);

    ssSetNumSampleTimes(S, 1);
    ssSetNumRWork(S, 0);
    ssSetNumIWork(S, 0);
    ssSetNumPWork(S, 0);
    ssSetNumModes(S, 0);
    ssSetNumNonsampledZCs(S, 0);

    ssSetSimStateCompliance(S, USE_DEFAULT_SIM_STATE);

    ssSetOptions(S, 0);
}

static void mdlInitializeSampleTimes(SimStruct *S)
{
    ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
    ssSetOffsetTime(S, 0, 0.0);

}

#define MDL_INITIALIZE_CONDITIONS   /* Change to #undef to remove function */
#if defined(MDL_INITIALIZE_CONDITIONS)

  static void mdlInitializeConditions(SimStruct *S)
  {
  }
#endif /* MDL_INITIALIZE_CONDITIONS */

#define MDL_START  /* Change to #undef to remove function */
#if defined(MDL_START) 

  static void mdlStart(SimStruct *S)
  {
  }
#endif /*  MDL_START */

static void mdlOutputs(SimStruct *S, int_T tid)
{
    const real_T *u = (const real_T*) ssGetInputPortSignal(S,0);
    real_T       *y = ssGetOutputPortSignal(S,0);
    y[0] = u[0];
}

#define MDL_UPDATE  /* Change to #undef to remove function */
#if defined(MDL_UPDATE)

  static void mdlUpdate(SimStruct *S, int_T tid)
  {
  }
#endif /* MDL_UPDATE */

#define MDL_DERIVATIVES  /* Change to #undef to remove function */
#if defined(MDL_DERIVATIVES)

  static void mdlDerivatives(SimStruct *S)
  {
  }
#endif /* MDL_DERIVATIVES */

static void mdlTerminate(SimStruct *S)
{
}

#ifdef  MATLAB_MEX_FILE    /* Is this file being compiled as a MEX-file? */
#include "simulink.c"      /* MEX-file interface mechanism */
#else
#include "cg_sfun.h"       /* Code generation registration function */
#endif

对上述模板进行分析。为了更好地解释清楚,此处举一个例子:
在这里插入图片描述
s-function的介绍_第1张图片
s-function的介绍_第2张图片
static void mdlInitializeSizes(SimStruct S)
{
ssSetNumSFcnParams(S, 0); /
不含用户参数,设置为零 /
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /
若参数不匹配,Simulink 将发出警告 /
}
ssSetNumContStates(S, 2); /
系统有两个连续状态*/
ssSetNumDiscStates(S, 0); /* 系统无离散状态*/
if (!ssSetNumInputPorts(S, 1)) return; /* 如果输入端口数不为 1,则返回*/
/* S-functions 块只有一个输入端口,当需要多个输入时,必须使用 mux 模块把需要输入的信号合
成一个向量*/
ssSetInputPortWidth(S, 0, 2); /* 输入信号宽度为 2 /
ssSetInputPortDirectFeedThrough(S, 0, 1); /
设置馈通标志为 1 /
if (!ssSetNumOutputPorts(S, 1)) return; /
如果输出端口数不为 1,则返回*/
ssSetOutputPortWidth(S, 0, 2); /* 输出信号宽度为 2 /
ssSetNumSampleTimes(S, 1); /
1 个采样时间 /
ssSetNumRWork(S, 0); /
不使用工作向量 */
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);
ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
}

static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
/*连续系统的采样时间设置为 0,等同于 ssSetSampleTime(S, 0, 0) */
ssSetOffsetTime(S, 0, 0.0);
}

#define MDL_INITIALIZE_CONDITIONS
static void mdlInitializeConditions(SimStruct S)
{
real_T x0 = ssGetContStates(S); / 获得指向连续状态的指针
/
int_T lp;
for (lp=0;lp<2;lp++) {
x0++=0.0; / 各状态初值设置为 0 */
}
}

static void mdlOutputs(SimStruct *S, int_T tid)
{ /*获得指向输出向量、连续状态向量和输入端口的指针 */
real_T *y = ssGetOutputPortRealSignal(S,0);
real_T x = ssGetContStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
UNUSED_ARG(tid); /
not used in single tasking mode /
/
y=Cx+Du 输出方程 */
y[0]=C[0][0]*x[0]+C[0][1]*x[1]+D[0][0]*U(0)+D[0][1]*U(1);
y[1]=C[1][0]*x[0]+C[1][1]*x[1]+D[1][0]*U(0)+D[1][1]*U(1);
}

#define MDL_DERIVATIVES
static void mdlDerivatives(SimStruct *S)
{
real_T dx = ssGetdX(S); / 获得指向状态导数向量的指针 */
real_T x = ssGetContStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
/
xdot=Ax+Bu 状态方程 */
dx[0]=A[0][0]*x[0]+A[0][1]*x[1]+B[0][0]*U(0)+B[0][1]*U(1);
dx[1]=A[1][0]*x[0]+A[1][1]*x[1]+B[1][0]*U(0)+B[1][1]*U(1);
}
static void mdlTerminate(SimStruct S)
{
UNUSED_ARG(S); /
unused input argument */
}

#ifdef MATLAB_MEX_FILE /* 是否编译成 MEX 文件 ? /
#include “simulink.c” /
包含 MEX 文件的接口机制 /
#else
#include “cg_sfun.h” /
代码生成注册函数 */
#endif

似乎很难吧。However,在这个社会中好像万事万物都有后门可走:s-function builder
用户只需在 S-function Builder 界面中的相应位置写入所需的信息和代码即可。S-function Builder会自动生成 C MEX S-函数源文件。用户只要单击 Build 按钮,S-function Builder 就会自动编译,自动生成用户所需的 MEX 文件。
s-function的介绍_第3张图片
第一步:先在名称栏中定义s函数的名称,之后将上图中的离散状态量个数,离散状态初值,连续状态变量个数,连续状态初值等信息按需要填上;
s-function的介绍_第4张图片
第二步:设置数据属性选项卡中的设置输入、输出的数据类型、信号维数等信息,设置用户参数的名称、数据类型等。
s-function的介绍_第5张图片
第三步:在这调用所需的库文件
第四步:在 Outputs、Continues Derivatives 和 Discrete Update 选项卡中分别填入输出方程、连续状态方程、离散状态方程以及其它用户定制的代码。
第五步:单击build键 is ok.

你可能感兴趣的:(matlab)