最近在做一个小型项目,需要在c++中调用simulink,于是对这一开发技术进行了调研。下面是三种实现方式,理论上来说,三种方法都是可以实现预期的效果,但是开发难度和使用难度等各不相同。
使用mcc命令,把m文件编译转为h,lib,dll文件,(暂时还没有调通,猜测是因为路径配置的原因)
编写一个仿真文件,只有一个正弦信号发送器和一个scope。
两个m文件,分别是
simulinkstart.m 用于设置仿真参数,并启动仿真
load_system('testcpp');
set_param('testcpp','SolverType','Fixed-step');
set_param('testcpp','Solver','FixedStepDiscrete');
set_param('testcpp','FixedStep','0.1');
set_param('testcpp','SimulationCommand','start');
set_param('testcpp','SimulationCommand','pause');
set_param('testcpp','SimulationCommand','step');
% set_param('testcpp','SimulationCommand','stop');
pause(0.1);
simulinkstep.m 用于控制仿真步进一步,并把结果输出并返回
function ret = simulinkstep()
set_param('testcpp','SimulationCommand','step');
simulationdata = get_param('testcpp/Scope','RuntimeObject');
ret = simulationdata.InputPort(1).Data(1);
end
这两个m文件在matlab中测试没有问题。
把它们使用如下命令进行编译
mcc -W cpplib:libsimulinkstart -T link:lib simulinkstart.m
mcc -W cpplib:libsimulinkstep -T link:lib simulinkstep.m
分别得到h,lib,dll文件。
在vs工程中加入h和lib,并把dll放到等会编译出的可执行文件目录或者把其路径加入环境变量
具体main函数代码
#include
#include "libsimulinkstart.h"
#include "libsimulinkstep.h"
using namespace std;
int main()
{
if (!libsimulinkstartInitialize())
{
return -1;
}
if (!libsimulinkstepInitialize())
{
return -1;
}
cout << "初始化成功" << endl;
simulinkstart();
cout << "仿真启动成功" << endl;
while (1)
{
mwArray ret(1, 1, mxDOUBLE_CLASS);
simulinkstep(1,ret);
double retc = ret.Get(1, 1);
cout << retc << endl;
Sleep(100);
}
libsimulinkstartTerminate();
libsimulinkstepTerminate();
return 0;
}
在这个main函数中,首先对这两个模块进行初始化,然后启动仿真,再用while循环获取每一步仿真后的值。编译完成后在命令行中运行(否则结果一闪而过)。
目前会报错误:
未定义函数或变量 load_system
但是使用如下一个很简单的函数就可以
在matlab端
function c = simpleadd(a,b)
c = a+b;
end
在c++端
if (!libAddInitialize())
return -1;
int a = 10;
int b = 20;
int c;
mwArray mwA(1, 1, mxINT32_CLASS);
mwArray mwB(1, 1, mxINT32_CLASS);
mwArray mwC(1, 1, mxINT32_CLASS);
mwA.SetData(&a, 1);
mwB.SetData(&b, 1);
simpleadd(1, mwC, mwA, mwB);
c = mwC.Get(1, 1);
cout << c << endl;
libAddTerminate();
经查,这是一个simulink工具箱用到的函数,未定义应该是路径没有设置好。不可能matlab核心的函数可以调用,而simulink的函数不能调用。
使用mex命令,把cpp文件编译为exe。
在cpp文件中,使用matlab提供的c++ API 控制仿真。然后在自己的c++程序中调用生成的exe,在qt中可以方便的使用QProcess实现这一功能。
#include "MatlabDataArray.hpp"
#include "MatlabEngine.hpp"
#include
void callSQRT() {
using namespace matlab::engine;
// Start MATLAB engine synchronously
std::unique_ptr matlabPtr = startMATLAB();
// Load Simulink model
FutureResult loadFuture = matlabPtr->evalAsync(u"load_system('testcpp')");
std::cout << "Loading Simulink model... " << std::endl;
std::future_status loadStatus;
do {
loadStatus = loadFuture.wait_for(std::chrono::seconds(1));
} while (loadStatus != std::future_status::ready);
std::cout << "Simulink model loaded\n";
}
int main() {
callSQRT();
return 0;
}
使用
mex -v -client engine testFeval.cpp
命令进行编译,得到testFeva.exe。
经验证testFeva.exe可以正常加载testcpp仿真。
这种方法最简单,详细过程就不再介绍。但这个方法有一个缺点,在使用时必须打开matlab。
使用方法一,在使用时不需要打开matlab,并且操作最为简单,只需要在matlab中建立仿真模型即可,在c++程序中可以直接加载仿真模型,并读取仿真过程中的变量值。可以做大量的仿真模型,只要输出输出接口一致,都能加载。所以方便做成插件的方式,在c++程序中加载不用的仿真模型,实现定制化的效果。
使用方法二,在使用时不需要打开matlab,但是操作繁琐,对于每一个仿真模型,都需要单独的cpp文件,在matlab中使用mex命令编译为exe文件。并且在自己的程序中调用exe,两个程序间的通信也是比较复杂的问题。
通信问题使用qt的qprocess可以解决,但是由于要对每一个仿真模型进行cpp文件的修改和编译,所以不具有扩展性,所以不再这一方法下继续往下做。
使用方法三,在使用时需要打开matlab并且运行仿真。只要在c++程序中写好接收和发送udp消息的逻辑,就能实现c++代码和simulink进行通信,操作也比较简单。