尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Simulink与App Designer实时通信:数据绘图、参数下发与跨语言转发全攻略

Simulink与App Designer实时通信:数据绘图、参数下发与跨语言转发全攻略 这次我们直接解决一个很常见的仿真痛点Simulink 模型里算出来的数据怎么实时画到 App 界面上怎么从界面下发参数给模型怎么在模型运行过程中实现双向通信。很多人在做课程设计、毕业设计或者工程项目时都会遇到这种情况Simulink 里仿真跑得挺好但结果只在 Scope 里看想做一个“像样”的上位机界面时却不知道怎么把 Simulink 的数据接到 MATLAB App Designer 或者自己的 C/Python 程序里。花大量时间做界面最后却卡在了“数据怎么传过去”这一步。这篇文章要讲的就是打通这条链路。核心内容包括三块第一Simulink 和 App 之间实时通信的几种常见方案第二如何把仿真数据实时绘制到 App 界面上第三如何把参数从 App 下发到模型、把结果导出给其他工具。文章会给出可落地的 MATLAB 代码、配置方法和排查思路适合正在做 Simulink 仿真监控界面、四旋翼仿真、车辆联合仿真、电池仿真等项目的人。1. 核心能力速览能力项说明技术栈MATLAB / Simulink MATLAB App Designer实时通信方案回调函数配合sim()、Simulink External Mode、UDP/TCP、MATLAB Engine API绘图能力App Designer 坐标区实时更新、animatedline 动态追加、Scope 输出联动参数传递方向App 到模型Simulink.SimulationInput、set_param模型到 AppsimOut、工作区变量数据导出能力MAT 文件、Excelwritetable、工作区变量批量任务parsim批量仿真参数扫描结果自动汇总实时性说明普通 External Mode 属于软实时适合监控与调参硬实时需 Simulink Real-Time 等工具包适用场景仿真监控界面、课程设计上位机、HIL 前验证、联合仿真数据中转合规要求涉及硬件控制、蓝牙通信、外部设备时必须先获得设备授权并在测试环境验证从能力上看Simulink 和 App 的通信链路可以做到“界面操作 - 模型参数变化 - 仿真结果回传 - 界面实时绘图”的闭环。不同方案的区别主要在实时性、开发成本和可扩展性上。2. 适用场景与使用边界这个技术方案适合以下几类人正在用 Simulink 做仿真但觉得 Scope 展示效果太简陋想做一个独立监控界面。课设、毕设里有“上位机 下位机”需求需要用 App 下发控制参数、显示传感器曲线。在做四旋翼、车辆、电池等复杂模型联合仿真需要把 MATLAB 数据实时转发给其他程序。需要批量跑多组参数对比实验希望自动导出结果到 Excel 或 MAT 文件。不合适的场景也要说清楚。如果你的应用要求毫秒级硬实时控制例如直接驱动真实电机并保证控制周期严格稳定那么普通 External Mode 和 UDP 转发都不够需要配合 Simulink Real-Time、Speedgoat 或外部实时控制器。如果只是“看起来实时”的监控例如每 100ms 刷新一次曲线那常规方案完全够用。另一个边界是授权与合规。如果 App 界面通过蓝牙控制 ESP32 或其他真实设备你必须确认设备是自己的或已获得授权不能对公共设备、他人设备做未授权控制。仿真数据如果涉及隐私例如位置信息、生物特征也需要在本地测试环境处理不要随意上传到公共服务器。3. 环境准备与前置条件3.1 软件环境MATLAB 版本建议 R2020a 及以上。版本越新App Designer 组件和 Simulink API 兼容性越好。必须安装 Simulink 工具箱。如果要做外部模式通信需要安装 Simulink Desktop Real-Time 或 Simulink Coder。做硬件在环时需要对应硬件支持包。如果要把 App 导出独立运行需要 MATLAB Compiler。如果要通过 Python 调用 MATLAB需要安装 MATLAB Engine API for Python并保证 Python 版本与 MATLAB 版本匹配。3.2 硬件环境不需要独立显卡Simulink 仿真和 App 界面主要吃 CPU 和内存。建议 CPU 具有 4 核以上内存 16GB 左右尤其是模型较大或批量仿真时内存很容易成为瓶颈。如果通过 UDP/TCP 与外部设备通信需要确认本机端口没有被防火墙拦截。如果要连接 ESP32、STM32 等真实设备需要准备对应的串口转 USB 模块或网络模块并安装好驱动。3.3 项目目录结构建议project/ model/ my_model.slx app/ MyApp.mlapp scripts/ run_sim.m export_data.m data/ sim_results.mat logs/ sim_log.txt把模型、App、脚本、数据分开存放后续做批量仿真和结果排查会方便很多。4. 核心技术方案Simulink 和 App 实时通信的三种路径这部分是文章的核心。三种方案分别对应不同需求方案 A 最简单适合“点击运行 - 等结果 - 画图”的流程方案 B 支持真实外部模式适合“模型运行时实时调参、实时看曲线”方案 C 适合跨语言、跨设备转发。4.1 方案 AApp Designer 回调中调用sim()仿真结束后传递数据这是最稳妥、最容易上手的方案。思路是在 App Designer 的按钮回调中调用sim()函数运行 Simulink 模型仿真结束后从simOut对象里取出时间和信号数据再用plot()绘制到 App 的坐标区组件。在 MATLAB App Designer 中放置一个“运行仿真”按钮按钮回调代码如下% 按钮回调运行仿真并在 App 坐标区绘制曲线 function runSimulationButtonPushed(app, event) modelName my_model; % 替换为实际模型名 % 通过 SimulationInput 传入外部输入参数 simIn Simulink.SimulationInput(modelName); simIn simIn.setVariable(inputGain, app.GainEditField.Value); % 运行仿真 simOut sim(simIn, StopTime, 10); % 提取时间与输出信号 t simOut.tout; y simOut.yout{1}.Values.Data; % 绘制到 App 的坐标区 plot(app.UIAxes, t, y); grid(app.UIAxes, on); xlabel(app.UIAxes, Time (s)); ylabel(app.UIAxes, Output); title(app.UIAxes, Simulink Simulation Result); end这套写法的几个关键点Simulink.SimulationInput可以在运行前动态设置模型工作区变量这是“App 输入参数 - 模型参数”的桥。模型端口需要用 “Signal Logging” 或 “Output” 配置默认情况下simOut.yout会保存输出信号。如果没有数据检查模型的“配置参数 - 数据导入/导出”里是否勾选了输出。simOut.tout是时间向量simOut.yout{1}.Values.Data是第一个输出信号的数值数组。如果是多输出依次取yout{2}、yout{3}即可。4.2 方案 BExternal Mode实现模型运行中的实时调参与监控如果你希望 Simulink 模型在运行时App 界面能实时刷新曲线、实时修改 PID 参数那就需要 External Mode外部模式。外部模式把 Simulink 模型编译成可执行代码通过 TCP/IP 或串口与 MATLAB 主机通信。配置好以后模型运行期间可以实时修改模块参数并立即生效。配置外部模式的基本步骤在 Simulink 模型中打开“配置参数”。选择“求解器”选项卡将“类型”设为“固定步长”必须设置固定步长数值。在“代码生成”选项卡中选择系统目标文件为slrt.tlc或ert.tlc。如果使用 Simulink Desktop Real-Time就选择对应的桌面实时目标。点击“部署到硬件”或“External Mode Control Panel”启动外部模式。在 MATLAB 命令行中可以用set_param实时修改参数% 外部模式运行时实时修改模型内的增益参数 set_param(my_model/Gain, Gain, 10);外部模式结合 App Designer 的典型做法是App 中的滑动条回调里调用set_param修改参数App 中的定时器组件每隔 0.1 秒从模型工作区读取实时输出并更新曲线。% App 定时器回调周期性刷新实时曲线 function simulationTimerFired(app, ~) % 从模型工作区读取最新数据 % 需要模型启用了信号记录功能 t evalin(base, tout); y evalin(base, yout); % 用 animatedline 增量更新并限定显示范围 addpoints(app.HistoryLine, t(end), y(end)); app.UIAxes.XLim [max(0, t(end)-10), t(end)1]; drawnow limitrate; end注意drawnow limitrate比drawnow更适合高频刷新避免界面卡顿。实际项目中建议在主界面留一个“开始外部模式”和“停止外部模式”的按钮由用户手动控制连接生命周期。4.3 方案 CUDP/TCP 或 MATLAB Engine API 跨语言转发如果你的 App 不是基于 MATLAB App Designer而是基于 C#、Python、Web 前端那么可以通过网络协议或 MATLAB Engine API 通信。UDP 是最轻量的方案。在 Simulink 模型中添加 UDP Send 模块将信号打包后发送到127.0.0.1或指定 IP 的端口。外部 Python 或 C# 程序监听该端口就能实时收到仿真数据。这个方案需要工具包支持例如 DSP System Toolbox 或 Instrument Control Toolbox。MATLAB Engine API 适合 Python 程序主动调用 MATLAB。先安装引擎cd matlabroot/extern/engines/python python setup.py install然后启动引擎并运行相应仿真代码import matlab.engine import matplotlib.pyplot as plt eng matlab.engine.start_matlab() eng.cd(/path/to/project, nargout0) eng.eval(simOut sim(my_model, StopTime, 5);, nargout0) t eng.eval(simOut.tout;) y eng.eval(simOut.yout{1}.Values.Data;) # 转为 Python 列表并绘图 t_list list(t) y_list list(y) plt.plot(t_list, y_list) plt.xlabel(Time (s)) plt.ylabel(Output) plt.grid(True) plt.show() eng.quit()这种方式适合“MATLAB 负责仿真Python/Web 负责展示”的架构。比如你在做车辆联合仿真Simulink 算出数据后实时传给 Flask 后端再由前端展示本质上就是这条路。5. 数据记录、绘图与批量导出5.1 Simulink 信号记录配置要拿到可以绘图的数据第一步是先让 Simulink 把信号记录下来。有两种做法一是直接在信号线上右键选择“日志记录所选信号”二是在模型配置参数中设置“数据导入/导出”里的输出选项。% 通过代码启用信号记录 set_param(my_model, SaveOutput, on); set_param(my_model, OutputSaveName, yout); set_param(my_model, SaveTime, on); set_param(my_model, TimeSaveName, tout);5.2 App Designer 实时绘图写法实时绘图最常用的组件是UIAxes加animatedline。相比反复调用plotanimatedline的增量追加方式在高频数据刷新时性能更好。% 初始化时创建动态曲线对象 app.HistoryLine animatedline(app.UIAxes, Color, [0.2 0.4 0.8], LineWidth, 1.5); % 每次定时器触发时追加数据点 function timerFired(app, ~) % 模拟实时获取数据实际场景替换为从模型读取 newTime app.SimTime 0.1; newValue sin(newTime) 0.05 * randn(); addpoints(app.HistoryLine, newTime, newValue); % 自动滚动显示最近 10 秒 app.UIAxes.XLim [max(0, newTime - 10), newTime 0.5]; drawnow limitrate; end这套代码在 App Designer 类中集成时需要把HistoryLine声明为类属性方便回调函数之间共享。5.3 导出到 Excel仿真完成后把数据导出为 Excel 是常用需求。直接用writetable即可% 将仿真结果导出为 Excel dataTable table(simOut.tout, simOut.yout{1}.Values.Data, ... VariableNames, {Time, Output}); writetable(dataTable, simulation_results.xlsx);如果模型有多个输出列名可以按需要扩展。注意simOut.yout{1}.Values.Data有可能是多维数组导出前需要squeeze或按通道拆分。5.4 批量参数扫描与结果汇总做参数扫描时parsim是比循环运行sim更高效的方案。它可以并发运行多个仿真任务然后把结果统一收集。% 批量参数扫描示例 gains [1, 2, 5, 10]; for i 1:length(gains) simIn(i) Simulink.SimulationInput(my_model); simIn(i) simIn(i).setVariable(inputGain, gains(i)); end % 并行运行所有仿真 simOuts parsim(simIn, ShowProgress, on); % 汇总每组的最后一个输出值 summary zeros(length(gains), 1); for i 1:length(gains) data simOuts(i).yout{1}.Values.Data; summary(i) data(end); end resultsTable table(gains, summary, VariableNames, {Gain, FinalValue}); writetable(resultsTable, scan_results.xlsx);注意parsim默认需要并行计算工具箱。如果没有可以改用for循环串行运行逻辑一致只是速度慢一些。6. 接口 API 与跨语言调用场景6.1 从 Simulink 模型向外发送 UDP 数据包以 UDP 发送为例Simulink 模型中可以加入 UDP Send 模块。运行模型后模型会周期性地将信号数据推送到指定端口。外部程序只需要监听该端口即可实时接收数据。% MATLAB 端 UDP 接收示例用于验证 Simulink 发送是否正常 u udp(127.0.0.1, 25000); u.InputBufferSize 1024; fopen(u); % 读取一帧数据阻塞等待最多 2 秒 data fread(u, 1, double); fclose(u); delete(u);这种方式适合“MATLAB 做仿真计算App 或 Web 做展示”的分离架构。接收端不需要安装 MATLAB灵活性高。6.2 从 App 向 Simulink 模型下发参数除了set_param还可以用Simulink.SimulationInput.setVariable在每次仿真前批量设置变量。两者的区别在于set_param直接改模型参数适合外部模式实时调参setVariable每次运行仿真时注入变量值不改变模型文件本身更适合批量扫描。实际项目里两种方式可以组合使用界面上用“运行”按钮启动普通仿真用“外部模式”按钮切换到实时调参。6.3 接口调用时的异常处理调用 API 时常见问题是 MATLAB 引擎没有启动、端口被占用、数据类型不匹配。这里给出一个简单的 Python 调用示例包含超时和异常捕获import matlab.engine eng matlab.engine.start_matlab() try: eng.eval(simOut sim(my_model, StopTime, 10);, nargout0) result eng.eval(simOut.yout{1}.Values.Data) print(仿真完成输出点数, len(result)) except Exception as e: print(调用失败, e) finally: eng.quit()批量任务建议在服务端生成任务列表逐条记录日志。任务失败时先检查模型名、路径和 MATLAB 进程是否残留再决定是否重试。接口服务如果要对外开放需要限制访问来源 IP不能把没有防护的 MATLAB 引擎直接暴露到公网。7. 资源占用与性能观察Simulink 和 App 实时通信的性能瓶颈通常不在显卡而在 CPU、内存和通信延迟。观察对象主要有三个第一CPU 占用率。模型越大、步长越小、绘图刷新频率越高CPU 占用越高。仿真步长为1e-4的模型和步长为0.01的模型CPU 占用可能差一个数量级。第二内存占用。如果开启信号记录且仿真时间很长simOut中保存的数组会非常大。跑 100 秒仿真、步长 0.001 时单个信号就有 10 万个数据点多个信号叠加后内存占用会明显上升。批量仿真时内存更需要重点观察。第三通信延迟。External Mode 和 UDP 都有通信开销。本机 UDP 转发的延迟通常可以接受但跨设备、跨网络通信时延迟和丢包会直接影响实时曲线的平滑度。更稳妥的判断是把“实时”定义为界面刷新周期在 50ms 到 200ms 之间这个精度用常规方案可以满足如果要求硬实时就需要换实时仿真工具链。降低资源占用的几个实用做法绘图时用drawnow limitrate控制刷新频率不要每次都强制重绘。信号记录时只记录需要的信号不要全模型到处加日志。仿真参数扫描用parsim并行执行但并发数不要超过 CPU 核心数的两倍。外部程序接收 UDP 时设置合理的缓冲区大小避免接收速度跟不上发送速度导致丢包。8. 常见问题与排查方法问题现象可能原因排查方式解决方案App 点击运行后界面卡死sim()长时间运行界面被阻塞检查命令行是否有仿真在运行用parsim或定时器异步运行按钮回调中先drawnow再执行仿真simOut.yout为空模型没有启用信号记录检查配置参数中“输出”是否勾选信号线上是否有日志标记勾选“保存输出”定义输出端口名称外部模式连接失败目标文件未配置为实时目标端口被占用查看 Simulink 诊断窗口检查端口占用更换端口选择slrt.tlc或ert.tlc确认固定步长set_param修改参数不生效模型正在运行但外部模式未启动确认外部模式处于运行状态部署后运行模型再通过set_param调参实时曲线非常卡顿绘图刷新频率过高plot反复创建对象检查回调中是否多次调用plot改用animatedlinedrawnow limitrateExcel 导出失败writetable输出路径不存在或变量格式不对检查目录权限和数据类型先mkdir创建目录用squeeze整理多维数组批量仿真内存溢出同时保存了太多仿真输出观察任务管理器内存占用减少parsim并发数或只保存需要的信号UDP 数据收不到端口被防火墙拦截数据格式不一致本机先用 MATLAB 或 Python 监听测试关闭防火墙测试统一字节序和数据类型MATLAB Engine 启动失败Python 版本与 MATLAB 不匹配查看 MATLAB 引擎安装日志换用兼容的 Python 版本重新安装引擎排查时遵循一个原则先确认链路两端再查中间转发。比如 App 收不到数据就先确认 Simulink 里有没有真实发送再确认接收端有没有监听正确端口最后确认双方的数据格式是否一致。9. 最佳实践与使用建议做一些工程化安排这套流程会稳定很多。第一次跑通时先用最小模型测试。不要直接上复杂车辆模型先建一个 Gain Scope 的简单模型验证 App 能收到数据、能绘图、能改参数再逐步替换成实际模型。这样可以避免“不知道是模型问题还是通信问题”的排查困境。保留一套最小可运行配置。把模型、App、Python 脚本、UDP 监听代码都放在同一个项目文件夹里并写一个README记录启动顺序。比如先启动 MATLAB再打开 App最后运行模型。模型文件、输入素材、输出结果分目录管理。仿真的中间变量不要全堆在基础工作区尽量使用Simulink.SimulationInput注入变量避免污染全局工作区。批量任务一定要加日志和失败重试。每次parsim的结果都单独保存到对应参数目录扫描结束后统一汇总。任务失败时先将错误信息写入日志文件再决定是否重跑。接口服务要限制访问范围。如果 MATLAB Engine 或 UDP 服务对外开放必须配置防火墙和 IP 白名单不能让任意设备直接触发仿真或读取数据。涉及人脸、声音、位置信息或他人设备控制时必须确认授权。仿真数据如果已经关联到真实设备例如通过 ESP32 控制真实电机必须确保设备属于自己并且在测试环境完成验证后再接入正式场景。10. 总结与下一步Simulink 和 App 实时通信、绘图、传递数据最值得先验证的是“方案 A”在 App Designer 中运行sim()把simOut里的数据绘制到坐标区。这套流程代码量不大但能让你马上建立“模型 - 数据 - 界面”的完整认知。跑通之后再切换到外部模式实现实时调参最后再上 UDP 或 MATLAB Engine 做跨语言扩展。最容易踩的坑有两个。第一个是simOut.yout拿不到数据根因是模型没有启用信号记录第二个是绘图卡顿根因是高频刷新时反复调用plot换成animatedline就能明显改善。后续可以继续扩展的方向包括用parsim做多参数批量扫描、把 App Designer 界面编译成独立程序、通过 UDP 把数据转发给 Web 前端或 C# 上位机、结合 Simulink Real-Time 做硬件在环测试。每一步都在现有代码基础上增量修改建议按这个顺序逐级推进。
返回列表