我是靠谱客的博主 老实鱼,最近开发中收集的这篇文章主要介绍Simulink 环境基础知识(二十九)--在结构体中组织相关的模块参数定义,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

在结构体中组织相关的模块参数定义

创建并使用参数结构体

将数据类型信息存储在字段值中

通过创建参数对象来控制字段数据类型和特征

管理结构体变量

通过创建嵌套结构体定义参数层次结构

将多个参数结构体组成一个数组

创建常量值信号结构体

迁移到参数结构体之前的注意事项

将现有参数对象组合成一个结构体

生成的代码中的参数结构体

有关参数结构体的限制

为查找表打包共享断点和表数据

根据现有 C 代码中的结构体类型创建参数结构体


在结构体中组织相关的模块参数定义

        如果在模型中使用 MATLAB® 数值变量设置模块参数值,大型模型可能会累积很多变量,从而增加模型的维护工作,导致变量名称长度增加。更好的做法是将这些参数值组织成结构体。每个结构体是一个变量,结构体中的每个字段存储一个数字参数值。可以为结构体、子结构体和字段赋予有意义的名称,以指示每个值的目的。

        使用结构体可以:

  • 减少必须维护的工作区变量数。

  • 避免工作区变量之间发生名称冲突。

    不能在同一个作用域(例如,基础工作区)中创建两个同名的变量。创建结构体时,必须为每个字段提供一个名称,但不同的结构体可以包含同名的字段。因此,可以将每个结构体和子结构体作为一个命名空间,防止同一作用域中的字段名称相互冲突或与其他变量名称冲突。

  • 从逻辑上对模块参数值进行分组。例如,使用嵌套的结构体清楚地标识每个子系统或引用模型使用的参数值。

        如果使用封装参数或模型参数将参数值传递给系统的组件,可以使用结构体以减少必须维护的封装参数或模型参数的数量。可以传递一个结构体变量,而不是传递多个变量。有关创建和操作 MATLAB 结构体的基本信息,参考结构体。有关在模型中设置模块参数值的基本信息,参考设置模块参数值。要使用结构体初始化总线信号,参考指定总线信号的初始条件

创建并使用参数结构体

        说明如何在模型中创建并使用参数结构体。示例模型f14使用基础工作区中的多个变量来设置模块参数值。例如,当打开模型时,它会在基础工作区中创建变量Zw、Mw和Mq。要将这些变量组织成一个结构体变量,请执行以下操作:

  1. 在命令提示符下,打开示例模型。

    f14
  2. 在命令提示符下,创建参数结构体 myGains。通过使用目标变量的值设置字段值。

    myGains.Zw = Zw;
    myGains.Mw = Mw;
    myGains.Mq = Mq;
  3. 在 Model Explorer 中,在 Model Hierarchy 窗格中点击 Base Workspace。在 Contents 窗格中,右键点击变量Mq并选择 Find Where Used

  4. 在 Select a system 对话框中,点击节点 f14,然后点击 OK。当提示更新图时,点击 OK

  5. 在 Contents 窗格中,右键点击标有 Gain1 的模块所对应的行,然后选择 Properties。Gain1 模块对话框随即打开。

  6. 将 Gain 参数的值从Mq 更改为 myGains.Mq,然后点击 OK

  7. 在 Contents 窗格中,右键点击 Transfer Fcn.1模块所对应的行,然后选择 Properties

  8. 将 Denominator coefficients 参数的值从[1,-Mq] 更改为 [1,-myGains.Mq],然后点击 OK

  9. 在 Model Hierarchy 窗格中,点击 Base Workspace。使用 Find Where Used 定位使用变量Mw 和 Zw的模块。在模块对话框中,根据下表所示替换对变量名称的引用。

    变量名称替换名称
    MwmyGains.Mw
    ZwmyGains.Zw
  10. 清除旧变量。

    clear Zw Mw Mq

    现在,修改后的每个模块参数使用myGains结构体的一个字段。每个结构体字段的数值等于清除的对应变量的值。可以迁移模型以使用单个参数结构体,而不是多个工作区变量。

将数据类型信息存储在字段值中

        要使用结构体或结构体数组来组织使用非double数据类型的参数值,可以在创建结构体时显式指定数据类型。当创建结构体时,请使用类型化的表达式(如 single(15.23))指定字段值。

myParams.Gain = single(15.23);

        如果以后要更改字段值,必须记得再次显式指定类型。如果不指定类型,字段值将使用double数据类型:

myParams.Gain = 15.23;
% The field 'Gain' now uses the data type 'double' instead of 'single'.

        要保留类型指定,可以使用下标赋值为字段指定新值:


% Assign value of type 'single'.
myParams.Gain = single(15.23);

% Assign new value while retaining type 'single'.
myParams.Gain(:) = 11.79;

        要匹配定点数据类型,请使用fi (Fixed-Point Designer) 对象设置字段值。

通过创建参数对象来控制字段数据类型和特征

        Simulink.Parameter对象允许将模块参数的值与其数据类型分离。如果使用参数对象存储结构体或结构体数组,则可以创建一个Simulink.Bus 对象作为整个结构体的数据类型。

        可以使用总线对象和参数对象显式控制以下内容:

  • 每个字段的数据类型。使用这种方法时,不需要记得使用类型化表达式或下标赋值来设置字段值。

  • 每个字段的复/实性、维度和单位。

  • 每个字段的最小值和最大值(如果该字段代表可调参数值)。

  • 整个结构体的形状。结构体的形状是指字段的数量、名称和层次结构。

  • 结构体从模型中生成的代码中的可调性。

  1. 创建一个参数结构体 myParams。

    myParams = struct(...
        'SubsystemA',struct(...
            'Gain',15.23,...
            'Offset',89,...
            'Init',0.59),...
        'SubsystemB',struct(...
            'Coeffs',[5.32 7.99],...
            'Offset',57,...
            'Init1',1.76,...
            'Init2',2.76)...
    );
  2. 使用函数Simulink.Bus.createObject创建代表结构体和子结构的Simulink.Bus 对象。

    Simulink.Bus.createObject(myParams)

    因为 myParams 包含两个唯一子结构体,所以该函数创建三个Simulink.Bus对象:一个名为slBus1,代表父结构体myParams;一个名为SubsystemA,代表子结构体SubsystemA;一个名为SubsystemB,代表子结构体 SubsystemB。

  3. 将总线对象 slBus1 重命名为 myParamsType。

    myParamsType = slBus1;
    clear slBus1
  4. 将结构体 myParams 存储在 Simulink.Parameter 对象中。

    myParams = Simulink.Parameter(myParams);

    参数对象的 Value 属性包含该结构体。

  5. 将参数对象的数据类型设置为总线对象 myParamsType。

    myParams.DataType = 'Bus: myParamsType';
  6. 打开 Bus Editor 以查看总线对象。

    buseditor
  7. 在 Model Hierarchy 窗格中,点击 SubsystemA 节点。在 Contents 窗格中,根据下图设置字段数据类型。

  8. (可选)设置子结构体SubsystemB 的字段数据类型。

        参数对象 myParams 存储参数结构体。该参数对象的数据类型为总线对象myParamsType。在进行仿真和代码生成之前,该参数对象会将字段值转换为在总线对象中指定的数据类型。要使用这些字段之一设置模块参数值,请指定表达式,例如 myParams.SubsystemB.Init1。

        要在命令提示符下访问字段值,请使用参数对象的Value属性。因为总线对象控制字段数据类型,所以不需要使用类型化表达式来设置字段值。

myParams.Value.SubsystemA.Gain = 12.79;

        总线对象会严格控制字段特征和结构体的形状。例如,如果将二元素字段myParams.SubsystemB.Coeffs的值设置为三元素数组,则当设置模块参数值时,模型将产生错误。要更改字段的维度,请修改总线对象 SubsystemB 中的元素 Coeffs。

将字段数据类型与信号数据类型匹配

        假设使用字段 myParams.SubsystemA.Gain 为 Gain 模块中的Gain参数设置值。如果希望该字段的数据类型与该模块的输出信号的数据类型匹配,则不能依赖上下文相关数据类型指定。可以考虑使用 Simulink.AliasType 或 Simulink.NumericType 对象来设置字段和信号的数据类型。如果不使用数据类型对象,则每次更改信号的数据类型时,都必须记得更改字段的数据类型。

  1. 在命令提示符下创建一个 Simulink.AliasType 对象,代表数据类型 single。

    myType = Simulink.AliasType;
    myType.BaseType = 'single';
  2. 在 Gain 模块对话框中,在 Signal Attributes 选项卡上,将 Output data type 设置为 myType。

  3. 在命令提示符下,打开 Bus Editor。

    buseditor
  4. 在 Model Hierarchy 窗格中,选择总线对象 SubsystemA。在 Contents 窗格中,将字段 Gain 的数据类型设置为 myType。

        现在,Gain 模块的输出信号和结构体字段myParams.SubsystemA.Gain 都使用通过 myType 的 BaseType属性指定的数据类型。

管理结构体变量

        要创建、修改和检查其值为结构体的变量,可以使用 Variable Editor。可以参考已交互式方式修改结构体和数组变量

通过创建嵌套结构体定义参数层次结构

        要进一步组织模块参数值,可以创建一个嵌套的结构体层次结构。例如,假设在模型中创建了名为 SubsystemA 和SubsystemB的子系统。

        使用变量(例如 Offset_SubsystemA 和 Offset_SubsystemB)设置子系统中的模块参数值。 要进一步组织模块参数值,可以创建一个嵌套的结构体层次结构。例如,假设在模型中创建了名为 SubsystemA 和SubsystemB的子系统。

        使用变量(例如 Offset_SubsystemA 和 Offset_SubsystemB)设置子系统中的模块参数值。

Gain_SubsystemA = 15.23;
Offset_SubsystemA = 89;
Init_SubsystemA = 0.59;

Coeffs_SubsystemB = [5.32 7.99];
Offset_SubsystemB = 57;
Init1_SubsystemB = 1.76;
Init2_SubsystemB = 2.76;

        创建一个参数结构体,其中每个子系统对应一个子结构体。使用现有变量的值设置字段值。

myParams = struct(...
    'SubsystemA',struct(...
        'Gain',Gain_SubsystemA,...
        'Offset',Offset_SubsystemA,...
        'Init',Init_SubsystemA),...
    'SubsystemB',struct(...
        'Coeffs',Coeffs_SubsystemB,...
        'Offset',Offset_SubsystemB,...
        'Init1',Init1_SubsystemB,...
        'Init2',Init2_SubsystemB)...
);

        一个结构体变量myParams就包含了子系统中模块的所有参数信息。因为每个子结构体相当于一个命名空间,所以可以多次定义Offset字段。要使用子结构体SubsystemB中的Offset字段作为模块参数的值,请在模块对话框中以表达式 myParams.SubsystemB.Offset的形式指定该参数值。

将多个参数结构体组成一个数组

        要组织具有类似特征的参数结构体,可以创建一个值为结构体数组的变量。这种方式有助于您参数化包含一种算法的多个实例的模型,例如使用模型参数的库子系统或引用模型。假设在模型中创建两个相同的子系统。

        假设每个子系统中的模块需要三个数值来设置参数值。为这两个结构体创建一个数组以存储这些值。

myParams(1).Gain = 15.23;
myParams(1).Offset = 89;
myParams(1).Init = 0.59;

myParams(2).Gain = 11.93;
myParams(2).Offset = 57;
myParams(2).Init = 2.76;

        该数组中的每个结构体存储一个子系统的三个参数值。要设置其中一个子系统中的模块参数值,请指定一个表达式,引用该数组中一个结构体的一个字段。

        例如,使用表达式 myParams(2).Init。

组织可重用组件和迭代算法的参数值

        还可以在 For Each Subsystem 模块中对结构体数组进行分区。当一个模型重复执行某算法(例如,对某个向量信号迭代执行该算法)时,使用这种方法有助于组织工作区变量。如果使用模型实参为引用模型的多个实例指定不同的参数值,则可以使用结构体数组来组织模型实参值。在引用模型工作区中,创建一个结构体变量并将模型配置为使用该结构体作为模型参数。使用结构体的字段设置模型中的模块参数值。然后,在基础工作区或父模型所链接到的数据字典中创建一个结构体数组。在父模型中,使用该数组中的每个结构体作为一个 Model 模块中的模型参数的值。该数组中的每个结构体存储引用模型的一个实例的参数值。

        示例模型sldemo_mdlref_datamngt包含封装的引用模型sldemo_mdlref_counter_datamngt的三个实例(封装的 Model 模块)。基础工作区变量IC1、IC2、Param1 和 Param2 是值为结构体的Simulink.Parameter对象。父模型使用这些变量设置Model模块上的封装参数值。由于 IC1 与 IC2、Param1 与Param2在结构上相同,所以可以将这四个结构体组合成两个结构体数组。

  1. 打开父模型示例。

    sldemo_mdlref_datamngt

    该模型在基础工作区中创建四个 Simulink.Parameter 对象。

  2. 打开示例引用模型。

    sldemo_mdlref_counter_datamngt

    模型工作区定义两个值为结构体的模型参数CounterICs 和CounterParams。模型中的模块使用这些结构体的字段设置参数值。

  3. 在模型sldemo_mdlref_datamngt中,打开Model Data Editor(在 Modeling 选项卡上,点击 Model Data Editor)。在 Model Data Editor中,检查Parameters 选项卡。

  4. 在模型中,点击其中一个 Model 模块。

    Model Data Editor 突出显示与所选 Model 模块上两个封装参数对应的行。该模块使用封装参数来设置由引用模型 sldemo_mdlref_counter_datamngt 定义的两个模型参数的值。每个 Model 模块使用基础工作区中四个参数对象的不同组合来设置参数值。

  5. 在 Model Data Editor 的 Value 列中,点击其中一个单元格开始编辑相应的封装参数(例如 IC1)的值。在参数值的旁边,点击操作按钮 ,然后选择 Open。将打开该参数对象的属性对话框。

  6. 在属性对话框中,在 Value 框的旁边,点击操作按钮,然后选择 Open Variable Editor

    Variable Editor 显示参数对象存储一个结构体。Param2和IC2中的结构体与 Param1 和 IC1 中的结构体具有相同的字段,但字段值不同。

  7. 在命令提示符下,将这四个参数对象组合成两个值为结构体数组的参数对象。

    % Create a new parameter object by copying Param1.
    Param = Param1.copy;
    
    % Use the structure in Param2 as the second structure in the new object.
    Param.Value(2) = Param2.Value;
    % The value of Param is now an array of two structures.
    
    % Delete the old objects Param1 and Param2.
    clear Param1 Param2
    
    % Create a new parameter object by copying IC1.
    % Use the structure in IC2 as the second structure in the new object.
    IC = IC1.copy;
    IC.Value(2) = IC2.Value;
    clear IC1 IC2
  8. 在父模型中,在 Model Data Editor 中,使用 Value 列根据表替换封装参数的值

    旧值新值
    Param1Param(1)
    IC1IC(1)
    Param2Param(2)
    IC2IC(2)

        每个 Model 模块使用数组 IC 中的一个结构体来设置模型参数 CounterICs 的值。类似地,每个模块使用 Param 中的一个结构体来设置 CounterParams 的值。

强制结构体数组中的一致性

        一个结构体数组中的所有结构体都必须具有相同的字段层次结构。层次结构中的每个字段必须在整个数组中具有相同的特征。可以使用参数对象和总线对象来强制结构体之间保持这种一致性。

        要使用参数对象来表示参数结构体数组,请将对象的值设置为结构体数组:

% Create array of structures.
myParams(1).Gain = 15.23;
myParams(1).Offset = 89;
myParams(1).Init = 0.59;
myParams(2).Gain = 11.93;
myParams(2).Offset = 57;
myParams(2).Init = 2.76;

% Create bus object.
Simulink.Bus.createObject(myParams);
myParamsType = slBus1;
clear slBus1

% Create parameter object and set data type.
myParams = Simulink.Parameter(myParams);
myParams.DataType = 'Bus: myParamsType';

        要使用这些字段之一设置模块参数值,请指定表达式,例如myParams(2).Offset。要在命令提示符下访问字段值,请使用参数对象的Value属性。

myParams.Value(2).Offset = 129;

创建常量值信号结构体

        可以使用结构体在 Constant 模块中创建一个能够传输多个数值常量的总线信号。参考Constant。有关总线信号的信息,参考虚拟总线

迁移到参数结构体之前的注意事项

  • 迁移模型以使用参数结构体之前,需要找出目标模型和其他模型中使用要替换的变量的所有模块。

    例如,假设一个模型中有两个模块使用工作区变量 myVar。如果创建具有 myVar 字段的结构体 myParams,并且只将其中一个模块的参数值设置为 myParams.myVar,另一个模块继续使用变量 myVar。如果删除 myVar,模型会生成错误,因为另一个模块还需要这个删除的变量。

    要找出使用某个变量的所有模块,请执行以下操作:

    1. 打开可能使用该变量的所有模型。如果这些模型在一个模型引用层次结构中,您可以只打开顶层模型。

    2. 在 Model Data Editor 或 Model Explorer 的 Contents 窗格中,右键点击该变量并选择 Find Where Used。Model Explorer 将显示使用该变量的所有模块。

    只能在打开的模型中查看是否使用了该变量。在迁移到参数结构体之前,请打开可能使用目标变量的所有模型。有关如何确定模型中是否使用某个变量的详细信息,参考查找使用特定变量的模块。也可以避免删除 myVar。但是,如果更改 myParams.myVar 结构体字段的值,还必须记得更改myVar的值以保持匹配。

  • 可以将多个单独的变量或参数对象(例如 Simulink.Parameter)组合成一个结构体,存储在单个变量或参数对象中。但是,生成的变量或对象作为一个实体存在。因此,不能为该结构体中的各个字段应用不同的代码生成设置(如存储类)。

将现有参数对象组合成一个结构体

        当使用参数对象设置模块参数值时(例如,为了应用存储类),要将这些对象组合成一个结构体,请执行以下操作:

  1. 创建一个 MATLAB 结构体并将其存储在某个变量中。要设置字段值,请使用每个现有参数对象存储的参数值。

  2. 将该变量转换为参数对象。创建一个 Simulink.Bus 对象并将其作为参数对象的数据类型。

  3. 选择要对生成的参数对象应用的存储类。只能选择一个存储类,它将应用于整个结构体。

  4. 将参数元数据(例如,现有参数对象的 Min 和 Max 属性)传输给总线对象中 Simulink.BusElement对象的对应属性。

        例如,假设有三个单独的参数对象。

coeff = Simulink.Parameter(17.5);
coeff.Min = 14.33;
coeff.DataType = 'single';
coeff.StorageClass = 'ExportedGlobal';

init = Simulink.Parameter(0.00938);
init.Min = -0.005;
init.Max = 0.103;
init.DataType = 'single';
init.StorageClass = 'Model default';

offset = Simulink.Parameter(199);
offset.DataType = 'uint8';
offset.StorageClass = 'ExportedGlobal';
  1. 创建结构体变量。

    myParams.coeff = coeff.Value;
    myParams.init = init.Value;
    myParams.offset = offset.Value;
  2. 将该变量转换为参数对象。

    myParams = Simulink.Parameter(myParams);
  3. 创建一个总线对象并将其作为参数对象的数据类型。

    Simulink.Bus.createObject(myParams.Value);
    paramsDT = copy(slBus1);
    
    myParams.DataType = 'Bus: paramsDT';
  4. 将元数据从旧参数对象传输给总线对象中的总线元素。

    % coeff
    paramsDT.Elements(1).Min = coeff.Min;
    paramsDT.Elements(1).DataType = coeff.DataType;
    
    % init
    paramsDT.Elements(2).Min = init.Min;
    paramsDT.Elements(2).Max = init.Max;
    paramsDT.Elements(2).DataType = init.DataType;
    
    % offset
    paramsDT.Elements(3).DataType = offset.DataType;

    为了帮助编写执行此传输操作的脚本,可以使用properties函数查找总线元素和旧参数对象都具有的公共属性。要列出结构体字段以便对它们进行迭代,请使用fieldnames函数。

  5. 将存储类应用于参数对象。

myParams.StorageClass = 'ExportedGlobal';

        现在,可以使用 myParams(而不是旧参数对象)的字段来设置模块参数值。

生成的代码中的参数结构体

        可以配置参数结构体,使其以结构体和结构体数组的形式出现在生成的代码中。

有关参数结构体的限制

  • 用于设置模块参数的字段值必须为数值或枚举类型。字段的值可以为实数或复数标量、向量或多维数组。

  • 如果一个结构体的任何字段的值是多维数组,则不能在仿真过程中调整任何字段的值。

  • 一个结构体数组中的所有结构体都必须具有相同的字段层次结构。层次结构中的每个字段必须在整个数组中具有相同的特征:

    • 字段名称

    • 数值数据类型,如 single 或 int32。

    • Complexity

    • Dimensions

    假设定义一个数组,其中包含两个结构体。

    paramStructArray = ...
    [struct('sensor1',int16(7),'sensor2',single(9.23)) ...
     struct('sensor1',int32(9),'sensor2',single(11.71))];

    不能在模块参数中使用任何这些字段,因为字段sensor1在每个结构体中使用不同的数据类型。

  • 参数结构体不支持在生成的代码中根据上下文确定数据类型。如果代码中的参数结构体可调,则该结构体的字段使用通过类型化表达式或Simulink.Bus对象指定的数值数据类型。如果不使用类型化表达式或Simulink.Bus对象,该结构体的字段将使用double数据类型。

为查找表打包共享断点和表数据

        当在查找表模块之间共享数据时,请考虑使用Simulink.LookupTable和Simulink.Breakpoint对象来存储数据和对数据进行分组,而不是使用结构体。这种方法通过清楚地将数据标识为查找表的一部分,并将断点数据与表数据显式关联,从而提高了模型的可读性。

根据现有 C 代码中的结构体类型创建参数结构体

        可以创建符合现有C代码定义的struct类型定义的参数结构体。使用此方法可以:

  • 将现有 C 代码替换为 Simulink® 模型。

  • 集成现有 C 代码以便在 Simulink 中进行仿真(例如,通过使用 Legacy Code Tool)。

  • 生成可与现有 C 代码一起编译成一个应用程序的 C 代码 (Simulink Coder™)。

        在 MATLAB 中,可将参数结构体存储在参数对象中,并使用总线对象作为数据类型(参考通过创建参数对象来控制字段数据类型和特征)。要根据C代码struct类型创建总线对象,可以使用Simulink.importExternalCTypes函数。

最后

以上就是老实鱼为你收集整理的Simulink 环境基础知识(二十九)--在结构体中组织相关的模块参数定义的全部内容,希望文章能够帮你解决Simulink 环境基础知识(二十九)--在结构体中组织相关的模块参数定义所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部