Main Content

设计和创建自定义模块

设置工作环境以设计和创建自定义模块

此示例打开目录,其中包含本主题所需的以下文件。

  1. ex_customsat_lib.slx

  2. sldemo_customsat.slx

  3. msfuntmpl.m

  4. custom_sat.m

  5. custom_sat_final.m

  6. customsat_callback.m

  7. customsat_plotcallback.m

  8. custom_sat_plot.m

  9. plotsat.m

如何设计自定义模块

一般情况下,可按照以下过程设计自定义模块:

假设您要创建一个自定义饱和模块,以便基于模块参数或输入信号的值来限制信号的上限和下限。在此模块的第二个版本中,您希望能在仿真完成后绘制饱和界限。以下教程将指导您如何设计这些模块。库 ex_customsat_lib 中包含两个版本的自定义饱和模块。

示例模型 sldemo_customsat 使用此模块的基本版本。

定义自定义模块行为

首先定义自定义模块的功能和限制。在此示例中,模块支持以下功能:

  • 打开和关闭饱和上限或下限。

  • 通过模块参数设置上限和/或下限。

  • 使用输入信号设置上限和/或下限。

它还存在以下限制:

  • 达到饱和的输入信号必须为标量。

  • 输入信号和饱和界限的数据类型都必须为双精度。

  • 不需要生成代码。

确定自定义模块的类型

根据自定义模块的功能,模块实现需要支持以下功能:

  • 多个输入端口

  • 相对简单的算法

  • 没有连续或离散系统状态

因此,本教程使用 2 级 MATLAB® S-Function 实现自定义模块。MATLAB S-Function 支持多个输入,而且因为算法很简单,所以更新图或仿真模型时不会产生很大的开销。要了解与其他类型的自定义模块相比 MATLAB S-Function 提供了哪些不同的功能,请参阅比较自定义模块功能

参数化 MATLAB S-Function

首先定义 S-Function 参数。此示例需要四个参数:

  • 第一个参数指示如何设置饱和上限。此界限可以关闭、通过模块参数进行设置,或者通过输入信号进行设置。

  • 第二个参数是饱和上限的值。仅当通过模块参数设置饱和上限时才使用此值。如果使用了此参数,您应该能够在仿真过程中更改此参数的值,也就是说,此参数是可调的。

  • 第三个参数指示如何设置饱和下限。此界限可以关闭、通过模块参数进行设置,或者通过输入信号进行设置。

  • 第四个参数是饱和下限的值。仅当通过模块参数设置饱和下限时才使用此值。与饱和上限一样,此参数在使用时也是可调的。

第一个和第三个 S-Function 参数代表模式,它们必须转换为 S-Function 能够识别的值。因此,可为饱和上限和下限模式定义以下值:

  • 1 表示关闭饱和界限。

  • 2 表示通过模块参数设置饱和界限。

  • 3 表示通过输入信号设置饱和界限。

编写 MATLAB S-Function

定义 S-Function 参数和功能后,即可编写 S-Function。可以将模板 msfuntmpl.m 作为起始点来编写 2 级 MATLAB S-Function。您可以在文件 custom_sat.m 中找到自定义饱和模块的完整版本。在继续本教程之前,请打开此文件。

此 S-Function 将修改 S-Function 模板,如下所示:

  • setup 函数基于为饱和上限和下限模式输入的值来初始化输入端口数。如果通过输入信号设置饱和界限,此方法将为模块添加输入端口。然后,setup 方法指示有四个 S-Function 参数并设置参数可调性。最后,此方法注册在仿真过程中使用的 S-Function 方法。

    function setup(block)
    
    % The Simulink engine passes an instance of the Simulink.MSFcnRunTimeBlock
    % class to the setup method in the input argument "block". This is known as
    % the S-function block's run-time object.
    
    % Register original number of input ports based on the S-function
    % parameter values
    
    try % Wrap in a try/catch, in case no S-function parameters are entered
        lowMode    = block.DialogPrm(1).Data;
        upMode     = block.DialogPrm(3).Data;
        numInPorts = 1 + isequal(lowMode,3) + isequal(upMode,3);
    catch
        numInPorts=1;
    end % try/catch
    block.NumInputPorts = numInPorts;
    block.NumOutputPorts = 1;
    
    % Setup port properties to be inherited or dynamic
    block.SetPreCompInpPortInfoToDynamic;
    block.SetPreCompOutPortInfoToDynamic;
    
    % Override input port properties
    block.InputPort(1).DatatypeID  = 0;  % double
    block.InputPort(1).Complexity  = 'Real';
    
    % Override output port properties
    block.OutputPort(1).DatatypeID  = 0; % double
    block.OutputPort(1).Complexity  = 'Real';
    
    % Register parameters. In order:
    % -- If the upper bound is off (1) or on and set via a block parameter (2)
    %    or input signal (3)
    % -- The upper limit value. Should be empty if the upper limit is off or
    %    set via an input signal
    % -- If the lower bound is off (1) or on and set via a block parameter (2)
    %    or input signal (3)
    % -- The lower limit value. Should be empty if the lower limit is off or
    %    set via an input signal
    block.NumDialogPrms     = 4;
    block.DialogPrmsTunable = {'Nontunable','Tunable','Nontunable', ...
        'Tunable'};
    
    % Register continuous sample times [0 offset]
    block.SampleTimes = [0 0];
    
    %% -----------------------------------------------------------------
    %% Options
    %% -----------------------------------------------------------------
    % Specify if Accelerator should use TLC or call back into
    % MATLAB script
    block.SetAccelRunOnTLC(false);
    
    %% -----------------------------------------------------------------
    %% Register methods called during update diagram/compilation
    %% -----------------------------------------------------------------
    
    block.RegBlockMethod('CheckParameters',      @CheckPrms);
    block.RegBlockMethod('ProcessParameters',    @ProcessPrms);
    block.RegBlockMethod('PostPropagationSetup', @DoPostPropSetup);
    block.RegBlockMethod('Outputs',              @Outputs);
    block.RegBlockMethod('Terminate',            @Terminate);
    %end setup function
    

  • CheckParameters 方法验证在 2 级 MATLAB S-Function 模块中输入的值。

    function CheckPrms(block)
    
    lowMode = block.DialogPrm(1).Data;
    lowVal  = block.DialogPrm(2).Data;
    upMode  = block.DialogPrm(3).Data;
    upVal   = block.DialogPrm(4).Data;
    
    % The first and third dialog parameters must have values of 1-3
    if ~any(upMode == [1 2 3]);
        error('The first dialog parameter must be a value of 1, 2, or 3');
    end
    
    if ~any(lowMode == [1 2 3]);
        error('The first dialog parameter must be a value of 1, 2, or 3');
    end
    
    % If the upper or lower bound is specified via a dialog, make sure there
    % is a specified bound. Also, check that the value is of type double
    if isequal(upMode,2),
        if isempty(upVal),
            error('Enter a value for the upper saturation limit.');
        end
        if ~strcmp(class(upVal), 'double')
            error('The upper saturation limit must be of type double.');
        end
    end
    
    if isequal(lowMode,2),
        if isempty(lowVal),
            error('Enter a value for the lower saturation limit.');
        end
        if ~strcmp(class(lowVal), 'double')
            error('The lower saturation limit must be of type double.');
        end
    end
    
    % If a lower and upper limit are specified, make sure the specified
    % limits are compatible.
    if isequal(upMode,2) && isequal(lowMode,2),
        if lowVal >= upVal,
            error('The lower bound must be less than the upper bound.');
        end
    end
    
    %end CheckPrms function
    

  • ProcessParametersPostPropagationSetup 方法处理 S-Function 参数调优。

    function ProcessPrms(block)
    
    %% Update run time parameters
    block.AutoUpdateRuntimePrms;
    
    %end ProcessPrms function
    
    
    function DoPostPropSetup(block)
    
    %% Register all tunable parameters as runtime parameters.
    block.AutoRegRuntimePrms;
    
    %end DoPostPropSetup function

  • Outputs 方法基于 S-Function 参数设置和输入信号来计算模块的输出。

    function Outputs(block)
    
    lowMode    = block.DialogPrm(1).Data;
    upMode     = block.DialogPrm(3).Data;
    sigVal     = block.InputPort(1).Data;
    lowPortNum = 2; % Initialize potential input number for lower saturation limit
    
    % Check upper saturation limit
    if isequal(upMode,2), % Set via a block parameter
        upVal = block.RuntimePrm(2).Data;
    elseif isequal(upMode,3), % Set via an input port
        upVal = block.InputPort(2).Data;
        lowPortNum = 3; % Move lower boundary down one port number
    else
        upVal = inf;
    end
    
    % Check lower saturation limit
    if isequal(lowMode,2), % Set via a block parameter
        lowVal = block.RuntimePrm(1).Data;
    elseif isequal(lowMode,3), % Set via an input port
        lowVal = block.InputPort(lowPortNum).Data;
    else
        lowVal = -inf;
    end
    
    % Assign new value to signal
    if sigVal > upVal,
        sigVal = upVal;
    elseif sigVal < lowVal,
        sigVal=lowVal;
    end
    
    block.OutputPort(1).Data = sigVal;
    
    %end Outputs function

将自定义模块放置到库中

库允许您与其他用户共享您的自定义模块,轻松更新自定义模块副本的功能,以及将适用于某个特定工程的模块收集到一起。如果您的自定义模块需要总线作为接口,您可以通过在数据字典中创建总线对象并将字典附加到库来与库用户共享总线对象(请参阅Link Data Dictionary to Custom Libraries)。

此示例将自定义饱和模块放置到一个库中。

  1. 在 Simulink® 编辑器中,在仿真选项卡中,选择新建 >

  2. 从 User-Defined Functions 库中,将一个 2 级 MATLAB S-Function 模块拖放到您的新库中。

  3. 用文件名 saturation_lib 保存您的库。

  4. 双击该模块以打开其 Function 的“模块参数”对话框。

  5. S-Function 名称字段中,输入 S-Function 的名称。例如,输入 custom_sat。在参数字段中输入 2,-1,2,1

  6. 点击确定

    至此,您已创建一个可与其他用户共享的自定义饱和模块。

通过添加自定义用户界面,您可以让此模块更方便使用。

为自定义模块添加用户界面

您可以利用 Simulink 的封装功能,为自定义模块创建一个模块对话框。模块封装还允许您添加端口标签,以指示哪些端口对应于输入信号和饱和界限。

  1. 打开包含您刚创建的自定义模块的库 saturation_lib

  2. 右键点击 2 级 MATLAB S-Function 模块,然后选择封装 > 创建掩膜

  3. 图标绘制命令框的图标和端口窗格中,输入 port_label('input',1,'uSig'),然后点击应用

    此命令将默认端口标记为未饱和的输入信号。

  4. 参数和对话框窗格中,添加四个参数,它们对应于四个 S-Function 参数。对于每个新参数,将一个弹出式控件或编辑控件拖放到对话框部分,如下表所示。将每个参数拖放到“参数”组中。

    类型提示名称计算可调弹出选项回调
    popup上边界:upMode 

    无限制

    输入限制作为参数

    使用输入参数进行限制

    customsat_callback('upperbound_callback', gcb)
    edit上限:upVal不适用customsat_callback('upperparam_callback', gcb)

    类型提示名称计算可调弹出选项回调
    popup下边界:lowMode 

    无限制

    输入限制作为参数

    使用输入参数进行限制

    customsat_callback('lowerbound_callback', gcb)
    edit下限:lowVal不适用customsat_callback('lowerparam_callback', gcb)

    MATLAB S-Function 脚本 custom_sat_final.m 包含封装参数回调。在工作文件夹中查找 custom_sat_final.m,以定义此示例中的回调。此 MATLAB 脚本有两个输入参量。第一个输入参量是一个字符向量,指示哪个封装参数调用了回调。第二个输入参量是关联的 2 级 MATLAB S-Function 模块的句柄。

    下图显示了在封装编辑器中完成的参数和对话框窗格。

  5. 初始化窗格中,选中允许库模块修改其内容复选框。此设置允许 S-Function 更改模块上的端口数。

  6. 文档窗格中执行以下操作:

    • 封装类型字段中,输入

      Customized Saturation
    • 封装描述字段中,输入

      Limit the input signal to an upper and lower saturation value
      set either through a block parameter or input signal.

  7. 点击确定

  8. 要将 S-Function 参数映射到封装参数,请右键点击 2 级 MATLAB S-Function 模块,然后选择封装 > 查看封装内部

  9. S-Function 名称字段更改为 custom_sat_final,并将参数字段更改为 lowMode,lowVal,upMode,upVal

    下图显示更改之后的 Function 的“模块参数”对话框。

  10. 点击确定。保存并关闭库,以退出编辑模式。

  11. 重新打开库,然后双击自定义饱和模块,以打开封装参数对话框。

要创建更复杂的用户界面,可在封装的模块上放置一个 MATLAB 图形用户界面。模块 OpenFcn 调用 MATLAB 图形用户界面,它通过调用 set_param 基于用户界面上的设置来修改 S-Function 模块参数。

编写封装回调

函数 customsat_callback.m 包含自定义饱和模块封装参数对话框的封装回调代码。此函数通过调用 feval 来调用与每个封装参数对应的局部函数。

下面的局部函数基于选择的饱和上限模式来控制饱和上限字段的可见性。回调首先通过使用属性名称 MaskValues 调用 get_param 来获取所有封装参数的值。如果回调只需要一个封装参数的值,它可以使用具体的封装参数名称(例如 get_param(block,'upMode'))来调用 get_param。因为此示例需要两个封装参数值,所以它使用 MaskValues 属性以减少对 get_param 的调用。

然后,回调通过使用属性名称 MaskVisbilities 调用 get_param 来获取封装参数的可见性。此调用返回一个字符向量元胞数组,指示每个封装参数的可见性。回调基于选择的饱和上限模式更改封装可见性的值,然后更新端口标签文本。

最后,回调使用 set_param 命令更新模块的 MaskDisplay 属性,以标记模块的输入端口。

function customsat_callback(action,block)
% CUSTOMSAT_CALLBACK contains callbacks for custom saturation block

%   Copyright 2003-2007 The MathWorks, Inc.

%% Use function handle to call appropriate callback
feval(action,block)

%% Upper bound callback
function upperbound_callback(block)

vals = get_param(block,'MaskValues');
vis = get_param(block,'MaskVisibilities');
portStr = {'port_label(''input'',1,''uSig'')'};
switch vals{1}
    case 'No limit'
        set_param(block,'MaskVisibilities',[vis(1);{'off'};vis(3:4)]);
    case 'Enter limit as parameter'
        set_param(block,'MaskVisibilities',[vis(1);{'on'};vis(3:4)]);
    case 'Limit using input signal'
        set_param(block,'MaskVisibilities',[vis(1);{'off'};vis(3:4)]);
        portStr = [portStr;{'port_label(''input'',2,''up'')'}];
end
if strcmp(vals{3},'Limit using input signal'),
    portStr = [portStr;{['port_label(''input'',',num2str(length(portStr)+1), ...
        ',''low'')']}];
end
set_param(block,'MaskDisplay',char(portStr));

最后一次调用 set_param 会在 MATLAB S-Function custom_sat.m 中调用 setup 函数。因此,可以修改 setup 函数,以便基于封装参数值而不是基于 S-Function 参数值来设置输入端口数。对 setup 函数的此类更改可使 2 级 MATLAB S-Function 模块上的端口数与封装参数对话框中显示的值保持一致。

修改后的 MATLAB S-Function custom_sat_final.m 包含下面的新 setup 函数。如果您是按照本教程中的步骤进行操作的,请打开此文件。

%% Function: setup ===================================================
function setup(block)

% Register original number of ports based on settings in Mask Dialog
ud = getPortVisibility(block);
numInPorts = 1 + isequal(ud(1),3) + isequal(ud(2),3);

block.NumInputPorts = numInPorts;
block.NumOutputPorts = 1;

% Setup port properties to be inherited or dynamic
block.SetPreCompInpPortInfoToDynamic;
block.SetPreCompOutPortInfoToDynamic;

% Override input port properties
block.InputPort(1).DatatypeID  = 0;  % double
block.InputPort(1).Complexity  = 'Real';

% Override output port properties
block.OutputPort(1).DatatypeID  = 0; % double
block.OutputPort(1).Complexity  = 'Real';

% Register parameters. In order:
% -- If the upper bound is off (1) or on and set via a block parameter (2)
%    or input signal (3)
% -- The upper limit value. Should be empty if the upper limit is off or
%    set via an input signal
% -- If the lower bound is off (1) or on and set via a block parameter (2)
%    or input signal (3)
% -- The lower limit value. Should be empty if the lower limit is off or
%    set via an input signal
block.NumDialogPrms     = 4;
block.DialogPrmsTunable = {'Nontunable','Tunable','Nontunable','Tunable'};

% Register continuous sample times [0 offset]
block.SampleTimes = [0 0];

%% -----------------------------------------------------------------
%% Options
%% -----------------------------------------------------------------
% Specify if Accelerator should use TLC or call back into
% MATLAB script
block.SetAccelRunOnTLC(false);

%% -----------------------------------------------------------------
%% Register methods called during update diagram/compilation
%% -----------------------------------------------------------------

block.RegBlockMethod('CheckParameters',      @CheckPrms);
block.RegBlockMethod('ProcessParameters',    @ProcessPrms);
block.RegBlockMethod('PostPropagationSetup', @DoPostPropSetup);
block.RegBlockMethod('Outputs',              @Outputs);
block.RegBlockMethod('Terminate',            @Terminate);
%endfunction
custom_sat_final.m 中的局部函数 getPortVisibility 使用饱和界限模式构造一个标志,该标志被传递回 setup 函数。setup 函数使用此标志确定必需的输入端口数。
%% Function: Get Port Visibilities =======================================
function ud = getPortVisibility(block)

ud = [0 0];

vals = get_param(block.BlockHandle,'MaskValues');
switch vals{1}
    case 'No limit'
        ud(2) = 1;
    case 'Enter limit as parameter'
        ud(2) = 2;
    case 'Limit using input signal'
        ud(2) = 3;
end

switch vals{3}
    case 'No limit'
        ud(1) = 1;
    case 'Enter limit as parameter'
        ud(1) = 2;
    case 'Limit using input signal'
        ud(1) = 3;
end

使用模块回调添加模块功能

ex_customsat_lib 中的 User-Defined Saturation with Plotting 模块使用模块回调为原来的自定义饱和模块添加功能。此模块提供了一个选项,可在仿真结束时绘制饱和界限。以下步骤说明如何修改原来的自定义饱和模块以创建这个新模块。

  1. 在封装参数对话框中添加一个复选框,用于打开和关闭绘图选项。

    1. 右键点击 saturation_lib 中的 2 级 MATLAB S-Function 模块,然后选择封装 + 创建掩膜

    2. 在封装编辑器的参数窗格中,添加具有以下属性的第五个封装参数。

      提示名称类型可调类型选项回调
      绘制饱和界限plotcheck复选框不适用customsat_callback('plotsaturation',gcb)

    3. 点击确定

  2. 为新的复选框编写回调。此回调初始化一个结构体,以便在仿真过程中将饱和界限值存储到 2 级 MATLAB S-Function 模块 UserData 中。MATLAB 脚本 customsat_plotcallback.m 中包含这个新回调以及旧回调的各修改版本,以处理新的封装参数。如果您是按照本示例中的步骤进行操作的,请打开 customsat_plotcallback.m 并复制其中的局部函数,以覆盖 customsat_callback.m 中原有的局部函数。

    %% Plotting checkbox callback
    function plotsaturation(block)
    
    % Reinitialize the block's userdata
    vals = get_param(block,'MaskValues');
    ud = struct('time',[],'upBound',[],'upVal',[],'lowBound',[],'lowVal',[]);
    
    if strcmp(vals{1},'No limit'),
        ud.upBound = 'off';
    else
        ud.upBound = 'on';
    end
    
    if strcmp(vals{3},'No limit'),
        ud.lowBound = 'off';
    else
        ud.lowBound = 'on';
    end
    
    set_param(gcb,'UserData',ud);

  3. 如果适用,请更新 MATLAB S-Function Outputs 方法以存储饱和界限,如在新的 MATLAB S-Function custom_sat_plot.m 中那样。如果您是按照本示例中的步骤进行操作的,请复制 custom_sat_plot.m 中的 Outputs 方法,以覆盖 custom_sat_final.m 中原有的 Outputs 方法。

    %% Function: Outputs ===================================================
    function Outputs(block)
    
    lowMode    = block.DialogPrm(1).Data;
    upMode     = block.DialogPrm(3).Data;
    sigVal     = block.InputPort(1).Data;
    vals = get_param(block.BlockHandle,'MaskValues');
    plotFlag = vals{5};
    lowPortNum = 2;
    
    % Check upper saturation limit
    if isequal(upMode,2)
        upVal = block.RuntimePrm(2).Data;
    elseif isequal(upMode,3)
        upVal = block.InputPort(2).Data;
        lowPortNum = 3; % Move lower boundary down one port number
    else
        upVal = inf;
    end
    
    % Check lower saturation limit
    if isequal(lowMode,2),
        lowVal = block.RuntimePrm(1).Data;
    elseif isequal(lowMode,3)
        lowVal = block.InputPort(lowPortNum).Data;
    else
        lowVal = -inf;
    end
    
    % Use userdata to store limits, if plotFlag is on
    if strcmp(plotFlag,'on');    
        ud = get_param(block.BlockHandle,'UserData');
        ud.lowVal = [ud.lowVal;lowVal];
        ud.upVal = [ud.upVal;upVal];
        ud.time = [ud.time;block.CurrentTime];
        set_param(block.BlockHandle,'UserData',ud)
    end
    
    % Assign new value to signal
    if sigVal > upVal,
        sigVal = upVal;
    elseif sigVal < lowVal,
        sigVal=lowVal;
    end
    
    block.OutputPort(1).Data = sigVal;
    
    %endfunction

  4. 编写函数 plotsat.m 以绘制饱和界限。此函数提取 2 级 MATLAB S-Function 模块的句柄,并使用此句柄检索模块的 UserData。如果您是按照本教程中的步骤进行操作的,请将 plotsat.m 保存到您的工作文件夹中。

    function plotSat(block)
    
    % PLOTSAT contains the plotting routine for custom_sat_plot
    %   This routine is called by the S-function block's StopFcn.
    
    ud = get_param(block,'UserData');
    fig=[];
    if ~isempty(ud.time)
        if strcmp(ud.upBound,'on')
            fig = figure;
            plot(ud.time,ud.upVal,'r');
            hold on
        end
        if strcmp(ud.lowBound,'on')
            if isempty(fig),
                fig = figure;
            end
            plot(ud.time,ud.lowVal,'b');
        end
        if ~isempty(fig)
            title('Upper bound in red. Lower bound in blue.')
        end
        
        % Reinitialize userdata
        ud.upVal=[];
        ud.lowVal=[];
        ud.time = [];
        set_param(block,'UserData',ud);
    end
  5. 右键点击 2 级 MATLAB S-Function 模块,然后选择属性。“模块属性”对话框随即打开。在回调窗格中,修改 StopFcn 以调用绘图回调,如下图所示,然后点击确定

相关主题