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.youkuaiyun.com/weixin_42736130/article/details/89084225
https://blog.youkuaiyun.com/weixin_42736130/article/details/89178868
https://blog.youkuaiyun.com/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

对上述模板进行分析。为了更好地解释清楚,此处举一个例子:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
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函数的名称,之后将上图中的离散状态量个数,离散状态初值,连续状态变量个数,连续状态初值等信息按需要填上;
在这里插入图片描述
第二步:设置数据属性选项卡中的设置输入、输出的数据类型、信号维数等信息,设置用户参数的名称、数据类型等。
在这里插入图片描述
第三步:在这调用所需的库文件
第四步:在 Outputs、Continues Derivatives 和 Discrete Update 选项卡中分别填入输出方程、连续状态方程、离散状态方程以及其它用户定制的代码。
第五步:单击build键 is ok.

<think>好的,我现在需要回答用户关于MATLAB和Simulink中的Function及S-Function介绍。首先,我需要明确用户的需求,他们可能想了解这两者的基本概念、区别、用途以及如何使用。用户之前询问过Simulink中Mask的Radio Button的使用,现在转向更底层的函数模块,可能是想深入理解建模和自定义模块的方法。 首先,我应该从Function开始,因为在MATLAB和Simulink中Function有不同的表现形式。在MATLAB中,Function是基本的编程单元,用于封装代码。而在Simulink中,有Function-Call子系统、MATLAB Function块等。需要分别解释清楚,避免混淆。 然后是S-Function,这是Simulink中用于自定义模块的重要工具。S-Function允许用户用MATLAB、C、C++等语言编写模块的行为,适合实现复杂或特殊的逻辑。我需要说明S-Function的作用、结构、编写步骤以及应用场景。 接下来要考虑用户可能的疑问点。例如,Function和S-Function的区别是什么?何时使用哪种?是否需要编程经验?是否需要编译?这些都是用户可能关心的问题。 还需要结构清晰,分点介绍,保持逻辑连贯。可能需要分为几个大块,比如MATLAB中的Function、Simulink中的Function、S-Function的概述、如何创建、优缺点比较等。同时,注意使用示例和实际应用场景,帮助用户更好地理解。 要确保术语准确,比如区分MATLAB Function块和S-Function块,虽然它们都是自定义函数,但实现方式不同。MATLAB Function块使用MATLAB代码,而S-Function可以用多种语言,但可能需要编译。 另外,用户之前的问题涉及到Mask的使用,可能他们正在学习如何自定义模块,所以S-Function作为更高级的自定义方法,需要详细说明其与普通Function的不同之处,比如更底层、更灵活,但复杂度更高。 可能还需要检查是否有遗漏的重要信息,比如S-Function的回调方法,或者如何在Simulink中配置S-Function的参数。同时,注意是否需要提供代码示例,比如一个简单的S-Function模板,或者MATLAB Function块的使用步骤。 最后,总结两者的适用场景,帮助用户根据需求选择合适的工具。比如,简单的逻辑用MATLAB Function块,复杂的、需要高性能的用S-Function。 在写作过程中,要避免技术术语过多,用中文通俗易懂地解释,同时保持专业性。需要确保步骤准确,比如创建S-Function的具体流程,参数设置的位置等。可能还需要提醒用户常见的错误,比如S-Function编译问题,或者如何调试。 总之,我需要组织一个结构清晰、内容全面、易于理解的回答,覆盖MATLAB Function和S-Function的关键点,帮助用户建立正确的认识,并指导他们如何选择和使用这些工具。</think>以下是关于 **MATLAB/Simulink 中的 Function 及 S-Function** 的详细介绍,涵盖基本概念、实现方式及使用场景: --- ### **一、MATLAB 中的 Function** #### 1. **基本概念** - **Function(函数)**:MATLAB 中封装可重用代码的基本单元,通过输入参数接收数据,经处理后返回输出结果。 - **特点**: - 独立作用域:函数内变量与外部隔离(除非使用全局变量)。 - 支持多输入/输出。 - 文件名为 `<函数名>.m`,例如 `myfunc.m`。 #### 2. **定义与调用** ```matlab % 定义函数 function [out1, out2] = myfunc(in1, in2) % 计算逻辑 out1 = in1 + in2; out2 = in1 * in2; end % 调用函数 [a, b] = myfunc(2, 3); % 输出 a=5, b=6 ``` #### 3. **应用场景** - 算法封装(如信号处理、数学运算)。 - 脚本代码模块化,提升可读性和复用性。 --- ### **二、Simulink 中的 Function** #### 1. **Function-Call 子系统** - **功能**:通过外部触发信号(如函数调用生成器)执行子系统内的逻辑。 - **用途**:事件驱动模型(如状态机、周期性任务)。 #### 2. **MATLAB Function 块** - **功能**:在 Simulink 模型中直接嵌入 MATLAB 代码。 - **特点**: - 支持向量化运算,适合快速原型开发。 - 与 Simulink 信号交互,需定义输入/输出端口。 - **示例**: ```matlab function y = myblock(u1, u2) y = u1^2 + u2; end ``` #### 3. **Simulink Function 块** - **功能**:跨模型全局调用的函数(类似全局函数),可在多个位置调用同一逻辑。 - **配置**:通过 `Simulink Function` 块定义函数名和参数。 --- ### **三、S-Function(系统函数)** #### 1. **基本概念** - **S-Function**:用 MATLAB、C/C++ 等语言编写的自定义模块,直接与 Simulink 求解器交互,实现底层逻辑。 - **核心机制**: - **回调方法**:Simulink 在仿真不同阶段(初始化、计算输出、更新状态等)自动调用指定函数。 - **直接控制模块行为**:可定义微分方程、离散状态、触发条件等。 #### 2. **S-Function 结构** - **必需回调方法**: - `mdlInitializeSizes`:定义输入/输出端口数量及维度。 - `mdlOutputs`:计算模块输出信号。 - `mdlUpdate`(可选):更新离散状态。 - `mdlDerivatives`(可选):定义连续状态导数。 #### 3. **创建 S-Function 的步骤** - **MATLAB S-Function**: 1. 编写模板文件(如 `mysfunc.m`)。 2. 使用 `sfundemos` 查看示例。 ```matlab function [sys,x0,str,ts] = mysfunc(t,x,u,flag) switch flag case 0 % 初始化 [sys,x0,str,ts] = mdlInitializeSizes; case 3 % 计算输出 sys = mdlOutputs(t,x,u); otherwise sys = []; end end ``` - **C MEX S-Function**: 1. 编写 `.c` 文件(需符合 Simulink 接口规范)。 2. 使用 `mex` 命令编译为 `.mex` 文件。 #### 4. **S-Function 的应用场景** - 实现 Simulink 内置模块不支持的功能(如自定义非线性系统)。 - 集成外部硬件驱动或已有 C/C++ 代码。 - 需要高性能计算的复杂算法(C MEX 版本效率更高)。 --- ### **四、Function 与 S-Function 对比** | **特性** | **MATLAB Function 块** | **S-Function** | |-----------------------|------------------------------|------------------------------| | **语言支持** | MATLAB | MATLAB, C/C++, Fortran | | **执行效率** | 较低(解释执行) | 高(C MEX 编译为二进制) | | **灵活性** | 中(受限于 MATLAB 语法) | 高(可自定义底层行为) | | **调试难度** | 易(直接查看变量) | 难(需熟悉回调机制) | | **适用场景** | 快速原型、简单逻辑 | 高性能需求、复杂系统建模 | --- ### **五、选择建议** 1. **优先使用内置模块**:如 Gain、Sum 等,避免重复开发。 2. **简单逻辑**:选择 MATLAB Function 块或 Simulink Function。 3. **高性能/复杂逻辑**:使用 C MEX S-Function。 4. **集成已有代码**:通过 S-Function 包装 C/C++ 函数。 --- ### **六、示例:S-Function 实现PID控制器** 1. **编写 MATLAB S-Function**: ```matlab function [sys,x0,str,ts] = pid_sfunc(t,x,u,flag,Kp,Ki,Kd) switch flag case 0 % 初始化 sizes = simsizes; sizes.NumContStates = 0; sizes.NumDiscStates = 3; % 存储积分和微分误差 sizes.NumOutputs = 1; sizes.NumInputs = 2; % 输入:设定值、实际值 sys = simsizes(sizes); x0 = [0; 0; 0]; % 初始状态 str = []; ts = [0.1 0]; % 采样时间 case 3 % 计算输出 error = u(1) - u(2); integral = x(2) + error * t; derivative = (error - x(3)) / t; output = Kp*error + Ki*integral + Kd*derivative; sys = output; case 2 % 更新离散状态 sys = [t; x(2) + (u(1)-u(2))*t; u(1)-u(2)]; end end ``` 2. **在 Simulink 中调用**:拖入 S-Function 块,设置参数 `Kp, Ki, Kd`。 --- ### **七、常见问题** 1. **S-Function 编译失败** - **原因**:C/C++ 编译器未安装或路径错误。 - **解决**:运行 `mex -setup` 配置编译器。 2. **MATLAB Function 块执行慢** - **建议**:改用 S-Function 或生成代码(Embedded Coder)。 3. **如何调试 S-Function** - **MATLAB 版**:直接设置断点。 - **C MEX 版**:使用 `printf` 输出或调试器(如 Visual Studio)。 --- 通过合理选择 Function 或 S-Function,可以高效实现从简单脚本到复杂系统的建模需求。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值