好的,咱们今天就来聊聊如何在 MATLAB 自定义函数(而不是直接写在 m 文件里)里面,把 Simulink 模型跑起来。
你有没有遇到过这样的场景:你写了一个 MATLAB 函数,里面有很多参数需要调整,你想用 Simulink 来模拟这个系统的不同工况,然后把 Simulink 的输出结果通过 MATLAB 函数进行处理、分析,甚至作为下一次仿真的输入?直接在 m 文件里调用 `sim` 命令当然是最直接的方式,但有时候,我们希望把 Simulink 的调用逻辑封装得更“函数式”一些,比如作为另一个更复杂 MATLAB 函数的一部分,或者需要更灵活地控制仿真过程。
下面,咱们就一步步拆解,看看怎么实现这个目标。
核心思路:MATLAB 函数作为“控制器”
想象一下,你的 MATLAB 自定义函数就像一个“指挥官”,它负责:
1. 准备参数: 根据你的需求,生成 Simulink 模型需要的输入信号、模型参数等等。
2. 设置仿真: 告诉 Simulink 模型要以什么方式运行(仿真时间、步长、是否使用外部模式等)。
3. 执行仿真: 启动 Simulink 仿真。
4. 获取结果: 从仿真后的 Simulink 模型中提取你关心的输出数据。
5. 后续处理: 用 MATLAB 函数对这些数据进行进一步的计算、绘图、或者反馈给下一次迭代。
步骤详解
假设我们有一个简单的 Simulink 模型,名为 `my_simple_model.slx`,它有一个输入端口 `In1` 和一个输出端口 `Out1`。我们想在 MATLAB 自定义函数中控制它的仿真,并读取输出。
第一步:准备你的 Simulink 模型
首先,确保你的 Simulink 模型已经搭建好,并且具有明确的输入和输出接口。
输入: 通常是 Signal Builder、Inport 模块,或者可以通过 `set_param` 直接设置的数据。
输出: 通常是 Outport 模块,仿真结束后可以通过 `simOut` 变量访问。
第二步:MATLAB 自定义函数的编写
咱们来写一个 MATLAB 函数,比如 `runSimAndAnalyze(param1, param2)`。这个函数会接收一些参数,然后调用 Simulink,最后返回一个分析结果。
```matlab
function analysisResult = runSimAndAnalyze(param1, param2, varargin)
% runSimAndAnalyze: 这是一个示例 MATLAB 函数,用于调用 Simulink 模型并进行分析。
%
% Inputs:
% param1: Simulink 模型中的一个参数,例如增益。
% param2: Simulink 模型中的另一个参数,例如一个初始状态。
% varargin: 可选参数,例如仿真时间、模型名称等。
%
% Output:
% analysisResult: 对 Simulink 仿真结果进行分析后得到的值。
% 1. 解析可选参数 (例如:仿真时间,模型路径)
defaultModelName = 'my_simple_model'; % 假设模型文件名为 my_simple_model.slx
defaultStopTime = 10; % 默认仿真时间
defaultInputSignal = []; % 默认输入信号
p = inputParser;
addParameter(p, 'ModelName', defaultModelName, @ischar);
addParameter(p, 'StopTime', defaultStopTime, @isnumeric);
addParameter(p, 'InputSignal', defaultInputSignal, @(x) iscell(x) || isempty(x)); % InputSignal 可以是 cell 数组
parse(p, varargin{:});
modelName = p.Results.ModelName;
stopTime = p.Results.StopTime;
inputSignal = p.Results.InputSignal;
% 2. 设置 Simulink 模型的参数
% 在调用 sim 之前,我们可以使用 set_param 来动态修改模型参数
% 或者,如果模型有 Simscape Electrical (Simscape Power Systems) 的 Electrical
% Reference 等模块,可以通过其参数来影响仿真。
% 这里我们演示如何修改一个名为 'GainValue' 的 Gain 模块的值,
% 以及一个名为 'InitialState' 的 StateSpace 模块的初始状态。
% 注意:你需要根据你的具体模型来修改模块名和参数名。
% 找到模型中的 'Gain' 模块,并设置其 'Gain' 属性
% 假设模型中有个 Gain 模块,名字叫 'MyGainBlock'
try
% set_param([modelName, '/MyGainBlock'], 'Gain', num2str(param1));
% 如果你的模型是直接使用 Model Workspace 或 Workspace 变量,
% 可以在 MATLAB workspace 中设置这些变量,然后 Simulink 模型引用它们。
% 这种方式更推荐,因为它分离了模型结构和运行数据。
% 假设我们直接在 MATLAB Workspace 中定义了需要传递给 Simulink 的变量
sim_gain = param1;
sim_initial_state = param2;
% 如果模型使用了 Global Parameters,也可以在这里修改
% set_param(modelName, 'Solver', 'ode45'); % 设置求解器
% set_param(modelName, 'FixedStep', '0.01'); % 设置固定步长
catch ME
warning('Failed to set Simulink model parameters: %s', ME.message);
% 可以选择抛出错误或者继续执行
% rethrow(ME);
end
% 3. 准备输入信号
% 如果你的模型有输入端口,你需要提供输入信号。
% Simulink 的 sim 函数可以接受一个 `timeseries` 对象或一个 `struct` 数组作为输入。
% 这里我们演示如何创建一个简单的输入信号。
% `inputSignal` 可以是一个 cell 数组,每个 cell 包含一个输入端口的信号。
% 每个信号是一个 struct,包含 'Time' 和 'Data' 字段。
if ~isempty(inputSignal)
% 假设只有一个输入端口
signalTimes = inputSignal{1}.Time;
signalData = inputSignal{1}.Data;
inputTimeseries = timeseries(signalData, signalTimes);
else
% 如果没有提供输入信号,或者模型没有输入端口,则留空
inputTimeseries = [];
end
% 4. 设置仿真参数
% Simulink 的 sim 函数有丰富的参数可以控制仿真。
% 例如:'SimulationMode', 'SaveOutput', 'SaveFormat', 'ReturnWorkspaceOutputs' 等。
% 最常用的是 'ReturnWorkspaceOutputs', 配合 'SaveOutput', 'SaveFormat'
% 来获取仿真输出。
simOptions = simset('SrcWorkspace', 'current'); % 使用当前 MATLAB workspace
% simOptions = simset('SrcWorkspace', 'caller'); % 使用调用函数的 workspace
% 5. 执行 Simulink 仿真
% sim(modelName, stopTime, simOptions, inputs...)
% inputs 部分可以是一个 Cell Array,每个元素对应一个输入端口的信号。
% 如果模型有多个输入端口,inputs 需要是 {signal_port1, signal_port2, ...}
disp(['Starting Simulink simulation for model: ', modelName]);
disp(['Simulation StopTime: ', num2str(stopTime)]);
try
% 传入模型参数(如果之前设置了 Workspace 变量)
% 也可以直接通过 sim 函数的参数传递,但方式比较复杂,通常不推荐。
% 如果模型有输入端口,并且我们准备了 inputTimeseries
if ~isempty(inputTimeseries)
% 假设模型只有一个输入端口 'In1'
% 这里的 'In1' 是 Simulink 模型中 Inport 模块的名称
% 实际上,sim 函数更倾向于通过结构体来传递输入信号,
% 结构体的字段名对应模型中全局变量或 Signal Builder 的变量名。
% 更直接的方式是使用 timeseries 对象直接传入,sim 函数会解析。
%
% 另一种常见且推荐的方式是,在 MATLAB workspace 中定义一个
% 结构体变量,例如 `sim_inputs`,然后 `sim` 函数会查找。
%
% 示例:
% if ~isempty(inputSignal)
% sim_inputs.Time = inputSignal{1}.Time;
% sim_inputs.Data = inputSignal{1}.Data;
% % 如果有多个输入,可以这样组织:
% % sim_inputs.Time1 = ...; sim_inputs.Data1 = ...;
% % sim_inputs.Time2 = ...; sim_inputs.Data2 = ...;
% % 或者使用 timeseries 结构体数组
% sim_inputs = {timeseries(inputSignal{1}.Data, inputSignal{1}.Time)};
% else
% sim_inputs = {};
% end
% 假设我们直接将 timeseries 对象作为输入参数传递
% 注意:sim 函数的输入参数顺序非常重要,与模型的输入端口顺序一致。
% 如果模型有 N 个输入端口,则需要 N 个输入参数。
% sim(modelName, stopTime, simOptions, Input1, Input2, ...)
% 假设模型只有一个输入端口,我们将其命名为 'InputSignalData'
% 并且在 MATLAB workspace 中定义了 sim_input_data
% sim_input_data = timeseries(inputSignal{1}.Data, inputSignal{1}.Time);
% 另一种更灵活的方式是:
% sim(modelName, stopTime, 'ReturnWorkspaceOutputs', 'on', ...
% 'Solver', 'ode45', ...
% 'SimInput', {timeseries(inputSignal{1}.Data, inputSignal{1}.Time)});
% 最终,最通用的方式是直接通过 sim 函数的最后一个参数(一个cell数组)
% 来传递输入信号。cell 数组中的每个元素都是一个输入信号,
% 顺序与模型中 Inport 模块的顺序相对应。
%
% 让我们简化一点:如果我们希望传递一个名为 `sim_input_data` 的 timeseries
% 变量给模型,我们可以这样做:
sim_input_data_ts = timeseries(inputSignal{1}.Data, inputSignal{1}.Time);
% 并且在 MATLAB workspace 中定义 sim_input_data_ts
% 或者直接在 sim 函数中传递:
simOut = sim(modelName, stopTime, simOptions, {sim_input_data_ts});
else
% 如果模型没有输入端口,或者不提供输入信号
% simOut = sim(modelName, stopTime, simOptions);
% 假设模型内部有 Signal Generator,或者我们不需要外部输入
% 并且我们希望将模型中的变量(如 sim_gain, sim_initial_state)
% 传递给模型,可以通过设置 'SrcWorkspace' 为 'caller'
% simOptions = simset('SrcWorkspace', 'caller');
% simOut = sim(modelName, stopTime, simOptions);
% 最标准的方式是:
simOut = sim(modelName, stopTime, 'ReturnWorkspaceOutputs', 'on', 'Solver', 'ode45');
end
% 6. 获取仿真结果
% simOut 是一个 Simulink.SimulationOutput 对象。
% 要获取信号数据,需要访问它的 .get() 方法。
% 你需要知道 Simulink 模型中 Outport 模块的名称。
% 假设模型有一个名为 'Out1' 的 Outport 模块。
% 检查 simOut 是否成功获取
if isempty(simOut)
error('Simulink simulation did not return any output.');
end
% 假设你的模型只有一个 Outport 模块,命名为 'Out1'
% simOut.get('Out1') 会返回一个 timeseries 对象
outputSignal = simOut.get('Out1');
% 如果模型输出多个信号,你需要知道它们的名称
% 例如:
% outputSignal1 = simOut.get('Out1');
% outputSignal2 = simOut.get('Out2');
% 提取 timeseries 中的数据
outputTime = outputSignal.Time;
outputData = outputSignal.Data;
disp('Simulink simulation finished successfully.');
% 7. 对仿真结果进行分析
% 这里可以进行任何你需要的 MATLAB 操作,
% 例如计算平均值、最大值、绘制曲线等。
averageOutput = mean(outputData);
maxOutput = max(outputData);
analysisResult = struct('average', averageOutput, 'maximum', maxOutput, ...
'time', outputTime, 'data', outputData);
% 也可以选择在函数内绘图
figure;
plot(outputTime, outputData);
title(['Simulink Output for param1 = ', num2str(param1)]);
xlabel('Time');
ylabel('Output');
grid on;
catch ME
disp('Error during Simulink simulation or result processing:');
disp(ME.message);
analysisResult = []; % 返回空或者错误指示
rethrow(ME); % 抛出错误,让调用者知道出了问题
end
end
```
第三步:如何在 MATLAB 函数中调用它
现在,我们有了 `runSimAndAnalyze` 这个自定义函数。你可以这样在 MATLAB 命令窗口或另一个 m 文件中调用它:
```matlab
% 示例 1:基本调用
gainValue = 5;
initialState = 0.1;
result1 = runSimAndAnalyze(gainValue, initialState, 'ModelName', 'my_simple_model', 'StopTime', 15);
if ~isempty(result1)
disp('Analysis Result 1:');
disp(result1);
end
% 示例 2:提供输入信号
% 创建一个正弦波输入信号
t_input = 0:0.1:10;
u_input = sin(t_input);
input_signal_cell = {struct('Time', t_input, 'Data', u_input)};
result2 = runSimAndAnalyze(2, 0.5, 'ModelName', 'my_simple_model', ...
'StopTime', 10, 'InputSignal', input_signal_cell);
if ~isempty(result2)
disp('Analysis Result 2:');
disp(result2);
end
% 示例 3:只修改参数,使用模型默认的输入或无输入
result3 = runSimAndAnalyze(10, 0.2, 'ModelName', 'my_simple_model');
if ~isempty(result3)
disp('Analysis Result 3:');
disp(result3);
end
```
关键点总结与注意事项
1. `sim` 函数是核心: `sim(modelName, stopTime, options, inputs...)` 是在 MATLAB 中运行 Simulink 的主要命令。
`modelName`: Simulink 模型的 `.slx` 文件名(不带扩展名,如果文件在当前路径或 MATLAB 路径下)。
`stopTime`: 仿真的终止时间。
`options`: 一个 `simset` 对象,用于设置仿真选项。
`inputs...`: 这是一个 可选 的参数。如果你需要向模型输入数据,它是一个 Cell 数组,其中每个元素代表一个输入端口的信号。信号通常是 `timeseries` 对象,或者一个包含 `'Time'` 和 `'Data'` 字段的结构体。信号的顺序必须与模型中 Inport 模块的顺序一致。
2. `simset` 的用法:
`simset('SrcWorkspace', 'current')`:让 Simulink 仿真在 MATLAB 当前工作区(`ans` 所在的那个 workspace)查找变量。
`simset('SrcWorkspace', 'caller')`:让 Simulink 仿真在调用 `sim` 函数的那个 MATLAB 函数的工作区查找变量。这对于我们自定义函数内的调用非常有用,可以让我们直接在函数内部定义变量,然后传递给 Simulink。
`simset('ReturnWorkspaceOutputs', 'on')`:告诉 `sim` 函数将仿真输出(如信号数据)作为 `Simulink.SimulationOutput` 对象返回。这是获取结果的关键。
`'SaveOutput', 'on', 'SaveFormat', 'StructureWithTime'`:也是一种获取输出的方式,会将数据保存到结构体中,并且 `simOut` 对象也是结构体。`ReturnWorkspaceOutputs` 通常更方便。
3. 修改模型参数:
推荐方式:Workspace 变量。 在 MATLAB workspace 中定义与模型中参数名同名的变量(例如 `sim_gain = param1;`),然后在 `simset` 中设置 `'SrcWorkspace', 'caller'` 或 `'current'`。Simulink 模型在仿真时会自动查找并使用这些变量。这种方式最灵活,也最容易管理。
直接修改模块参数(`set_param`): `set_param([modelName, '/BlockPath'], 'ParameterName', value)`。这种方式可以动态修改模型中的任何参数,但需要你知道准确的模块路径和参数名,并且每次仿真前都需要设置,可能不如 Workspace 变量管理方便。
Model Workspace: 在 Simulink 模型内部,你可以通过“Model Explorer”配置 Model Workspace,然后将变量定义在那里。当你在 MATLAB 函数中调用 `sim` 时,可以通过 `simset('SrcWorkspace', 'model')` 来指向模型的工作区,或者直接在 MATLAB workspace 中定义同名变量,让它覆盖模型工作区中的变量(优先级取决于 Simulink 的变量查找机制)。
4. 获取仿真输出: `sim` 函数在设置为 `ReturnWorkspaceOutputs` 时,会返回一个 `Simulink.SimulationOutput` 对象。
`simOut.get('OutputPortName')`:这是获取特定输出信号(来自 Outport 模块)的标准方法。`OutputPortName` 是你在 Simulink 模型中 Outport 模块的 `Port name` 属性。
它返回一个 `timeseries` 对象,你可以从中提取 `Time` 和 `Data`。
5. 错误处理: 使用 `trycatch` 块来捕获 `sim` 函数执行过程中可能出现的错误,例如模型不存在、参数错误、仿真崩溃等,并给出友好的提示。
6. 函数封装: 将 `sim` 函数的调用和结果处理逻辑封装在你的自定义 MATLAB 函数中,使得主程序更加清晰,易于重用和调试。
这个过程就像是在用 MATLAB 脚本“编程”来控制 Simulink 仿真,你可以根据需要构建复杂的仿真流程,比如:
参数扫描: 在一个循环里调用你的自定义函数,每次传入不同的参数组合。
优化: 将你的自定义函数作为目标函数,配合 MATLAB 的优化工具箱(如 `fmincon`, `patternsearch`)来寻找最优的模型参数。
集成: 将 Simulink 仿真结果与其他 MATLAB 工具(如信号处理工具箱、控制系统工具箱)结合使用。
希望这个详细的解释能帮到你!动手实践一下,你会发现这种方式的强大和灵活。