在很多项目中,我们需要在App中看到我们的模型。这就需要我们将我们在一些建模软件(例如solidworks)中建好的模型导入到app的坐标区当中。但是,MATLAB既不能直接打开solidworks文件,也不支持导入.stl文件(笔者了解过一些插件可能可以,但是水平所限,未能实现)。这种情况下,该怎么办呢?
首先,我们要先把模型在MATLAB中显示出来。虽然MATLAB不能打开由SoildWorks导出的模型文件,但是可以打开Urdf文件。那么,我们就可以尝试将模型从SolidWorks导出成Urdf文件。
1.下载Urdf插件
将模型文件转化为Urdf文件并不是SolidWorks原本的功能,而是一个插件。我们需要将该插件下载下来并安装。下载地址为:Releases · ros/solidworks_urdf_exporter · GitHub。点进网址页面,如下图所示:
找到和自己SolidWorks对应的版本,下载“sw2urdfSetup.exe”文件。下载好后,双击安装即可。
2.启用Urdf插件
打开SolidWorks,在“设置”中找到“插件”,点击后,弹出如下界面:
将“其他插件”中的“SW2URDF”前面的勾选框勾中,点击确定。
3.按如下路径依次点击:“工具” → \rightarrow → “File” → \rightarrow → “Export as URDF”,弹出如下界面:
点击红框框出的按钮,选择保存路径,然后点击“Finish”按钮即可:
获得了Urdf文件后,我们就可以在MATLAB中将其打开了。在这里,我使用.m文件和读者说明如何打开Urdf文件并将模型显示在图窗中。此处之所以不使用App,是因为打开Urdf并显示模型的代码并不能在App中运行。但是,后续的解决方案又和打开Urdf文件相关。因此,在这里我先要介绍MATLAB打开Urdf文件。
1.新建.m脚本文件,输入如下代码:
clc; clear; close all;
%% 打开urdf文件
[filename,pathname] = uigetfile('*.*');
robot = importrobot([pathname,filename]);
% 设置数据格式为列向量格式(或者row行向量格式)
robot.DataFormat = 'column';
%% 打开一个窗口显示模型
f = figure(1);
show(robot);
运行效果如下:
2.我们的模型在图窗中显示出来,并且还带有坐标系。那么,如果我们将这个代码移植到App中,放在一个按钮的回调函数中,会发生什么呢?
可以看到,这时候编辑器提醒我们,这样的写法是错误的。那么,我们就需要查询一下官方文档,有没有某种语法可以将rigidtree显示在App的坐标区控件上。
3.在命令行输入如下命令:
help show
然后点击“名为Show的其他函数”,找到“rigidBodyTree/show”,点击打开:
4.在关于show的文档中,我们找到show函数的四个语法:
show(robot)
show(robot,configuration)
show(___,Name,Value)
ax = show(___)
我们发现并没有将模型显示在指定坐标区的语法。但是在MATLAB App的坐标区中,我们又必须指定模型显示在某一个坐标区。使用show函数并不能解决这个问题。那该怎么办呢?解决方案将在1.3节中详细说明。
回到我们的脚本。思考这样一个问题:当我们使用诸如plot或者scatter函数进行绘图时,也都会显示出图窗、坐标区和图像。在plot绘制的图形中,图像本身是Line类型,坐标区是Axes类型,而图窗是figure类型,Line是Axes的子级,Axes又是figure的子级。那么,类比一下,使用show显示的模型,是不是可以使用调用子级的方式,查看它是什么类型呢?如果是类似Line可以使用plot等绘图函数绘制出来的类型,或许我们就可以把绘图数据提取出来,使用绘图函数指定坐标区绘制。这样,也就达到了在指定坐标区显示模型的效果。
1.按照这个思路,我们尝试使用“.Children”调用当前坐标区的子级:
ax = gca;
model = ax.Children;
2.在命令行中输入“model”,调出变量属性。可以发现,model是一个3×1的graphics数组:
这说明,当前坐标区中有三个绘图元素,分别是两个Patch类变量和一个Light类变量。Light类变量是一个光源,为了使模型看起来更有光泽。下图中,模型头部的高光部就是这个光源照在模型头部的效果。
3.那另外两个Patch变量是什么呢?我们在命令行中输入如下代码:
model_1 = model(1)
命令行弹出如下内容:
关于Patch函数的使用,读者可以参考官方文档。简单来说,该函数是用来绘制三维图像,以绘制网格面的方法来绘制。其中,“Faces”和“Vertices”是绘制Patch的数据。但是,model中有两个Patch变量,我们如何得知哪个Patch变量是我们的模型变量呢?而另一个变量又是什么呢?
3.点击“所有属性”,找到“Visible”属性:
在命令行输入如下代码:
model_1.Visible = 'off';
这时候再打开显示模型的图窗:
发现我们的模型不再显示,而坐标区中间留下了一个坐标轴。那么这时候,我们就可以确认,graphics数组的第一个元素为我们的模型变量,而第二个变量为上图中的这个坐标轴。因此,我们新声明两个变量:“F”和“V”,分别表示“Faces”数据和“Vertices”数据:
F = model_1.Faces;
V = model_1.Vertices;
然后,在命令行中使用patch函数,以F和V作为输入绘制面网格:
patch('Faces',F,'Vertices',V);
效果如下:
4.这个时候,坐标区出现了一个纯黑色的模型。这就表示,我们可以利用Vertices和Faces的数据,使用patch函数在指定坐标区绘制模型。那么首先,我们需要把绘图数据保存下来,方便我们的App使用。在脚本中写入如下代码并运行:
%% 将模型绘制数据保存下来
modelData = cell(2,1);
modelData{1} = model_1.Faces;
modelData{2} = model_1.Vertices;
运行后,在“工作区”找到“modelData”变量,右键点击选择保存。保存的.mat文件命名为“salmon”。
5.回忆一下我们在MATLAB Appdesigner开发独立桌面App全流程(一):以打开串口功能为例介绍Appdesigner的基本使用第8节所涉及的按钮回调函数的编写,我们在App的画布上新建一个按钮控件,将其显示名称改为“打开模型”,按钮变量命名为“OpenModelButton”。然后给按钮添加回调函数,在公共属性和回调函数中添加如下代码:
properties (Access = public)
ser; % Serial
serialname;
model; % To store the model data
modelvisual; % Store the patch handle
baud = 115200; % baud rate
datetimer; % To show time
SerialData;
end
% Button pushed function: OpenModelButton
function OpenModelButtonPushed(app, event)
try
cla(app.ModelUIAxes);
[filename,pathname] = uigetfile('salmon.mat');
% 打开模型数据文件
%%% 这个地方建议使用load函数而不是open函数,因为在测试中,封装好的app无法调用open函数
app.model = load([pathname,filename]);
% 显示模型在监视框中
%%% 这个地方要写成 app.model.modelData{2}的形式是因为.mat文件使用load函数读取后是一个struct变量,这种类型的变量需要通过.来调用。
app.modelvisual = patch(app.ModelUIAxes,"Faces",app.model.modelData{2},...
"Vertices",app.model.modelData{1},...
"FaceColor",'interp');
light(app.ModelUIAxes);
set(app.modelvisual,"FaceColor",[0.93,0.42,0]);
set(app.modelvisual,"EdgeColor",[0.93,0.42,0]);
catch ME
report = getReport(ME);
uialert(app.UIFigure,report,'Error Message','Interpreter','html');
end
end
在公共属性中,新建了“model”和“modelvisual”变量,分别用来存储模型的数据和patch句柄。
6.第5步完成后,点击运行,然后点击“打开模型”按钮,选中我们的模型文件后,点击打开。效果如下图所示: