本页面提供的是上一版软件的文档。当前版本中已删除对应的英文页面。

将 MDF 通道映射到 Simulink 模型输入端口

此示例向您说明如何以编程方式通过 Simulink 模型的输入端口映射 MDF 通道并使用其数据。它收集 Simulink 模型的输入端口名称,并将它们与给定 MDF 文件的内容相关联。然后在它们之间创建链接,当模型运行时,该链接会使用来自 MDF 文件的通道数据。

获取模型详细信息

定义示例模型名称并将其打开。

mdlName = 'ModelForMDFInput';
open_system(mdlName);

使用 createInputDataset 函数获取关于模型及其输入的整体信息。

dsObj = createInputDataset(mdlName)
dsObj = 
Simulink.SimulationData.Dataset '' with 2 elements

                             Name      BlockPath 
                             ________  _________ 
    1  [1x1 timeseries]      triangle  ''       
    2  [1x1 struct    ]      busInput  ''       

  - Use braces { } to access, modify, or add elements using index.

获取模型输入端口名称

此模型既有总线又有单独的输入端口。helperGetMdlInputNames 函数说明如何获取所有模型输入的名称,而不管它们在模型中是如何定义的。

mdlInputNames = helperGetMdlInputNames(mdlName)
mdlInputNames = 4×1 string array
    "triangle"
    "pwm"
    "pwm_level"
    "pwm_filtered"

调查 MDF 文件

现在您已有模型的输入端口名称,就可以看到 MDF 文件中存在哪些通道,以便尝试匹配它们。MDF 功能的 channelList 函数允许快速访问 MDF 文件中存在的可用通道。

mdfName = 'CANape.MF4';
mdfObj = mdf(mdfName);
mdfChannelInfo = channelList(mdfObj)
mdfChannelInfo=120×9 table
            ChannelName            ChannelGroupNumber    ChannelGroupNumSamples    ChannelGroupAcquisitionName    ChannelGroupComment    ChannelDisplayName    ChannelUnit    ChannelComment                    ChannelDescription                 
    ___________________________    __________________    ______________________    ___________________________    ___________________    __________________    ___________    ______________    ___________________________________________________

    "ampl"                                 2                       199                        100ms                      100ms                   ""               100ms           100ms         "Amplitude of channel 1-3"                         
    "channel1"                             2                       199                        100ms                      100ms                   ""               100ms           100ms         "FLOAT demo signal (sine wave)"                    
    "Counter_B4"                           1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "Single bit demo signal (bit from a byte shifting)"
    "Counter_B5"                           1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "Single bit demo signal (bit from a byte shifting)"
    "Counter_B6"                           1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "Single bit demo signal (bit from a byte shifting)"
    "Counter_B7"                           1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "Single bit demo signal (bit from a byte shifting)"
    "map1_8_8_uc_measure"                  1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][0]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][1]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][2]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][3]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][4]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][5]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][6]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[0][7]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
    "map1_8_8_uc_measure[1][0]"            1                      1993                        10 ms                      10 ms                   ""               10 ms           10 ms         "8*8 fixed axis,  permanently morphing"            
      ⋮

构造表来管理关注的项目

使用一个表将模型输入端口映射到 MDF 通道。

channelTable = table();
channelTable.PortNames = mdlInputNames;
n = size(channelTable.PortNames,1);
channelTable.ChGrpNum = NaN(n,1);
channelTable.ChNameActual = strings(n,1);
channelTable
channelTable=4×3 table
      PortNames       ChGrpNum    ChNameActual
    ______________    ________    ____________

    "triangle"          NaN            ""     
    "pwm"               NaN            ""     
    "pwm_level"         NaN            ""     
    "pwm_filtered"      NaN            ""     

执行输入端口到通道的匹配

helperReportChannelInfo 函数在 MDF 文件中搜索与模型输入端口名称匹配的通道名称。找到通道后,该通道的详细信息会记录在表中。具体来说,包括文件中给定通道所在的通道组编号及其实际定义的名称。请注意,实际的通道名称与模型端口名称并不完全匹配。在本示例中,通道名称匹配不区分大小写,并忽略下划线字符。该算法可以根据具体应用的匹配标准的需要进行调整。

channelTable = helperReportChannelInfo(channelTable, mdfChannelInfo)
channelTable=4×3 table
      PortNames       ChGrpNum    ChNameActual 
    ______________    ________    _____________

    "triangle"           1        "Triangle"   
    "pwm"                1        "PWM"        
    "pwm_level"          1        "PWM_Level"  
    "pwm_filtered"       1        "PWMFiltered"

用 MDF 通道数据填充 Simulink 数据集对象

先前创建的数据集对象包含单个时序对象和由时序对象组成的结构体。这使得将数据分配回给它们有些困难。用户需要牢记以下事项:

  • 将 'TimeSeries' 指定为 MDF read 函数的返回类型时,必须为每个通道单独调用 read。

  • 由于数据集对象具有不同元素(时序标量和由时序对象组成的结构体标量),您需要手动管理集合,并确保写入正确的位置。

for ii = 1:dsObj.numElements
    switch ii
        case {1} % [1x1 timeseries], triangle
            % Read the input port data from the MDF file one channel at a time.
            mdfData = read(mdfObj, channelTable.ChGrpNum(ii), channelTable.ChNameActual(ii), 'OutputFormat', 'TimeSeries');
            % Populate the dataset object.
            dsObj{ii} = mdfData;
            
        case {2} % [1x1 struct], busInput
            for jj = 1:numel(fieldnames(dsObj.getElement(ii)))
                % Read the input port data from the MDF file one channel at a time.
                mdfData = read(mdfObj, channelTable.ChGrpNum(jj+1), channelTable.ChNameActual(jj+1), 'OutputFormat', 'TimeSeries');
                % Populate the dataset object.
                dsObj{ii}.(channelTable.PortNames{jj+1}) = mdfData;                
            end
    end
end
dsObj
dsObj = 
Simulink.SimulationData.Dataset '' with 2 elements

                             Name      BlockPath 
                             ________  _________ 
    1  [1x1 timeseries]      Triangle  ''       
    2  [1x1 struct    ]      busInput  ''       

  - Use braces { } to access, modify, or add elements using index.

将数据集用作 Simulink 模型的输入

set_param(mdlName, 'LoadExternalInput', 'on');
set_param(mdlName, 'ExternalInput', 'dsObj');

运行模型

执行模型时,请注意 MDF 通道数据正确映射到指定的输入端口,并按预期通过 Simulink 绘制。

open_system(mdlName);
bp = find_system(mdlName, 'BlockType', 'Scope');
open_system(bp);
pause(1)
set_param(mdlName, 'SimulationCommand', 'start');

辅助函数

function mdlInputNames = helperGetMdlInputNames(mdlName)
% helperGetMdlInputNames Find input port names of a Simulink model.
%
% This function takes in the name of a Simulink model and returns the names of each model input. This specific model has 
% both a bus and a stand-alone input port going into it. To drive an input port that expects a bus means you need to supply 
% the signals as timeseries objects in a struct that matches the structure of the bus object attached to the input port.

% Test to see if the model is currently loaded in memory.
isLoaded = bdIsLoaded(matlab.lang.makeValidName(mdlName));

% If the model is not open then load it.
if ~isLoaded
    load_system(mdlName);
end

dsObj = createInputDataset(mdlName);
numElements = dsObj.numElements;
isStruct = zeros(1:numElements);

% Check to see if any of the elements in the returned dataset object are
% structs. If they are, assume they are for an input port that accepts a bus.
for elementIdx = 1:numElements
    isStruct(elementIdx) = isa(dsObj.getElement(elementIdx),'struct');
end

% For a port that accepts a bus, the data to be loaded must be arranged in a struct
% that matches the structure of the bus object attached to the input port.
busInportIdx = 1;
for idx = 1:numElements
    if isStruct(idx)
        % Get names of signals from a bus input port.
        inPortsBus(busInportIdx, :) = string(fieldnames(dsObj.getElement(idx)));
    else
        % Get signal name from a non-bus input port.
        inPorts(idx) = string(dsObj.getElement(idx).Name);
    end
end

mdlInputNames = [inPorts, inPortsBus]';
end


function channelTableOut = helperReportChannelInfo(channelTableIn, mdfChannelInfo)
% channelTableOut Reports if a channel is present in a set of channel names.

% Assign the output data.
channelTableOut = channelTableIn;

% Remove underscores and make everything lowercase for matching.
inPortChannelNames = lower(erase(channelTableIn.PortNames,'_'));
mdfChannelNames = lower(erase(mdfChannelInfo.ChannelName,'_'));

% Match the input channel names to the channel names in the MDF file.
[~, inPortidx] = ismember(inPortChannelNames, mdfChannelNames);

% Assign the relevant information back to the channel table.
channelTableOut.ChGrpNum = mdfChannelInfo{(inPortidx), {'ChannelGroupNumber'}};
channelTableOut.ChNameActual = mdfChannelInfo{(inPortidx), {'ChannelName'}};
end