0.需求
做了一个优化算法可视化系统,但优化的过程需要使用matlab来计算,因此需要在项目里引入matlab封装好的jar包,并在java开发中调用matlab方法,指定输入参数,转换输出类型。
1.matlab封装jar包
这篇文章讲的很详细:Matlab代码打包成jar包供java调用_marleb算法 打成jar与java交互怎么使用-CSDN博客
打包后在for_redistribution_files_only文件中获取jar包。
【报错与解决】打包过程有出现过一个小报错,根据错误日志发现是编码问题,最简单粗暴的解决方法就是删掉代码中的中文注释。
2.项目引入jar包
这里讲的很详细,推荐通过Maven来引入:
IDEA、Maven引入外部jar包-CSDN博客
3.调用方法与类型转换
3.1 调用方法
可以在matlab导出的文件夹里找到接口文档
我封装时定义的包名是optbymatlab,类名是OptWork,顺着点进去,就能找到类方法。
方法名和导入的matlab程序是一样的,主要关注原matlab函数对应的入口函数的参数与返回值。以下示例为我封装的matlab函数原程序和接口文档里的类方法介绍。原程序有4个输入参数和2个输出,接口方法的详情部分里也写了需要4个输入参数,和原程序是对应的,且输出的java类型为Object[]。
需要另外注意的是形参列表里第一个参数【int类型的nargout】,这里的意思是设定输出的数量,也就是要得到几个输出。因此整体的输入是【一个int类型 + 4个输入参数】,输出为Object[],且这个数组的大小就是输入的int。
3.1 输入
那么就可以开始写程序了。因为我这里matlab设置的输入类型是整数和字符串,int型数据和String类型的数据可以直接带入,所以就没有进行输入类型的转换。
如果是其他类型,可以查一下官方文档:处理从 Java 方法返回的数据 - MATLAB & Simulink - MathWorks 中国
注意,输入的参数一定是【int类型的输出数量设置 + matlab程序设置的输入参数】。即,如果只想要第一个输出,就可以设置为1。
import optbymatlab.OptWork;
OptWork optWork = new OptWork();
//希望获取2个输出,输入四个参数
Object[] outputs = optWork.demo_(2,30,30,"model1","alg1");
System.out.println("完成计算");
3.2 输出
获取到一个Object数组之后,通过debug查看数组内元素的运行类型。我的matlab程序输出的分别是一个值(或者说,一个矩阵),和一个元胞数组。对应的类型就是矩阵类型-MWNumericArray、元胞数组类型-MWCellArray。需要对这些类型进行转换,然后提取里面的数值
【如果找不到类】这些MW开头类型就在导入matlab封装的jar包时要一块儿导入的那个javabuilder包里,所以如果找不到类可能是因为没导入javabuilder。
3.2.1 MWNumericArray类型【矩阵】
一般用的比较多的都是矩阵,即MWNumericArray类型。整理常用的方法:
①getDouble():如果矩阵只有一个数,用该方法提出来;
②getDoubleData():如果是一个多维数组,提出一个一维数组【按列提取】
更多方法可以参考官方文档:在 MATLAB 中管理数值数组的 Java 类 - MATLAB - MathWorks 中国
只有一个数的示例:
import com.mathworks.toolbox.javabuilder.*;
double acc = ((MWNumericArray) outputs[0]).getDouble();
3.2.2 MWCellArray类型【元胞数组】
元胞数组也是matlab的一个常用类型,我在matlab的程序中,所输出元胞数组内的元素是个结构体,结构体内包含三个字段,每个字段是一个矩阵,大家可以做个参考。
首先转换成MWNumericArray类型,然后找方法。整理比较有用的方法是这些:
① numberOfElements() :返回元胞数组的长度,即元素数量
② get(int i):返回第i个元素(索引从1开始)
官方文档:用于管理 MATLAB 元胞数组的 Java 类 - MATLAB - MathWorks 中国
设置循环,对每个结构体进行提取。这里的提取过程不太美观,甚至有点粗暴,我本意是打算再使用结构体的类型MWStructArray再次提取,但是debug时发现粗暴一点好像更直接,还不用转来转去了。能解决问题的都是好方法!
大概的提取过程是这样的:通过get(int i)方法得到这个结构体的Object类型,debug时发现运行类型为一个三维数组,且第一维对应结构体的三个字段,第三维对应字段里的数据且类型是double[][],于是转成三维Object[][][]然后提取第三维的数据,并将第三维的数据转换成double[][],就得到数据了。
因为我只需要第二个字段的数据,所以以下示例为只获取元胞数组中结构体的第二个字段【代码的可参考性不强,但是上面的获取思路可以借鉴】
import optbymatlab.OptWork;
import com.mathworks.toolbox.javabuilder.*;
OptWork optWork = new OptWork();
Object[] outputs = optWork.demo_(2,N,G,sModel,alg);
//矩阵类型
acc = ((MWNumericArray) outputs[0]).getDouble();
//元胞数组
MWCellArray cellArray = (MWCellArray)outputs[1];
int K = cellArray.numberOfElements();
for (int k = 1; k <= K; k++) {
//每个元素是一个结构体
Object o = cellArray.get(k);
Object[][][] cellData = (Object[][][])o;
double[][] R = (double[][]) cellData[1][0][0];
int n = R.length;
List<DataPoint3> xyz = new ArrayList<>();
for (int i = 0; i < n; i++) {
double x = R[i][0];
double y = R[i][1];
double z = R[i][2];
xyz.add(new DataPoint3(x, y, z));
}
xyzBatches.add(xyz);
}
3.2.2 MWStructArray类型【结构体】
虽然我没用到结构体,但如果有结构体类型的需要,可以查看官方文档:
用于管理 MATLAB 结构体数组的 Java 类 - MATLAB - MathWorks 中国