Main Content

在仿真和代码执行期间在参数值集之间切换

要存储相同模块参数的多个独立值集,可以使用结构体数组。要在参数集之间切换,请创建一个变量作为数组的索引,并更改该变量的值。您可以在仿真期间更改变量值,如果变量可调,还可以在生成的代码执行期间更改变量值。

在仿真和代码生成期间,还可以使用变体参数 (Simulink.VariantVariable) 有条件地更改模型中的模块参数值。有关示例,请参阅Create a Simple Variant Parameter Model

为模块参数实现可变值集的更好方法是使用变体参数 (Simulink.VariantVariable)。有关使用此工作流的示例,请参阅Create a Simple Variant Parameter Model。使用变体参数库 (Simulink.VariantBank) 将变体参数组合到生成的代码中的一个结构体数组中。变体参数库在代码中使用指针切换来基于变体条件切换活动参数值集。

浏览模型示例

打开示例模型。

open_system('sldemo_fuelsys_dd_controller')

此模型代表一台汽油发动机的供油系统。模型的输出是流向引擎的燃油速率。

导航到 switchable_compensation 嵌套子系统。

open_system(['sldemo_fuelsys_dd_controller/fuel_calc/',...
     'switchable_compensation'])

此子系统对喷油量信号中的噪声进行校正和滤波。该子系统基于燃油供给模式使用不同滤波器系数,从而根据引擎中的传感器故障控制逻辑更改。例如,控制算法在正常运行期间激活 low_mode 子系统。它激活 rich_mode 子系统以响应传感器故障。

打开 low_mode 子系统。

open_system(['sldemo_fuelsys_dd_controller/fuel_calc/',...
     'switchable_compensation/low_mode'])

Discrete Filter 模块对喷油量信号进行滤波。在模块对话框中,分子参数设置滤波器的分子系数。

同级子系统 rich_mode 还包含 Discrete Filter 模块,该模块使用不同系数。

更新模型图以显示信号数据类型。该模块的输入和输出信号使用单精度浮点数据类型 single

在模型的左下角,点击模型数据标记,然后点击数据字典链接。此模型的数据字典 sldemo_fuelsys_dd_controller.sldd 在模型资源管理器中打开。

在模型资源管理器的模型层次结构窗格中,选择设计数据节点。

目录窗格中,查看 Simulink.NumericType 对象的属性,如 s16En15。所有这些对象当前都表示单精度浮点数据类型 single。该模型使用这些对象来设置信号数据类型,包括 Discrete Filter 模块的输入和输出信号。

假设在仿真和执行生成的代码的过程中,您需要其中每个子系统都基于您控制其值的变量在不同分子系数之间切换。

将参数值存储在结构体数组中

将现有的分子系数集存储在其值为结构体的 Simulink.Parameter 对象中。该结构体的每个字段存储一个 Discrete Filter 模块的系数。

lowBlock = ['sldemo_fuelsys_dd_controller/fuel_calc/'...
    'switchable_compensation/low_mode/Discrete Filter'];
richBlock = ['sldemo_fuelsys_dd_controller/fuel_calc/'...
    'switchable_compensation/rich_mode/Discrete Filter'];
params.lowNumerator = eval(get_param(lowBlock,'Numerator'));
params.richNumerator = eval(get_param(richBlock,'Numerator'));
params = Simulink.Parameter(params);

params 的值复制到临时变量中。修改此临时结构体中的字段值,并将修改后的结构体指定为 params 的第二个元素。

temp = params.Value;
temp.lowNumerator = params.Value.lowNumerator * 2;
temp.richNumerator = params.Value.richNumerator * 2;
params.Value(2) = temp;
clear temp

params 的值是一个包含两个结构体的数组。每个结构体存储一个滤波器系数集。

创建变量以在参数集之间切换

创建名为 CtrlSimulink.Parameter 对象。

Ctrl = Simulink.Parameter(2);
Ctrl.DataType = 'uint8';

low_mode 子系统的 Discrete Filter 模块对话框中,将分子参数设置为表达式 params(Ctrl).lowNumerator

set_param(lowBlock,'Numerator','params(Ctrl).lowNumerator');

rich_mode 子系统的 Discrete Filter 模块中,将分子参数的值设置为 params(Ctrl).richNumerator

set_param(richBlock,'Numerator','params(Ctrl).richNumerator');

表达式使用变量 Ctrl 选择 params 中的一个结构体。然后表达式解引用结构体中的一个字段。该字段值设置分子系数的值。

要在系数集之间切换,只需将 Ctrl 的值更改为结构体数组中的对应索引。

使用总线对象作为结构体数组的数据类型

(可选)创建一个 Simulink.Bus 对象,用作结构体数组的数据类型。您可以:

  • 控制结构体的形状。

  • 对于每个字段,控制数据类型和物理单位等特性。

  • 在生成的代码中控制 struct 类型的名称。

使用函数 Simulink.Bus.createObject 创建对象,并将对象重命名为 paramsType

Simulink.Bus.createObject(params.Value)
paramsType = slBus1;
clear slBus1

您可以使用数据字典中的 Simulink.NumericType 对象来控制结构体字段的数据类型。在总线对象中,使用数据类型对象的名称来设置每个元素的 DataType 属性。

paramsType.Elements(1).DataType = 's16En15';
paramsType.Elements(2).DataType = 's16En7';

使用总线对象作为结构体数组的数据类型。

params.DataType = 'Bus: paramsType';

使用枚举类型切换变量

(可选)使用枚举类型作为切换变量的数据类型。您可以将每个参数集与一个有意义的名称相关联,并限制切换变量的允许值。

创建一个名为 FilterCoeffs 的枚举类型。为 params 中的每个结构体创建一个枚举成员。将每个枚举成员的基础整数值设置为 params 中的对应索引。

Simulink.defineIntEnumType('FilterCoeffs',{'Weak','Aggressive'},[1 2])

使用枚举类型作为切换变量的数据类型。将变量的值设置为 Aggressive,它对应于索引 2

Ctrl.Value = FilterCoeffs.Aggressive;

向数据字典添加新对象

将您创建的对象添加到数据字典 sldemo_fuelsys_dd_controller.sldd

dictObj = Simulink.data.dictionary.open('sldemo_fuelsys_dd_controller.sldd');
sectObj = getSection(dictObj,'Design Data');
addEntry(sectObj,'Ctrl',Ctrl)
addEntry(sectObj,'params',params)
addEntry(sectObj,'paramsType',paramsType)

您也可以在数据字典中存储枚举类型。但在这种情况下,您无法导入枚举类型,因为您无法保存对 sldemo_fuelsys_dd_controller.sldd 的更改。有关在数据字典中存储枚举类型的详细信息,请参阅Enumerations in Data Dictionary

仿真期间在参数集之间切换

打开示例模型 sldemo_fuelsys_dd,它引用控制器模型 sldemo_fuelsys_dd_controller

open_system('sldemo_fuelsys_dd')

将仿真停止时间设置为 Inf,以便您可以在仿真期间与模型交互。

开始仿真运行并打开 Scope 模块对话框。示波器显示,在引擎正常运行期间,燃油流量(fuel 信号)大幅波动。

在模型资源管理器中,查看数据字典 sldemo_fuelsys_dd_controller.sldd 的内容。将 Ctrl 的值设置为 FilterCoeffs.Weak

更新 sldemo_fuelsys_dd 模型图。示波器显示,喷油量振荡的振幅由于滤波器系数减弱而降低。

停止仿真。

生成和检查代码

如果您有 Simulink Coder 软件,您可以生成代码,使您能够在代码执行期间在参数集之间切换。

在模型资源管理器中,查看数据字典 sldemo_fuelsys_dd_controller.sldd 的内容。在目录窗格中,将列视图设置为 Storage Class

使用 StorageClass 列将存储类 ExportedGlobal 应用于 params,以便结构体数组在生成的代码中显示为可调全局变量。将相同的存储类应用于 Ctrl,以便您可以在代码执行期间更改切换变量的值。

或者,要配置对象,请使用以下命令:

tempEntryObj = getEntry(sectObj,'params');
params = getValue(tempEntryObj);
params.StorageClass = 'ExportedGlobal';
setValue(tempEntryObj,params);

tempEntryObj = getEntry(sectObj,'Ctrl');
Ctrl = getValue(tempEntryObj);
Ctrl.StorageClass = 'ExportedGlobal';
setValue(tempEntryObj,Ctrl);

从控制器模型生成代码。

slbuild('sldemo_fuelsys_dd_controller')
### Starting build procedure for: sldemo_fuelsys_dd_controller
### Successful completion of code generation for: sldemo_fuelsys_dd_controller

Build Summary

Top model targets built:

Model                         Action           Rebuild Reason                                    
=================================================================================================
sldemo_fuelsys_dd_controller  Code generated.  Code generation information file does not exist.  

1 of 1 models built (0 models already up to date)
Build duration: 0h 0m 41.715s

在代码生成报告中,查看头文件 sldemo_fuelsys_dd_controller_types.h。代码定义枚举数据类型 FilterCoeffs

file = fullfile('sldemo_fuelsys_dd_controller_ert_rtw',...
    'sldemo_fuelsys_dd_controller_types.h');
coder.example.extractLines(file,'#ifndef DEFINED_TYPEDEF_FOR_FilterCoeffs_',...
    '/* Forward declaration for rtModel */',1,0)
#ifndef DEFINED_TYPEDEF_FOR_FilterCoeffs_
#define DEFINED_TYPEDEF_FOR_FilterCoeffs_

typedef enum {
  Weak = 1,                            /* Default value */
  Aggressive
} FilterCoeffs;

#endif

代码还定义结构体类型 paramsType,它对应于 Simulink.Bus 对象。这些字段使用模型中的单精度浮点数据类型。

coder.example.extractLines(file,'#ifndef DEFINED_TYPEDEF_FOR_paramsType_',...
    '#ifndef DEFINED_TYPEDEF_FOR_FilterCoeffs_',1,0)
#ifndef DEFINED_TYPEDEF_FOR_paramsType_
#define DEFINED_TYPEDEF_FOR_paramsType_

typedef struct {
  real32_T lowNumerator[2];
  real32_T richNumerator[2];
} paramsType;

#endif

查看源文件 sldemo_fuelsys_dd_controller.c。代码使用枚举类型来定义切换变量 Ctrl

file = fullfile('sldemo_fuelsys_dd_controller_ert_rtw',...
    'sldemo_fuelsys_dd_controller.c');
coder.example.extractLines(file,'FilterCoeffs Ctrl = Aggressive;',...
    '/* Block signals (default storage) */',1,0)
FilterCoeffs Ctrl = Aggressive;        /* Variable: Ctrl
                                        * Referenced by:
                                        *   '<S12>/Discrete Filter'
                                        *   '<S13>/Discrete Filter'
                                        */

代码还定义结构体数组 params

coder.example.extractLines(file,'/* Exported block parameters */',...
    '/* Variable: params',1,1)
/* Exported block parameters */
paramsType params[2] = { {
    { 8.7696F, -8.5104F },

    { 0.0F, 0.2592F }
  }, { { 17.5392F, -17.0208F },

    { 0.0F, 0.5184F }
  } } ;                                /* Variable: params

模型的 step 函数中的代码算法使用切换变量对结构体数组进行索引。

要在存储在结构体数组中的参数集之间切换,请在代码执行期间更改 Ctrl 的值。

关闭与数据字典的连接。此示例丢弃未保存的更改。要保存更改,请使用 '-save' 选项。

Simulink.data.dictionary.closeAll('sldemo_fuelsys_dd_controller.sldd','-discard')

相关主题