本文最后更新于 2026年5月11日。
您好!听到脚本运行慢并且发现它在“不停编译模型”是非常关键的反馈。
Simulink 在 M 脚本中进行大量操作时变慢的主要原因确实是 频繁的参数设置或结构查询触发了模型编译、刷新或检查。
在您的脚本中,以下操作可能导致性能瓶颈:
-
频繁的
get_param和set_param: 每次调用都可能触发模型的部分更新。 -
Simulink.findVars: 每次循环查找变量,特别是在涉及数据字典 (SLDD) 或大型基础工作区时,开销较大。 -
模型未处于编译或更新模式: 某些操作在模型打开状态下执行比在 更新图 (Update Diagram) 模式下执行慢。
⚡ 优化方案:Batching 和锁定编译
最有效的优化方法是使用 模型配置命令 来避免在每个 set_param 调用时都触发模型刷新,以及优化 Signal Object 的检查。
优化思路
-
关闭模型自动编译/更新: 使用
set_param(modelName, 'RTWSystemTargetFile', 'ert.tlc', 'Dirty', 'off')(或者更简单的,仅设置一些不必要的检查为关闭) 可以减少模型对属性更改的反应。 -
批处理参数设置: 确保所有设置操作都在一次性完成。
-
优化 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 块)
-
临时锁定模型状态:
Matlab
set_param(modelName, 'RTWRetainRTWFile', 'on'); set_param(modelName, 'DataDictionaryOverride', 'off');通过设置这些通常与模型更新/编译相关的参数,我们试图让 Simulink 知道我们正在进行 批处理更改,暂时不需要每次都触发复杂的检查和刷新。
-
onCleanup确保恢复:Matlab
cleanupObj = onCleanup(@() restoreModelParams(modelName, originalRTWCheck, originalSolver));这是 MATLAB 中保证代码健壮性的最佳实践。它确保无论脚本是正常运行结束,还是由于错误中断,
restoreModelParams函数都会被调用,将模型恢复到原始状态,避免对模型造成永久性意外更改。
请您尝试运行 set_named_signals_logging_v4 这个优化版本,它应该能够显著减少运行时的编译和刷新,从而加快执行速度。