资讯动态

Simulink中To Workspace的二维数据导出与绘图全解析

发布时间:2026/10/14 7:39:06 来源:尧图企业网站定制
做Simulink仿真的人早晚都会撞上To Workspace这个模块。它的功能很直白把模块输入信号送到MATLAB工作空间。但直白不等于简单我第一次用的时候照着教程拖了个To Workspace出来仿真跑完工作空间里多了个simout我心想成了然后输入plot(simout)结果MATLAB回给我一屏红色报错因为simout是个结构体不是可以直接plot的矩阵。如果你处理的是二维数据这个问题会被放大——二维数据在Simulink里可能是多通道信号也可能是每步采样都更新一次的矩阵保存格式不对画图时要么维度对不上要么只剩下最后几千个点。这篇内容主要围绕两条线展开一条是To Workspace的设置与数据格式另一条是拿到工作空间变量后怎么绘制图像。适合做过一两次仿真、但对数据后处理还不太熟的人也适合被仿真结果折磨过的老手来查漏补缺。下文所有的参数和方法都是我在实际模型里验证过的尤其末尾的几个坑几乎每个新项目都要再犯一遍。1. To Workspace模块参数解读搞清楚数据到底被保存成了什么1.1 四种保存格式对应四种数据形态To Workspace模块的参数对话框里最关键的是Variable name和Save format。前者决定工作空间变量叫什么后者决定这个变量长什么样。很多人在这一步就踩了坑以为Save format只是数据封装的差别其实它直接决定你后面能不能顺利plot。保存格式工作空间变量形态画图前需要做的事Structure with time结构体包含time和signals字段手动提取signals.values和时间向量Structure结构体只有signals字段没有time自己补时间轴麻烦Timeseriestimeseries对象自带时间和数据数组可直接plot或取Data和TimeArray纯二维/多维数组不包含时间自己生成时间向量后plot我自己的经验是如果只是快速看波形用Timeseries效率最高如果要写一个长期复用的后处理脚本我会选Structure with time因为结构体里的dimensions、label字段能把信号的维度和名字都带出来数据是自描述的。Array格式最直观但适用范围很窄后面会用专门一节讲它的坑。1.2 Limit data points和Decimation一个容易被忽略的截断开关在To Workspace设置界面里有两个参数经常被忽略Limit data points to last和Decimation。Limit data points to last默认值是5000意思是最多保存最后5000个采样点。如果你仿真时间很长、步长又小比如0.01秒步长跑100秒就有10001个点TO Workspace会悄悄丢掉前一半数据只留下后5000个点。很多人画出的曲线莫名其妙只剩后半段就是这里出的问题。解决办法很简单把Limit data points to last改成inf表示不限制。Decimation是抽样间隔默认1表示每个采样点都记录。如果设成10就表示每10个点里取1个点保存。这个参数会直接影响时间轴的长度和密度也会让画出来的曲线变糙。如果只是为了观察趋势设个大一点的Decimation没问题但要分析动态响应细节建议还是保持1。1.3 二维数据在Simulink里的真实维度含义“二维数据”这四个字在Simulink语境里其实有两种完全不同的意思必须分清楚。第一种是常见的多通道时间序列。某一个采样时刻信号是一个1×M的行向量比如Mux模块把两个正弦波合在一起就是一路2通道向量信号。仿真N步之后数据在MATLAB里变成N×2的二维矩阵行是时间列是通道。这种二维最友好直接plot就能用。第二种是信号本身就是二维矩阵。典型场景是图像处理、有限元或状态矩阵演化比如每一采样时刻输出一个2×2矩阵。这时候To Workspace保存下来的东西从外表看不是二维而是三维第一个维度是采样时刻后两个维度是矩阵的行和列。很多新手看到三维数组就懵了其实它的含义就是“不同时刻的一堆矩阵快照”。区分这两种形态的方法很直接双击信号线看宽度说明或者在仿真结束后用size(simout.signals.values)看数组维度。后面所有取数和绘图操作都取决于你先搞清楚这一点。2. 工作空间取数实战结构体、Timeseries和纯数组应该怎么用2.1 Structure with time解剖里面的values和time如果你用的是Structure with time格式那么工作空间里的变量是一个结构体常见字段是time、signals、blockName。signals又是一个结构数组里面包含values、dimensions、label等重要字段。取数标准写法是t simout.time; d simout.signals.values; dim simout.signals.dimensions;比如一个二维矩阵信号运行后size(d)返回[1001, 2, 2]含义是1001个采样时刻每个时刻一个2×2矩阵。要取第500个时刻的矩阵快照用squeeze(d(500, :, :))就可以得到一个2×2矩阵。如果dim是4说明这是一个4通道向量信号此时size(d)通常是[1001, 4]可以直接当普通二维矩阵处理。还要注意如果输入信号是总线或Mux合并的多组信号simout.signals可能不是单个结构而是一个结构数组这时候要写成simout.signals(1).values、simout.signals(2).values分别取。先跑一下fieldnames(simout.signals)看清楚再动手能省不少debug时间。2.2 Timeseries最省事的选择Timeseries格式是我现在最常用的保存格式。它本质是一个MATLAB timeseries对象自带时间和数据使用起来非常直接t simout.Time; y simout.Data;还可以直接plot(simout)MATLAB会自动把时间轴和数据画出来。但要注意plot(timeseries)对纯向量数据最友好如果Data是三维数组某些版本会报维度错误所以稳妥做法还是y squeeze(simout.Data);提前把多余的单例维度压缩掉后面画图就不会有意外。Timeseries另一个好处是可以通过getsampleusingtime截取时间段比如只分析前10秒的数据seg getsampleusingtime(simout, 0, 10);这个能力在长仿真里非常实用不用手动去数组里数下标。2.3 Array格式的边界条件和转置陷阱Array格式输出的变量就是一个纯数组画图前需要自己生成时间轴。比如数据y是[1001, 2]每列是一个通道时间步长ts为0.01那么t (0:size(y,1)-1) * ts; plot(t, y(:,1), t, y(:,2));这里最常翻车的点是维度方向搞反。有时候你从其他代码或旧脚本里拿到的数据是[2, 1001]也就是通道在前、时间在后如果直接plot(t, y)MATLAB会把2当作采样点数画出来的图完全不对。最保险的检查方法是用size看第一个维度是否和时间长度一致不一致就转置if size(y, 1) ~ length(t) y y.; end记住这一点能避免很多画图画到一半意识不到的诡异线条。3. 画图之前的数据调理维度压缩、时间轴对齐与批量绘图3.1 三维values降维成二维矩阵的三个函数当数据是“每个时刻一个二维矩阵”时直接进入绘图环节是不现实的。你需要先把三维数组整理成可循环或可观察的二维结构。最常用的三个函数是squeeze、permute和reshape。假设simout.signals.values的size是[N, R, C]N是采样点数R和C是矩阵行列data simout.signals.values; % 取第k个时刻的二维矩阵 frame squeeze(data(k, :, :)); % 如果想一次性把所有时刻展开成二维按行展平 data2d reshape(permute(data, [1 3 2]), N, []);permute的作用是把行列顺序调换配合reshape得到“每个采样点一行、每个矩阵元素一列”的格式。这个格式适合做PCA、相关性分析这类后续处理。如果不调换顺序直接reshape元素排列会错乱很多人的所谓“数据对不上”其实就是reshape顺序问题。3.2 时间轴的生成与对齐仿真步长、Decimation的关系时间轴最好是直接从保存结果里取不要自己用步长乘索引硬算。原因很简单变步长仿真、Decimation抽样、以及复杂模型中个别模块的采样时间不一致都会让实际的时间点不是均匀间隔。如果你用Structure with time或Timeseries时间轴就是simout.time或simout.Time这是最准的。如果非要自己生成必须先确认模型是定步长仿真且Decimation1否则一定对不齐。另外要注意如果信号经过Rate Transition或Zero-Order Hold它的时间戳和仿真主步长不一定一样。画图前先检查一下时间轴的差分dt diff(t); min(dt); max(dt);如果min(dt)和max(dt)相差很大说明数据里有不均匀采样用普通plot会显得毛糙但绘制时用plot(t, y)本身不受影响因为plot是按点连线不是按均匀网格。3.3 多通道批量绘制避免手写几十条plot的天真写法有5个、10个通道的时候很多人会一条条写plot既啰嗦也不容易维护。其实plot支持矩阵输入一个命令就能画多通道plot(t, y);只要y是[N, M]M列就会自动生成M条曲线。如果M比较大可以配合子图循环for i 1:M subplot(ceil(M/2), 2, i); plot(t, y(:, i)); title(sprintf(通道 %d, i)); grid on; end如果结构体里有label字段可以把它作为图例legends {simout.signals.label}; legend(legends{1:M}, Location, best);但这个label不一定都有值可能返回空字符串所以更好的做法是自己在脚本里维护一个channelNames的cell数组配合循环自动生成图例。这一步看起来不起眼实际在图很多的项目里能省下大量时间。4. 完整案例从Simulink模型到工作空间再到多张图像4.1 用两个信号源构造二维数据并接入To Workspace我们先做一个最简单的两通道向量信号用例。模型结构是两个Sine Wave模块频率分别设为1 rad/s和3 rad/s接到Mux再连To Workspace。To Workspace变量名设为simout_tsSave format选TimeseriesLimit data points to last设为infSample time继承输入即可。第二个用例是矩阵信号。我用一个MATLAB Function模块输入是Clock模块的时间输出一个2×2旋转矩阵function M fcn(u) M [cos(u), -sin(u); sin(u), cos(u)]; end把这个输出接另一个To Workspace变量名设为simout_matSave format选Structure with time。这个用例能很好体现“每个时刻一个二维矩阵”的数据形态。4.2 仿真后的数据检查whos、length、size一把梭仿真结束第一件事不是画图而是检查数据形状。我习惯跑这几行whos simout_ts simout_mat length(simout_ts.Time) size(simout_ts.Data) size(simout_mat.signals.values)预期结果是simout_ts.Data的大小约为[2001, 2]或[2001,1,2]simout_mat.signals.values的大小约为[2001,2,2]。如果看到第一个维度是2001说明数据长度符合预期如果看到5001多半是某个地方没有关掉默认截断。4.3 多通道折线图、热图和三维曲面图的绘图脚本多通道折线图直接写t simout_ts.Time; y squeeze(simout_ts.Data); plot(t, y); grid on; xlabel(时间 (s)); ylabel(幅值); legend({通道1, 通道2});矩阵信号的每个时刻快照可以用热图观察。比如把第1、500、1000、1500、2000个时刻的矩阵画出来data simout_mat.signals.values; N size(data, 1); idx round(linspace(1, N, 5)); for k 1:length(idx) subplot(1, length(idx), k); imagesc(squeeze(data(idx(k), :, :))); axis square; title(sprintf(t%.2f, simout_mat.time(idx(k)))); colorbar; end如果想要三维曲面图把imagesc换成surf[X, Y] meshgrid(1:2, 1:2); surf(X, Y, squeeze(data(idx(1), :, :)));这里矩阵只有2×2曲面会比较简单但流程是一样的。换成更大尺寸的矩阵比如10×10效果就很直观了。4.4 进阶把二维数据做成动态动画有时候静态图不足以反映矩阵随时间演化的过程我会在循环里连续更新图像生成动画效果。比如旋转矩阵每个时刻都在变化用以下脚本可以连续播放t simout_mat.time; data simout_mat.signals.values; N size(data, 1); figure; for k 1:10:N imagesc(squeeze(data(k, :, :))); axis square; title(sprintf(t%.2f, t(k))); colorbar; drawnow; pause(0.02); end如果想把动画保存成GIF可以在循环里用exportgraphics配合imwrite实现。这一步在分析二维矩阵信号时特别值因为它能看到变化趋势而不只是一帧帧的静态图。5. 踩坑实录数据丢失、维度翻转和时间错位5.1 设置了Array格式矩阵信号直接报错有一段时间我图省事喜欢把所有To Workspace都设成Array格式结果遇到矩阵信号时仿真直接报错提示当前信号维度不支持Array格式。查询文档才知道Array格式只对标量信号和向量信号友好矩阵信号想保存要么用Structure with time要么用Timeseries。这个坑几乎每个新人都要踩一次但记得一次就够了矩阵信号不要碰Array格式哪怕你只是临时看数据也优先选Timeseries。5.2 默认5000点截断仿真结果只剩后半段有一次仿真跑了一个小时步步长0.001秒总共好几百万个点。结果我画图时发现前面的响应过程完全消失曲线就是从某个时间点开始的。查了半天发现是Limit data points to last默认只保留最后5000个点。从那以后我的所有To Workspace模块都会先把Limit data points to last改成inf然后再开始仿真。如果数据量确实大也不要直接指望无限的inf而是考虑用Decimation降采样或者改用Signal Logging它在内存管理上更高效。5.3 模型里多个To Workspace共用同一个变量名后写入的覆盖前面的一个模型里如果有多个To Workspace很容易习惯性都叫simout。结果跑完仿真工作空间里只有一个simout而且内容还不全就是最后一个模块写入的数据。这种情况的排查很憋屈因为不报错只是数据对不上。建议从一开始就给每个模块起独立名字比如modelA_signal1、modelA_signal2。如果嫌手改麻烦可以把Model Configuration里Data Import/Export的Signal Logging统一打开配合信号名字自动生成变量但那是另一个话题了。5.4 结构体signals字段是结构数组直接signals.values会报错总线信号或由Mux合并的多组信号保存后simout.signals可能是一个结构数组每个元素代表一组信号。这时候直接写simout.signals.values通常是取不出来的因为MATLAB不知道你要取哪一个。正确姿势是先用fieldnames(simout.signals)看看结构再用simout.signals(1).values、simout.signals(2).values分别处理。如果信号数量非常多可以用循环for i 1:length(simout.signals) disp(size(simout.signals(i).values)); end看清楚每个字段的结构再动手比瞎试高效得多。6. 我现在的固定保存套路与一个最短路绘图技巧结合上面这些坑我现在的工作习惯基本固定了。第一凡是可能输出矩阵信号的模型一律用Timeseries或Structure with time不再去试Array。第二每个To Workspace模块都单独命名命名规则是模型名_信号名。第三跑完仿真第一件事永远是size和length确认数据形状和点数而不是直接plot。最后分享一个小技巧如果你只有一个变量名sudo且它的Save format是Timeseries最快画曲线的方式其实就是一行plot(simout)。但如果你需要微调线型、图例、坐标轴就强制自己先取出Time和Data再画。这个习惯能让你在任何情况下都有条不紊不会因为改了保存格式而手忙脚乱。二维数据用To Workspace输出再绘图整个过程就是这么回事。把这个流程固定下来以后任何仿真结果到你手里都能在几分钟内变成一张干净可用的图。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑