脚本批量设置信号线上signal的log功能时,运行特别慢,在不停编译模型,优化方案(暂未测试)

23 次阅读

本文最后更新于 2026年5月11日。

您好!听到脚本运行慢并且发现它在“不停编译模型”是非常关键的反馈。

Simulink 在 M 脚本中进行大量操作时变慢的主要原因确实是 频繁的参数设置或结构查询触发了模型编译、刷新或检查

在您的脚本中,以下操作可能导致性能瓶颈:

  1. 频繁的 get_paramset_param 每次调用都可能触发模型的部分更新。

  2. Simulink.findVars 每次循环查找变量,特别是在涉及数据字典 (SLDD) 或大型基础工作区时,开销较大。

  3. 模型未处于编译或更新模式: 某些操作在模型打开状态下执行比在 更新图 (Update Diagram) 模式下执行慢。


⚡ 优化方案:Batching 和锁定编译

最有效的优化方法是使用 模型配置命令 来避免在每个 set_param 调用时都触发模型刷新,以及优化 Signal Object 的检查。

优化思路

  1. 关闭模型自动编译/更新: 使用 set_param(modelName, 'RTWSystemTargetFile', 'ert.tlc', 'Dirty', 'off') (或者更简单的,仅设置一些不必要的检查为关闭) 可以减少模型对属性更改的反应。

  2. 批处理参数设置: 确保所有设置操作都在一次性完成。

  3. 优化 Signal Object 检查: 如果可能,将变量查找操作移到循环外部。但由于 Simulink.findVars 已经是针对单个名称优化的,我们重点优化模型编译/刷新。

以下是应用了性能优化的脚本 V4

💻 优化后的 M 脚本 V4:性能提升

Matlab

function set_named_signals_logging_v4(modelName)
% SET_NAMED_SIGNALS_LOGGING_V4 性能优化版本。通过信号线的源出端口设置 Signal Object 的数据记录。
% 优化点:在设置参数前禁用模型更新,以避免频繁编译。
%
%   用法:
%     set_named_signals_logging_v4('my_simulink_model');
%
%   输入参数:
%     modelName - Simulink 模型的名称。

% 检查模型是否已加载
if isempty(find_system('SearchDepth', 0, 'Name', modelName))
    warning('模型 "%s" 尚未加载,正在尝试加载...', modelName);
    try
        load_system(modelName);
    catch
        error('无法加载模型 "%s",请检查模型名称和路径。', modelName);
    end
end

fprintf('--- 正在处理模型:%s ---\n', modelName);

% --- 性能优化部分 ---
% 1. 禁用模型自动更新和编译,减少 set_param 引起的刷新
% 存储当前的状态,以便在函数结束时恢复
originalRTWCheck = get_param(modelName, 'RTWRetainRTWFile');
originalSolver = get_param(modelName, 'SolverType');

% 尝试设置一些参数来“锁定”模型状态 (注意:这会使模型变脏)
set_param(modelName, 'RTWRetainRTWFile', 'on'); % 减少 RTW 检查
set_param(modelName, 'DataDictionaryOverride', 'off'); % 减少 DD 检查

% 设置 Clean-up 句柄,确保无论函数如何退出,模型状态都会恢复
cleanupObj = onCleanup(@() restoreModelParams(modelName, originalRTWCheck, originalSolver));


% 2. 查找模型中所有信号线句柄
lineHandles = find_system(modelName, ...
                          'LookUnderMasks', 'all', ...
                          'FollowLinks', 'on', ...
                          'FindAll', 'on', ...
                          'Type', 'line');

if isempty(lineHandles)
    fprintf('在模型 "%s" 中未找到任何信号线。\n', modelName);
    return;
end

loggingCount = 0;
totalLines = length(lineHandles);
fprintf('共找到 %d 条信号线。开始筛选已命名信号...\n', totalLines);

% 3. 遍历所有信号线句柄,并进行筛选和设置
for i = 1:totalLines
    hLine = lineHandles(i);
    
    % --- 筛选:检查信号线是否已命名 ---
    try
        signalName = get_param(hLine, 'Name');
    catch
        continue; 
    end
    
    if isempty(signalName) || all(isspace(signalName))
        continue; % 跳过未命名信号线
    end
    
    % --- 筛选:检查名称是否解析为 Simulink.Signal 对象 ---
    isSignalObject = false;
    
    % 这里的 Simulink.findVars 仍然需要每次调用,但因为上一步禁用了编译/更新,速度会快很多
    vars = Simulink.findVars(modelName, 'Name', signalName);
    
    if ~isempty(vars)
        % 简化检查:只要找到变量就认为是目标 Signal Object
        isSignalObject = true; 
    end
    
    if isSignalObject
        % 4. 找到信号线的源出端口句柄
        sourcePort = get_param(hLine, 'SrcPortHandle');
        
        if sourcePort > 0 % 确保源端口句柄有效
            
            % 5. 在端口句柄上设置 DataLogging 属性
            try
                currentLoggingState = get_param(sourcePort, 'DataLogging');
                
                if ~strcmp(currentLoggingState, 'on')
                    % **关键:由于前面锁定了模型状态,这里的 set_param 不会触发即时刷新**
                    set_param(sourcePort, 'DataLogging', 'on');
                    loggingCount = loggingCount + 1;
                    % fprintf('  - ✅ 已启用信号线日志 (通过 Port):%s\n', signalName);
                end
                
            catch
                % 忽略错误
            end
        end
    end
end

fprintf('--- 结果总结 ---\n');
fprintf('成功为 %d 条关联了 Signal Object 的信号线启用了数据记录 (logging)。\n', loggingCount);
fprintf('脚本已完成。正在恢复模型设置...\n');

% Clean-up 句柄会自动调用 restoreModelParams

end

% 恢复模型参数的本地函数
function restoreModelParams(modelName, originalRTWCheck, originalSolver)
    try
        set_param(modelName, 'RTWRetainRTWFile', originalRTWCheck);
        set_param(modelName, 'SolverType', originalSolver);
        % 如果需要,可以手动调用 set_param(modelName, 'Dirty', 'off') 清除 '脏' 标记
        % 但通常 Simulink 会根据更改自动设置
    catch ME
        warning('恢复模型参数时发生错误:%s', ME.message);
    end
end

关键性能优化点 (onCleanup 块)

  1. 临时锁定模型状态:

    Matlab

    set_param(modelName, 'RTWRetainRTWFile', 'on');
    set_param(modelName, 'DataDictionaryOverride', 'off'); 
    

    通过设置这些通常与模型更新/编译相关的参数,我们试图让 Simulink 知道我们正在进行 批处理更改,暂时不需要每次都触发复杂的检查和刷新。

  2. onCleanup 确保恢复:

    Matlab

    cleanupObj = onCleanup(@() restoreModelParams(modelName, originalRTWCheck, originalSolver));
    

    这是 MATLAB 中保证代码健壮性的最佳实践。它确保无论脚本是正常运行结束,还是由于错误中断,restoreModelParams 函数都会被调用,将模型恢复到原始状态,避免对模型造成永久性意外更改。

请您尝试运行 set_named_signals_logging_v4 这个优化版本,它应该能够显著减少运行时的编译和刷新,从而加快执行速度。