相比java等语言,MATLAB在矩阵计算等方面可太强大了。要是java能直接调MATLAB就好了。
确实可以!机缘巧合之下,发现了这个思路,折腾了好几天。
(有些不足的就是其调用开销比较大,目前还不知道如何合理的调用,)
后面了解到,一个处理方法是,先将matlab转为C,再转为Java,这样可以避免java在每次调用函数时需要与Matlab环境进行通讯导致效率低下的问题。(也许不能算通讯,数据交换?)——2022/03/20
MATLAB 编译
脚本编写
编写一个MATLAB函数脚本,这里我以绘图函数为例。
% 自定义画图函数drawplot.m
% 以两个1*n的矩阵为x,y坐标绘制一段线
% 参数 x y
function drawplot(x, y)
plot(x, y, 'r'); %%使用matlab函数plot()作图
xlabel('X轴')
ylabel('Y轴')
end
另外还有一个drawplot2Line.m
用于输出两条线(红+蓝)
打包
-
在MATLAB命令行窗口输入
deploytool
,打开部署工具
-
选择
Library Compiler
-
添加脚本并配置
- 选择导出类型(我选择Java Package)
- 选择要转换的脚本文件(可导入多个)
- 导出的包名
- 在jar包中要生成的类以及类内方法(类名可自定义)
- 是否一起打包MATLAB运行环境MCR(默认不打包,自行从网上下载)
- 打包
-
等待打包完成
-
生成的共有三个文件夹,其中包含导出的jar包和javadoc文档
- for_redistribution
- for_redistribution_files_only
- for_testing
(三者有什么区别我也没有理解透,只要知道取出其中的包导入到java工程中就好)
Java调用
需要将上述方法导出的jar包以及MATLAB安装目录下的\toolbox\javabuilder\jar\javabuilder.jar
包一同导入到Java工程中。Java导入本地jar包我这里就不讲了。
对于矩阵数据,需要使用javabuilder.jar
中提供的MWNumericArray
进行传参。
输入
传递给MATLAB函数时,基本类型可以直接带入,矩阵需要使用MWNumericArray
常规的矩阵输入方式:
MWNumericArray input = null; // 用于保存输入矩阵
//方式一:
input = MWNumbericArray.newInstance(int, int[], MWClassID.DOUBLE, MWComplexity.REAL);
//参数1表示矩阵的维数,参数2表示各维的大小即几行几列,参数三表示数据类型,参数四表示实数或复数
//方式二:
double array[] = { 3, 2, 4, 5 };//要计算的数
//int array[]={3,2,4,5};也是可以的。
input = new MWNumericArray(array, MWClassID.DOUBLE);
result = c1.myFunction(1, intput);
调用
Class1 c1=null;
try{
//输入参数配置
...
c1 = new Class1();
c1.draw.plot(x,y); //无返回
output = c1.myFunction(1, intput); //有返回(返回一个参数Object[1])
output2 = c1.myFunction2(3, intput); //有返回(返回三个参数Object[3])
//处理返回值
...
}catch(Exception e){
System.out.println(e);
}finally{
//释放资源
...
}
对于导出的函数,其结构有两种:
- 对于无数据输出的,为
function(Onject... obj)
,直接输入参数即可; - 对于有数据输出的,为
function(int i, Object... obj)
,i
表示返回的参数个数,Object... obj
为对应MATLAB函数所输入的参数。
输出
对于有数据返回的函数,返回值为Objecct[]
数组。
处理方式:
MWNumericArray output = null; //用于保存输出矩阵
output=(MWNumericArray) result[0]; //将结果Object[]转换成MWNumericArray
double res = output.getDouble(1); //从MWNumericArray对象中读取数据
其他的读取数据方式见【4. MWNumericArray方法】
后期处理
释放本地资源
MWArray.disposeArray(input); //建立的矩阵用这句
WArray.disposeArray(output); // 建立的矩阵用这句
MWArray.disposeArray(result); //建立的Object数组用这句
c1.dispose(); //建立的matlab类对象用这句
//以上释放时,必须保证其不为null。
MWNumericArray
方法
getDouble()
:以double格式从MWNumericArray
获取矩阵第一个数,常用于只有一个数的矩阵getDouble(int i)
:以double格式从MWNumericArray
获取矩阵第i个数getDouble(int[] i)
:以double的格式从MWNumericArray
获取矩阵中的i[0]
行,i[1]
列的数(多维则i有多个元素)getDoubleData()
:以double[]
一维数组的格式从MWNumericArray
获取矩阵(按列连接)toDoubleArray()
:将矩阵以Object
返回,实为多维double
数组返回,需要强制转换成为多维数组。set(int[] i, double)
:对矩阵的i[0]
行i[1]
列进行赋值
其他如getByte
, getInt
, getShort
, getLong
, getFloat
也是同样的用法。
示例
这里我以调用drawplot2Line()作为例子。
MWNumericArray x = null;
MWNumericArray y = null;
Plotter plotter = null;
try {
int[] dims = {1, 120001};
x = MWNumericArray.newInstance(dims, MWClassID.DOUBLE);
y = MWNumericArray.newInstance(dims, MWClassID.DOUBLE);
for (int i = 1; i <= 120001; i++) {
double t=0.001*i-6;
x.set(i, 16*Math.pow(Math.sin(t),3)
y.set(i, 13*Math.cos(t)-5*Math.cos(
}
plotter = new Plotter();
plotter.drawplot( x, y);
plotter.waitForFigures();
} catch (Exception e) {
System.out.println(e);
} finally {
// 释放本地资源
MWArray.disposeArray(x);
MWArray.disposeArray(y);
if (plotter != null) {
plotter.dispose();
System.out.println("over");
}
}
这里我是用了比较简单的函数来画的爱心
x=16*(sin(t))^3
与y=13*cos(t)-5*cos(2*t)-2*cos(3*t)-cos(4*t)
,其中-6<=t<=6
当然也可以采用其他的图形。
环境部署
一般而言,在运行环境下是没有MATLAB的。
没有MATLAB环境下调用jar包,会由于MWNumericArray
这些类出现NoClassDefFoundError
问题。对这一问题在【怎么解决java.lang.NoClassDefFoundError错误】中有很好的解释
但MATLAB又太大,10+G起步,更好的办法就是安装MATLAB Compiler Runtime (MCR) ,虽说对于我的R2018a(9.4)版本的MCR也有1G+大了。
安装的MCR需要与编译时使用的MATLAB版本一致,我自己使用的是R2018(9.4)
Windows
下载后双击安装即可
Ubuntu
下载
wget https://ssd.mathworks.com/supportfiles/downloads/R2018a/deployment_files/R2018a/installers/glnxa64/MCR_R2018a_glnxa64_installer.zip
解压
unzip MCR_R2018a_glnxa64_installer.zip
安装
默认在 /usr/local/MATLAB/MATLAB_Runtime/v94
注意这里最后的版本号要视自己情况
# 普通安装
sudo -H ./install
# 静默安装(无UI的Ubuntu下)
sudo ./install -mode silent -agreeToLicense yes
# 指定目录
sudo ./install -mode silent -agreeToLicense yes -destinationFolder /data/tomcat/MCR
环境配置
虽说按照官方的走了一遍,但并没有成功,后面尝试了写到
/etc/profile
中也还是不行,待有空了继续捣鼓吧。 比较毕竟ubuntu只是尝试着用一用,主力还是在windows上,而且Windows安装MCR会自动配置好环境。
-
查看当前的SHELL环境
echo $SHELL
-
根据不同的SHELL环境选择不同的配置方法(我的环境为
/bin/bash
) -
保存当前的库路径为
mypath
变量,并输出mypath=$LD_LIBRARY_PATH && echo $mypath
-
附加MATLAB环境到
mypath
中。mypath="${mypath:+${mypath}:} <MATLAB_RUNTIME_INSTALL_DIR>/runtime/glnxa64: <MATLAB_RUNTIME_INSTALL_DIR>/bin/glnxa64: <MATLAB_RUNTIME_INSTALL_DIR>/sys/os/glnxa64: <MATLAB_RUNTIME_INSTALL_DIR>/extern/bin/glnxa64"
以上命令需要在一行中输入,如:
mypath="${mypath:+${mypath}:}/usr/local/MATLAB/MATLAB_Runtime/v94/runtime/glnxa64:/usr/local/MATLAB/MATLAB_Runtime/v94/bin/glnxa64:/usr/local/MATLAB/MATLAB_Runtime/v94/sys/os/glnxa64:/usr/local/MATLAB/MATLAB_Runtime/v94/extern/bin/glnxa64"
-
再次打印输出查看
mypath
echo $mypath
-
在当前会话中将
mypath
赋给LD_LIBRARY_PATH
注意!永久修改会影响原系统对 lib 动态链接库的调用,比如说后来我在使用vim时会因为
libexpat.so.1
被定位到MATLAB中的,导致原来的vim无法使用(我印象里似乎我的初始LD_LIBRARY_PATH
是为空?按理说我写的mypath是基于LD_LIBRARY_PATH附加的,但后来看只有新加的MATLAB相关的了)从网上找了一份原版的
export LD_LIBRARY_PATH=/lib:$HOME/lib:/lib/x86_64-linux-gnu:/usr/lib echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> ~/.bashrc
export LD_LIBRARY_PATH=$mypath
-
如永久设置则需要执行以下命令,写到
~/.bashrc
中echo "export LD_LIBRARY_PATH=$mypath" >> ~/.bashrc
-
应用更改
source ~/.bashrc
-
检查GNU版本
ldd --version
如果是2.17及以下,则还需要按上述步骤将<MATLAB_RUNTIME_INSTALL_DIR>/bin/glnxa64/glibc-2.17_shim.so
添加到LD_PRELOAD
环境变量中。
卸载
rm -rf <MATLAB_RUNTIME_INSTALL_DIR>