【Matlab】Qt Matlab混合编程——以曲线拟合为例

目录

    • 一、概要
    • 二、环境介绍
    • 三、Matlab C++链接库的生成
      • 1.配置编译器
      • 2.编写函数
      • 3.编译matlab函数,生成C++可调用的链接库
    • 四、Matlab C++链接库的使用
      • 1.Qt工程的建立
      • 2.Qt程序的编写
      • 3.运行测试
    • 五、总结

一、概要

在编写Qt应用时,若想用到比较复杂的算法,如拟合、FFT等,没有现成的C/C++库。而这些在Matlab中都是很容易实现的,那么有没有一种方法可以让Qt“不劳而获”得调用Matlab的算法呢?
其实方法有两种:
1. 对于不同编程语言,完全可以通过【公共内存】的方式实现交互,这类似于进程间通讯。简单来说,可以Qt与Matlab共同读写同一文件,比如Qt将原始数据放入文件,Matlab检测到后对原始数据进行计算,然后将结果放到这个文件中供Qt读取。
2. Matlab的m文件可以编译为Qt可以调用的.lib .dll C++链接库,Qt加载链接库并包含头文件后,可以在C++环境下实现m文件同样的计算效果。这也是本文主要讲解的方式。

二、环境介绍

编译器:MingW64 C++
Qt:Qt5.13.0
Matlab:2018b
首先说明下为什么使用MingW64编译器。安装Qt时要选择安装MingW64,通常为了使应用程序能跨平台运行,一般选择MingW编译器来编译,而不是微软平台的MSVS。同时,Qt和Matlab混编时,若使用MSVS编译器,还需要安装VS2013等,耗费十几G硬盘空间,并且Qt使用MSVS编译器会出现各种各样的问题,不建议使用。
再说下matlab版本,实测过2018b和2020a都可以实现混编,其实只要安装完matlab后,只要在安装目录下的bin\win64\mexopts文件夹中含有mingw64.xml即可。这在较早版本中是不支持的,如2014版本。所以想要混编最好使用2018及以上的版本。

三、Matlab C++链接库的生成

1.配置编译器

安装Qt时选了MingW64之后,在Qt的安装目录下会有mingw730_64文件夹,如

D:\Qt5.13\Tools\mingw730_64

这个文件夹就是mingw64的编译器,我们只需要配置matlab让其找到编译器即可的。
添加编译器目录到环境变量,如下图所示。
【Matlab】Qt Matlab混合编程——以曲线拟合为例_第1张图片
此时在matlab命令行中输入

mbuild -setup

就能看到mingw的编译器了。如果还不行,那么可以手动在matlab命令行中配置环境变量:

setenv('MW_MINGW64_LOC','D:\Qt5.13\Tools\mingw730_64')

之后输入mbuild -setup则会出现mingw编译器,如下所示:

>> mbuild -setup
MBUILD 配置为使用 'MinGW64 Compiler (C)' 以进行 C 语言编译。

要选择不同的语言,请从以下选项中选择一种命令:
 mex -setup C++ -client MBUILD 
 mex -setup FORTRAN -client MBUILD

因为我们要以C++编译,所以要点击下面的mex -setup C++ -client MBUILD ,之后MBUILD就被配置为mingw64 C++的编译器了,如下:

>> mbuild -setup
MBUILD 配置为使用 'MinGW64 Compiler (C)' 以进行 C 语言编译。

要选择不同的语言,请从以下选项中选择一种命令:
 mex -setup C++ -client MBUILD 
 mex -setup FORTRAN -client MBUILD
MBUILD 配置为使用 'MinGW64 Compiler (C++)' 以进行 C++ 语言编译。

之后,在命令行输入

mex -setup

同mbuild,将mex配置为mingw64 C++编译器,如下所示:

>> mex -setup
MEX 配置为使用 'MinGW64 Compiler (C)' 以进行 C 语言编译。
警告: MATLAB C 和 Fortran API 已更改,现可支持
	 包含 2^32-1 个以上元素的 MATLAB 变量。您需要
	 更新代码以利用新的 API。
	 您可以在以下网址找到更多的相关信息:
	 https://www.mathworks.com/help/matlab/matlab_external/upgrading-mex-files-to-use-64-bit-api.html。

要选择不同的语言,请从以下选项中选择一种命令:
 mex -setup C++ 
 mex -setup FORTRAN
MEX 配置为使用 'MinGW64 Compiler (C++)' 以进行 C++ 语言编译。
警告: MATLAB C 和 Fortran API 已更改,现可支持
	 包含 2^32-1 个以上元素的 MATLAB 变量。您需要
	 更新代码以利用新的 API。
	 您可以在以下网址找到更多的相关信息:
	 https://www.mathworks.com/help/matlab/matlab_external/upgrading-mex-files-to-use-64-bit-api.html。

需要注意的是,matlab每次重启后,都要重新按以上步骤进行mbuild -setup/mex -setup的配置。

2.编写函数

以一次多项式拟合为例,编写以下函数:


function [a, b, rsquare] = mat_fit( xData, yData )

    [xData, yData] = prepareCurveData( xData, yData );

    % Set up fittype and options.
    ft = fittype( 'poly1' );

    % Fit model to data.
    [fitresult, gof] = fit( xData, yData, ft );
    
    a = fitresult.p1;
    b = fitresult.p2;
    rsquare = gof.rsquare;

    % Plot fit with data.
    figure( 'Name', 'poly1 fit' );
    h = plot( fitresult, xData, yData );
    legend( h, 'raw point', 'fit line', 'Location', 'NorthEast' );
    % Label axes
    xlabel x
    ylabel y
    grid on

end

一次函数形式为y = a * x + b,函数的输出a,b则是一次多项式中的系数,rsquare为确定系数,越接近于1说明拟合得越准。
函数的输入则为x、y坐标,最后,通过plot打印出拟合的函数以及原始数据。在matlab中运行一下,如

>> [a b r] = mat_fit(1:5,6:10)

结果如下:

>> [a b r] = mat_fit(1:5,6:10)

a =

   1.000000000000000


b =

     5


r =

     1

同时绘图如下:
【Matlab】Qt Matlab混合编程——以曲线拟合为例_第2张图片

3.编译matlab函数,生成C++可调用的链接库

在matlab主界面点击APP,在下拉框中选中Liberty Complier,在TYPE中选择C++链接库,然后点击加号选择函数所在的m文件,之后点击Package等待即可。
【Matlab】Qt Matlab混合编程——以曲线拟合为例_第3张图片
编译完成后,会自动打开编译生成的文件,我们只需要.dll .h .lib这三个文件即可,这三个文件在输出目录的for_redistribution_files_only文件夹中。

四、Matlab C++链接库的使用

至此我们已经生成了.dll .lib链接库,以及库的.h头文件,那么我们在Qt工程中包含头文件,且添加库后,就可以编程序调用刚刚编写的matlab函数了。

1.Qt工程的建立

这里文明建立一个Qt Widgets Application工程,并根据测试需求进行简单的UI界面设计,如下图所示。
【Matlab】Qt Matlab混合编程——以曲线拟合为例_第4张图片
重点是Qt 的pro文件的配置,我们需要在pro文件中加入以下内容:

DEFINES += __MW_STDINT_H__

INCLUDEPATH += $$quote(G:/Matlab2018b/extern/include)
INCLUDEPATH += $$quote(GR:/Matlab2018b/extern/include/win64)

LIBS+=-L$$quote(G:/Matlab2018b/extern/lib/win64/microsoft) -llibmx
LIBS+=-L$$quote(G:/Matlab2018b/extern/lib/win64/microsoft) -llibmx
LIBS+=-L$$quote(G:/Matlab2018b/extern/lib/win64/microsoft) -llibmat
LIBS+=-L$$quote(G:/Matlab2018b/extern/lib/win64/microsoft)  -llibeng
LIBS+=-L$$quote(G:/Matlab2018b/extern/lib/win64/microsoft)  -lmclmcr
LIBS+=-L$$quote(G:/Matlab2018b/extern/lib/win64/microsoft)  -lmclmcrrt

主要是让qt能够找到matlab的头文件,以及链接库,其中的matlab路径按各人的安装路径填写即可,注意路径中的斜杠是斜杠/,而不是windows路径中使用的反斜杠。
然后将上面生成的.lib .h文件复制到Qt工程目录下,之后在Qt中右击工程,选择添加库。【Matlab】Qt Matlab混合编程——以曲线拟合为例_第5张图片
在弹出来的界面中选择【外部库】,点击下一步,之后按如下配置,点击浏览选择库,之后勾掉Linux和Mac,且勾掉为debug版本添加’d’作为后缀。
【Matlab】Qt Matlab混合编程——以曲线拟合为例_第6张图片
接下来,将.h头文件添加到Qt工程中即可。

2.Qt程序的编写

一下为全部代码,即点击【曲线拟合】按钮后调用matlab库对编辑框中的x、y点进行拟合,然后将一次多项式的两个系数输出,同时输出确定系数来反馈拟合效果。当然,必要的防错也要考虑。
主要注意以下几点:
(1).mat_fitInitialize()为库的初始化函数,一般放在程序一开始执行,需要消耗10秒左右的时间;
(2).mwArray为参数类,实例化时需要配置参数。如

mwArray in_x(1,x.size(),mxDOUBLE_CLASS,mxREAL);

这里的1,x.size()配置该变量为一行x.size()列,后边配置为双精度浮点实数。
(3).mat_fit为真正的调库函数,第一个参数为函数的输出变量个数,这要与m文件中的输出个数一致。后边即是输出、输入等,都要与m文件中的函数严格对应。
(4).mwArray类函数的设置、取值等通过阅读代码即可了解。

#include 
#include 
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "mat_fit.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    mat_fitInitialize();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    int i;
    double *arr = nullptr;
    QString res;
    QStringList x = ui->lineEdit->text().split(" ");
    QStringList y = ui->lineEdit_2->text().split(" ");


    if(x.size() != y.size())
    {
        ui->textEdit->append(QString("x y坐标长度不相等"));
    }
    else if(x.size() < 2 || y.size() < 2)
    {
        ui->textEdit->append(QString("请输入最少两个点"));
    }
    else
    {
        mwArray in_x(1,x.size(),mxDOUBLE_CLASS,mxREAL);
        mwArray in_y(1,y.size(),mxDOUBLE_CLASS,mxREAL);
        mwArray out_a(1,1,mxDOUBLE_CLASS,mxREAL);
        mwArray out_b(1,1,mxDOUBLE_CLASS,mxREAL);
        mwArray out_r(1,1,mxDOUBLE_CLASS,mxREAL);

        arr = (double *)malloc(x.size() * sizeof(double));
        if(arr == nullptr)
        {
            ui->textEdit->append("内存申请错误");
            goto exit;
        }

        for(i = 0;i < x.size(); i++)
        {
            arr[i] = QString(x[i]).toDouble();
        }
        in_x.SetData(arr,x.size());
        for(i = 0;i < y.size(); i++)
        {
            arr[i] = QString(y[i]).toDouble();
        }
        in_y.SetData(arr,y.size());

        mat_fit(3,out_a,out_b,out_r,in_x,in_y);

        out_a.GetData(arr,1);
        out_b.GetData(arr + 1,1);
        out_r.GetData(arr + 2,1);

        res = QString("y = %1 * x + %2 rsquare = %3").arg(QString::number(arr[0],'f',6))
                                                        .arg(QString::number(arr[1],'f',6))
                                                        .arg(QString::number(arr[2],'f',6));
        ui->textEdit->append(res);
    }


exit:
    if(arr != nullptr)
    {
        free(arr);
    }
}

3.运行测试

运行后输入x坐标为1-5,y坐标为6-10,得出拟合结果为y = 1.000000 * x + 5.000000 ,确定系数为rsquare = 1.000000,且弹出plot绘图,与预期相符
【Matlab】Qt Matlab混合编程——以曲线拟合为例_第7张图片

五、总结

有了这个方法后,就可以在Qt中轻松得使用matlab支持的大量的复杂算法,比如滤波、快速傅里叶、拟合、矩阵运算等,能够把算法功能强有力得集成在Qt应用中,还是比较实用的。

你可能感兴趣的:(Qt,算法,matlab,c++,qt)