3. 仿真控制自动化:使用sim函数、set_param控制仿真启停、仿真时间与求解器配置

各位工程师朋友,这一章我们来聊聊仿真控制自动化。说实话,我刚入行那会儿,每次跑仿真都是手动点"Run"按钮,改个参数再点一次,改个求解器再点一次……一天下来,手指头都点酸了。后来我意识到,这根本不是技术问题,是效率问题。你想想看,如果能让MATLAB自己帮我们控制仿真流程,那该多省心?

嗯,这一章我们就来搞定这件事。核心就三个东西:sim函数set_param,以及仿真时间和求解器的配置。掌握了它们,你就能像指挥家一样,让Simulink模型乖乖听你的话。

3.1 为什么需要仿真控制自动化?

我遇到过不少项目,需要跑几百组参数组合。比如电机控制系统的PID参数整定,手动改一次参数跑一次仿真,再记录结果……这种重复劳动,说白了就是浪费生命。

自动化控制的好处很明显:

  • 批量仿真:一次脚本跑完所有参数组合
  • 精确控制:仿真启停、时间、求解器全部由代码决定
  • 结果可复现:脚本一跑,结果一模一样,再也不怕"我上次明明跑出来了"这种尴尬
  • 集成测试:可以嵌入到更大的自动化测试框架中

核心思想:把仿真从"手动操作"变成"程序控制"。你写一次脚本,以后每次跑仿真都只需要执行一行命令。

3.2 sim函数:仿真启动的核心

sim函数是MATLAB控制Simulink仿真的入口。我个人习惯用它的两种调用方式:

3.2.1 基本用法

% 最简单的调用
sim('my_model');

% 带返回值的调用
simOut = sim('my_model');

% 指定仿真时间
simOut = sim('my_model', 'StopTime', '10');

% 指定参数
simOut = sim('my_model', 'StopTime', '10', 'Solver', 'ode45');

这里要注意,simOut是一个Simulink.SimulationOutput对象,里面包含了仿真结果、日志信息等。我在项目中经常用它来提取数据:

% 提取仿真结果
simOut = sim('my_model');
time = simOut.tout;          % 时间向量
data = simOut.yout;          % 输出数据
logs = simOut.logsout;       % 信号日志

3.2.2 使用Simulink.SimulationInput对象

这种方式更灵活,我强烈推荐。你可以预先配置好所有参数,然后一次性传给sim函数:

% 创建SimulationInput对象
simIn = Simulink.SimulationInput('my_model');

% 设置参数
simIn = simIn.setModelParameter('StopTime', '20');
simIn = simIn.setModelParameter('Solver', 'ode45');
simIn = simIn.setModelParameter('MaxStep', '0.01');

% 设置模块参数(这个很实用)
simIn = simIn.setBlockParameter('my_model/Gain', 'Gain', '5');

% 运行仿真
simOut = sim(simIn);

我的经验:用SimulationInput对象的好处是,你可以创建多个不同的配置对象,然后批量跑仿真。我曾经在一个项目中,用这种方式一次性跑了200组参数,省了整整两天时间。

3.3 set_param:仿真过程中的"遥控器"

set_param函数可以让你在仿真运行过程中动态修改参数。说白了,它就像是一个遥控器,你可以随时调整模型的行为。

3.3.1 仿真前配置

% 设置仿真参数
set_param('my_model', 'StopTime', '30');
set_param('my_model', 'Solver', 'ode23t');
set_param('my_model', 'MaxStep', '0.005');

% 设置模块参数
set_param('my_model/Gain', 'Gain', '10');
set_param('my_model/Constant', 'Value', '5');

3.3.2 仿真启停控制

这个功能我特别喜欢。你可以用代码精确控制仿真的开始和结束:

% 启动仿真
set_param('my_model', 'SimulationCommand', 'start');

% 暂停仿真
set_param('my_model', 'SimulationCommand', 'pause');

% 继续仿真
set_param('my_model', 'SimulationCommand', 'continue');

% 停止仿真
set_param('my_model', 'SimulationCommand', 'stop');

注意:使用SimulationCommand控制仿真启停时,要确保模型已经加载到内存中。我曾经犯过这个错——模型没加载就发命令,结果报错。建议先调用load_system('my_model')加载模型。

3.4 仿真时间配置

仿真时间配置看似简单,但里面有不少门道。我见过不少新手在这里栽跟头。

3.4.1 基本时间参数

参数名 说明 示例
StartTime 仿真起始时间 '0'
StopTime 仿真结束时间 '10'
MaxStep 最大步长 '0.01'
MinStep 最小步长 'auto'
InitialStep 初始步长 'auto'

3.4.2 时间配置的坑

我曾经在一个项目中,仿真一个快速变化的信号。我设了StopTime='100',但MaxStep设得太大,结果仿真结果完全失真。后来我总结了一个经验:

  • 快速变化系统:MaxStep要设小,比如信号频率的1/10
  • 慢速系统:MaxStep可以设大,提高仿真速度
  • 多速率系统:建议用MaxStep约束最大步长,避免错过关键事件

经验法则:MaxStep ≤ 系统最小时间常数的1/10。如果系统中有多个不同时间尺度的动态,取最小的那个。

3.5 求解器配置

求解器选择是个技术活。我刚开始做仿真时,总是用默认的auto求解器,结果有时候仿真特别慢,有时候精度不够。后来我花了不少时间研究这个。

3.5.1 求解器类型

求解器 类型 适用场景
ode45 变步长 大多数连续系统,精度高
ode23 变步长 中等精度,比ode45快
ode15s 变步长 刚性系统,比如电路仿真
ode23t 变步长 中等刚性系统
discrete 固定步长 纯离散系统
fixed-step 固定步长 实时仿真、硬件在环

3.5.2 求解器配置实战

% 配置变步长求解器
set_param('my_model', 'Solver', 'ode45');
set_param('my_model', 'RelTol', '1e-4');   % 相对容差
set_param('my_model', 'AbsTol', '1e-6');   % 绝对容差

% 配置固定步长求解器
set_param('my_model', 'Solver', 'ode4');   % 四阶龙格-库塔
set_param('my_model', 'FixedStep', '0.001'); % 固定步长

% 配置离散求解器
set_param('my_model', 'Solver', 'discrete');
set_param('my_model', 'FixedStep', '0.01');

我的建议:如果你不确定用哪个求解器,先试试ode45。如果仿真速度很慢或者出现警告,再换成ode15s。我在一个电力电子项目中,用ode45跑了半小时没跑完,换成ode23t后,3分钟就搞定了。

3.6 综合实战:自动化仿真脚本

好了,理论讲完了,我们来写一个完整的自动化仿真脚本。这个脚本我在多个项目中都用过,非常实用。

%% 自动化仿真控制脚本
% 作者:资深仿真工程师
% 功能:批量仿真不同参数组合

% 1. 加载模型
load_system('my_model');

% 2. 定义参数组合
Kp_values = [1, 2, 5, 10];
Ki_values = [0.1, 0.5, 1, 2];
results = [];

% 3. 批量仿真
for i = 1:length(Kp_values)
    for j = 1:length(Ki_values)
        % 创建仿真配置
        simIn = Simulink.SimulationInput('my_model');
        
        % 设置仿真参数
        simIn = simIn.setModelParameter('StopTime', '10');
        simIn = simIn.setModelParameter('Solver', 'ode45');
        simIn = simIn.setModelParameter('RelTol', '1e-4');
        
        % 设置模块参数
        simIn = simIn.setBlockParameter('my_model/PID Controller/P', 'Gain', ...
                                        num2str(Kp_values(i)));
        simIn = simIn.setBlockParameter('my_model/PID Controller/I', 'Gain', ...
                                        num2str(Ki_values(j)));
        
        % 运行仿真
        try
            simOut = sim(simIn);
            
            % 提取结果
            time = simOut.tout;
            output = simOut.yout{1}.Values.Data;
            
            % 计算性能指标
            overshoot = max(output) - output(end);
            settling_time = time(find(abs(output - output(end)) > 0.02*output(end), 1, 'last'));
            
            % 保存结果
            results = [results; Kp_values(i), Ki_values(j), overshoot, settling_time];
            
            fprintf('Kp=%.1f, Ki=%.2f: 超调量=%.2f, 调节时间=%.2f\n', ...
                    Kp_values(i), Ki_values(j), overshoot, settling_time);
        catch ME
            warning('仿真失败: Kp=%.1f, Ki=%.2f', Kp_values(i), Ki_values(j));
            disp(ME.message);
        end
    end
end

% 4. 保存结果
save('simulation_results.mat', 'results');

% 5. 关闭模型
close_system('my_model', 0);

disp('批量仿真完成!');

关键点:注意代码中的try-catch结构。我在项目中遇到过某些参数组合导致仿真崩溃的情况,如果不加异常处理,整个脚本就会中断。加上try-catch后,即使个别仿真失败,也能继续跑完所有组合。

3.7 本章知识体系

为了让你更直观地理解本章的知识结构,我画了一张流程图:

仿真控制自动化知识体系 仿真控制自动化 sim函数 set_param 时间与求解器 基本调用 SimulationInput 仿真前配置 启停控制 时间参数 求解器选择 综合实战:自动化仿真脚本 核心目标:用代码控制仿真流程,实现批量、精确、可复现的仿真 关键技巧:SimulationInput + try-catch + 参数批量遍历

3.8 避坑指南

最后,分享几个我在项目中踩过的坑:

  • 模型路径问题:如果模型不在当前工作目录,要用完整路径。我曾经因为路径问题折腾了半小时。
  • 参数类型set_param的参数值必须是字符串,即使是数字也要用num2str转换。
  • 仿真时间单位:Simulink默认时间单位是秒,但如果你用了其他单位,要注意统一。
  • 求解器容差RelTolAbsTol设得太小会导致仿真极慢,设得太大又可能不准确。我一般从1e-3开始试。

曾经踩过的坑:有一次我批量跑仿真,结果发现所有结果都一样。排查了半天,发现是set_param在循环中没生效——因为模型参数被缓存了。解决方案是在每次循环前调用set_param时加上'SimulationCommand', 'update'强制刷新。

好了,这一章的内容就到这里。仿真控制自动化是个很实用的技能,掌握了它,你的工作效率能提升一个档次。记住,核心就是用代码代替手动操作,让MATLAB帮你干活。


公众号:蓝海资料掘金营,微信deep3321