分享

Matlab Coder的使用

 imelee 2017-09-20

    Matlab Coder是一个将matlab代码转化为C/C++代码或dll、lib的工具,得到的代码或程序独立于Matlab,可以在其他工程中随意使用。这样,如果碰到一些数学问题,比如矩阵运算、算法验证等,可以先用Matlab实现,然后方便地转化为源代码在各种应用中使用。

    下面来介绍Coder的使用方法,内容参考自Matlab帮助文档。
    Coder的使用主要基于3个组件:Matlab、Coder工具箱和C编译器。一般安装好Matlab后,前两个是默认提供的(执行ver命令可快速查看Coder工具箱是否安装);C编译器默认提供,但可根据自己的情况更换其他C编译器(Matlab推荐使用其他编译器,自带的性能不佳),具体可执行mex -setup进行编译器配置,如
>> mex -setup
MEX configured to use 'Microsoft Visual C++ 2013 Professional (C)' for C language compilation.
Warning: The MATLAB C and Fortran API has changed to support MATLAB
     variables with more than 2^32-1 elements. In the near future
     you will be required to update your code to utilize the
     new API. You can find more information about this at:
     http://www./help/matlab/matlab_external/upgrading-mex-files-to-use-64-bit-api.html.
To choose a different language, select one from the following:
mex -setup C++
mex -setup FORTRAN
    完成配置后,开始转换Matlab函数代码,例如下面函数,它实现了搜索banana函数的最小值位置,并计算矩阵A的行列式:TestCoderFcn.m
% Test for Coder
function [x, Adet] = TestCoderFcn(x0, p0, A)%#codegen
% search minimum
global p
p = p0;
x = fminsearch(@myfun, x0);
% calculate det
Adet = det(A);
end
% banana function
function f = myfun(x)
global p
f = 100*(x(2)-x(1)^2)^2+(p-x(1))^2;
end
    使用代码main.m做效果测试:
% 测试
clc
clear
close all
x0 = [1,3];
p0 = 2;
A = [1 -2 4; -5 2 0; 1 0 3]; % det(A) = -32
[x, Adet] = TestCoderFcn(x0, p0, A);
fprintf('函数的极值出现在(%f, %f)\n', x)
fprintf('矩阵的行列式为%f\n', Adet)
    测试结果为:
函数的极值出现在(2.000005, 4.000021)
矩阵的行列式为-32.000000
    在转化前,需要对代码进行3种检查:代码语法检查、代码可转换检查和运行时检查。
  • 代码语法检查,就是在代码编写时不能出现语法错误,否则,Matlab编辑器会在右边显示warning或error,如下图,后边的红色提示表示当前行发现语法错误,修正后变为绿色,表示无语法错误。
  • 除此之外,还可以使用Code Analyzer Report进行语法检查:
  • 代码可转换检查,就是检查代码是否可转化为C代码,因为Matlab很多函数还不提供转化功能,检查方法一种是通过Coder GUI检查,另一种方法是在代码中加注释%#codegen,有此注释,编辑器会认为此文件要做Coder转化,进而检查可转化性。
  • Matlab Coder可转化的语法、函数、对象等在帮助中有详细陈列,在编写时需要时常查看使用的函数是否支持转化:
  • 运行时检查,就是Coder会先把函数转成Mex函数并执行,来检查运行时是否存在问题,如果存在问题,Coder会给出问题描述,依此进行修改。具体实现见后面说明。
    代码通过语法检查和可转化检查后,开始进行转化。输入coder打开MATLAB Coder,或通过工具栏打开:
    选择需要转化的文件TestCoderFcn.m,Coder会在当前文件夹生成一个prj工程文件,便于工程设置保存:
    点击Next,选择main.m文件,并点击Autodefine Input Types运行该文件,Coder会跟踪运行过程得到函数输入输出变量的类型和大小:
这里x0是1x2的向量,p0是常数,A为3x3的矩阵,但实际上A的维数可以更高,所以进行如下调节
    另外,由于使用了全局变量,所以Does this code use global variables?选择Yes,并设置相应变量名和类型:
    点击Next,执行Chech for issues进行运行时检查
    如果运行成功,会提示代码没问题,否则需要根据报错进行修改
    点击Next,根据需要选择相应选项产生所需代码或库文件,这里选择产生源代码,点击Generate按钮开始生成:
    最后Coder会生成相应的源代码:
    这些源代码保存在codegen\lib\TestCoderFcn文件夹下,可以看到Coder生成的文件很多,这也是一个不太方便的地方,如果需要代码复用,就要把所有源文件拷过去,增加了工程目录的复杂性:
    另外,Coder提供源代码打包功能,点击结束界面右上角的PACKAGE可将必要源代码压缩打包,分享使用。
    对于源代码的使用,可以参考codegen\lib\TestCoderFcn\examples文件夹下main.h和.c文件。
    下面利用这两个文件测试生成源代码的运行效果。首先把这两个文件拷贝到其他文件夹,因为如果直接修改这两个文件,再次运行Coder产生代码时,会提示这两个文件发生改变,影响代码生成。
    打开main.c文件,可以看到其中的main函数代码如下:
int main(int argc, const char * const argv[])
{
  (void)argc;
  (void)argv;
  /* Initialize the application.
     You do not need to do this more than one time. */
  TestCoderFcn_initialize();
  /* Invoke the entry-point functions.
     You can call entry-point functions multiple times. */
  main_TestCoderFcn();
  /* Terminate the application.
     You do not need to do this more than one time. */
  TestCoderFcn_terminate();
  return 0;
}
    前两行代码无用,先将其注释掉:
//(void)argc;
//(void)argv;
    剩下就是3个函数,第一个函数用来初始化,第二个完成主要功能,第三个结束应用。第二个函数其实很简单,就是初始化变量,传入核心函数TestCoderFcn执行得到结果:
static void main_TestCoderFcn(void)
{
  double x0[2];
  double p0;
  emxArray_real_T *A;
  double Adet;
  double x[2];
  /* Initialize function 'TestCoderFcn' input arguments. */
  /* Initialize function input argument 'x0'. */
  argInit_1x2_real_T(x0);
  p0 = argInit_real_T();
  /* Initialize function input argument 'A'. */
  A = c_argInit_UnboundedxUnbounded_r();
  /* Call the entry-point 'TestCoderFcn'. */
  TestCoderFcn(x0, p0, A, x, &Adet);
  emxDestroyArray_real_T(A);
}
    所以关键需要修改的就是3个初始化:
argInit_1x2_real_T(x0);
p0 = argInit_real_T();
A = c_argInit_UnboundedxUnbounded_r();
    其中p0的初始化可以直接修改:
//p0 = argInit_real_T();
p0 = 2;
    其他两个函数修改为
static void argInit_1x2_real_T(double result[2])
{
  //int b_j1;
  /* Loop over the array to initialize each element. */
  //for (b_j1 = 0; b_j1 < 2; b_j1++) {
    /* Set the value of the array element.
       Change this value to the value that the application requires. */
    //result[b_j1] = argInit_real_T();
  //}
  result[0] = 1;
  result[1] = 3;
}
static emxArray_real_T *c_argInit_UnboundedxUnbounded_r(void)
{
  emxArray_real_T *result;
  //static int iv0[2] = { 2, 2 };
  static int iv0[2] = { 3, 3 };
  int b_j0;
  int b_j1;
  /* Set the size of the array.
     Change this size to the value that the application requires. */
  result = emxCreateND_real_T(2, iv0);
  /* Loop over the array to initialize each element. */
  double A[] = {1,-2,4,-5,2,0,1,0,3};
  for (b_j0 = 0; b_j0 < result->size[0U]; b_j0++) {
    for (b_j1 = 0; b_j1 < result->size[1U]; b_j1++) {
      /* Set the value of the array element.
         Change this value to the value that the application requires. */
      result->data[b_j0 + result->size[0] * b_j1] = A[b_j0 + result->size[0] * b_j1];
    }
  }
  return result;
}
    为了展示拟合结果,在TestCoderFcn函数执行后,加一句结果输出:
TestCoderFcn(x0, p0, A, x, &Adet);   
printf("Fitting Result: %f, %f\n", x[0], x[1]);
printf("Det pf A: %f\n", Adet);
    如此修改后,在Coder中选择生成exe文件,并在More Settings->Custom Code中设置Additional include directories为main.c所在目录,Additional source files为main.c:
    设置好后,点击Generate生成可执行文件TestCoderFcn.exe,并在Matlab中执行如下语句查看结果:
!TestCoderFcn.exe
system('TestCoderFcn.exe');
    结果为
>> !TestCoderFcn.exe
Fitting Result: 2.000005, 4.000021
Det pf A: -32.000000
>> system('TestCoderFcn.exe');
Fitting Result: 2.000005, 4.000021
Det pf A: -32.000000
    可以看到运行结果与Matlab结果是一致的。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多