MATLAB程序转C代码的方法研究
2018-01-15周世钦王波涛
周世钦+王波涛
摘要:通过MATLAB程序转C代码的研究与应用可以加快软件、算法从研究到实际应用的进程,提高软件、算法研发的效率。对于转C的流程、方法、注意事项以及限制等问题需要深入研究。以MATLAB2014的转C工具MATLAB Coder为基础,分析了MATLAB转C代码的细节,完整地研究了MATLAB代码转成C代码的整体流程。通过合理的转C规范,MATLAB支持常规算法和大多数工具库函数转C,保持原功能、逻辑不便。使用动态预分配内存,外置接口,简化函数等方法,使得一部分非常规算法、不支持转C或限制性MATLAB函数能够正确转化为C语言代码。
Abstract: The research and application that MATLAB program transforming to C code can expedite the progress from algorithm or software research to practical application and improve the efficiency of software or algorithm development. These problems about transformation's processes, methods, points for attention, limitation and so on need to be further researched. Based on MATLAB Coder of Matlab2014 tool for transforming to C, it analyses details of MATLAB transforming to C code and its whole process. With reasonable transformation specification, MATLAB support general algorithm and most tool functions to transform to C code, which retain intrinsic function and logic. Using some methods such as dynamically pre-allocating memory, external interface and simplifying functions, it also makes a part of uncommon algorithm and MATLAB tool functions which are not support to transform to C or have transformation limitation possible to successfully transform to C language code.
关键词:Matlab转C;Coder;代码规范;转C流程;转C限制
Key words: Matlab transformingto C;Coder;code specification;transformation process;transformation limitation
中图分类号:TP311 文献标识码:A 文章编号:1006-4311(2018)02-0182-04
0 引言
目前,利用Matlab软件[1]作为工具来进行算法设计、验证已经成为研究者们首选的手段之一[2],而通过Matlab转C,可以直接将Matlab函数转换成高效优化的C程序,移植到嵌入式环境中去[3]。
Matlab Coder工具是MathWorks公司推出的Matlab2014a版本中一个重要的产品[4],它可以将Matlab 函数直接生成C代码[5]。借助Matlab Coder,软件工程师不再需要将设计的算法进行C语言重新编程,而是利用Matlab软件中强大的函数库[6],按照通用的转C流程,参照转C规范,通过建立Matlab函数,并设置目标语言所需要的参数,生成能够脱离Matlab环境独立运行的C代码,这样就大大减轻了软件工程师的编码工作[7]。
对于基于Matlab2014a版本Coder的Matlab转C,本文介绍经过实践的、行之有效的转C方案与流程,并将转C中遇到的问题与解决方法、思路在下文中进行介绍,以助于更好的实现转C工作。文章接下来的一节将详细介绍Matlab程序整体转C代码流程。对于转C过程中的几个常遇问题及其处理方法,将在随后的一节中罗列出来并给出详细说明。实际转C过程中,可能会存在障碍和一些限制,对此,创新性地通过动态预分配内存,外置接口,简化函数等方法化解,在文章的最后一节将会对此进行详细说明。
运用matlab转C方法,可以方便、快捷、准备地将matlab算法转化成C语音实现,使之运用在嵌入式环境成为可能。通过本文的详细介绍,能够使读者对于使用matlab coder工具进行matlab转C有更加直观、系统的认识;引导读者正确使用2014a版本的matlab coder进行matlab转C工作;为从matlab算法设计、仿真、验证到算法的C语言实现、嵌入式环境应用提供了一条切实可行的途径;同时,文章中也着重列出了多项技术要点,是转C工作顺利进行和是否成功的关键所在,对实际matlab转C工作有着重要的借鉴意义。但也应当了解到, Matlab转C依然存在着无法解决的情况,这些情况并不在本文讨论范围之内,故应尽量避开之,或者,隨着Matlab Coder的升级,某些问题可随之化解。
1 Matlab程序整体转C代码流程
借助Matlab Coder转C有其一定的步骤与要求规范,Matlab2014a版本Matlab Coder的转C流程如图1所示,以及其重要的几步工作如下所述。简单的Matlab函数经过简单的调整,经过如下步骤和要求就能成功转C,而面对复杂的情况时,除了要按照上述流程严格执行外,还要根据具体情况做出相应的修改才能达到某些步骤的要求。endprint
1.1 封装Matlab函数
将Matlab程序所要实现的功能封装成一个Matlab函数文件,并将其所实现的功能结果作为函数输出,将所需的数据作为输入,再将整个函数的代码进行规范,以此作为转C函数。对于Matlab程序,脚本文件[8]是无法通过Coder 工具转成C代码的,只有函数文件能通过Coder工具转成C。
转C前要对函数代码进行转C规范。大部分不符合规范的代码都可以通过代码分析器检测出来(编译文本框的左侧有对应的提示),可通过此依次做出相应的修改,以达到转C要求。小部分是代码分析器检测不到的,需要自己发现或者转C编译报错时才能从错误报告中找到,并对其进行规范。完成规范之后才能通过Matlab Coder进行转C。
1.2 函数整体分析
利用Code Generation Readiness Tool整体分析函数是否符合转C要求。此步骤主要检查转C函数中是否调用了不支持转C的Matlab函数或Matlab对象[9](如:数据类型),Matlab编程[10]是否正确,是否存在嵌套调用等不符合转C要求规范的算法,但是,不检查语法细节是否符合转C要求规范。
对于检测出的、不符合转C要求的,只能对Matlab程序的算法进行修改,去除不符合要求的部分,甚至要对部分算法的功能进行调整、取舍。
1.3 库函数转C限制检查
确定转C函数中调用的Matlab库函数是否存在转C限制。可以查看Matlab帮助文档中的转C函数支持列表,对照的查看是否存在轉C限制的Matlab工具函数。若调用的Matlab函数存在转C限制,并且调用时没有遵循这个限制,则无法转C。
1.4 建立转C工程并运行
创建转C工程,加载需要转C的功能函数,设定输入变量的数据类型及其大小,选择转C后代码的输出形式,点击“编译”或“Run”即可开始执行转C。创建转C工程时,创建的转C工程和需要转C的功能函数都应存放在当前Matlab工作路径下。
1.5 转C错误检查与修改
转C出现错误,点击“Open error report”或“View Report”打开错误报告,找到首条错误报告并查看错误信息和出错代码位置,根据相关信息提示修改或规范源代码,使得其在不改变源代码功能逻辑的情况下符合转C 规范,保存后再次进行转C编译。以此重复类推来逐个消除转C错误,直到最终成功完成转C编译。
1.6 转C成功
成功完成转C。点击 “View Report”打开转C结果报告,可以查看到数据类型等的转C相关信息,也罗列了转C的输出文件。在转C工程路径下会有个codegen文件夹,里面根据文件输出类型和转C工程名新建文件夹,存放着相关输出的文件,其中.c和.h文件为输出的C代码文件,与Matlab源代码同名文件为其主调函数文件。
1.7 代码验证
对于转出的C代码正确与否,coder提供的方法是生成mex文件用于验证,但是mex验证不成功时,又无法通过其分析原因所在。对此,不再用mex函数验证,直接以代码验证的方式进行。其主要解决的问题是,代码验证不通过时,可以同步分析C程序和Matlab程序以找出转C失败原因。
新建一个C语言工程用于调用转C后的功能函数C语言代码,产生结果并和用Matlab运算产生的结果进行比较,如果结果一样或者误差在合理范围内,则说明转C成功,转出的C代码功能完整,逻辑正确,可直接使用。如果结果不一致、误差极大,则说明转C出现问题,可单步运行C程序和Matlab程序,看误差最先出现在那里,并分析是什么原因造成的,修改或规范它,然后再进行转C编译和代码验证,直到二者结果一致或者误差在合理范围内为止。代码验证时,C程序和Matlab程序用的输入应当一致,并用多组数据进行验证。如果有必要,也可以将程序运行中的、有代表性的对应数据进行比较,以此确保转出的C程序功能、逻辑与源代码一致。
2 转C过程常遇问题及处理方法
转C过程中会遇到诸如不符合Matlab Coder转C规范的语句、对程序进行转C规范时,不符合程序功能逻辑的修改、Matlab和C语言的编程差异[11]导致的转C问题、Matlab转C适应性问题与限制性问题、以及转C对Coder 工具的影响等诸多方面的问题,甚至有些问题不能得到反馈,需自己察觉与分析。
根据Matlab转C过程遇到的一些特别的情况总结的一些转C细节概述以及对此做出的一些处理,在此列出仅供参考,以避免遇到此情况。
2.1 统筹编程
如果计划要将所编写的Matlab程序转成C代码,那么在设计算法之初,就一定要先参考转C流程步骤的第二部和第三步,否则就有可能出现完成了算法设计却无法转成C代码的窘境。若到转C时才发现这些问题再去改,则有可能需要对算法进行重新设计,改动量较大,甚者是无法修改的情况。
2.2 数据类型变化
Matlab变量转C过程中,变量的第一次调用(多为赋值语句,并为非可忽略语句),决定了它在C语言中的数据类型。如果在Matlab程序中,由于引用赋值(如下面第2)行语句)而导致它的类型发生变化,那么,则在转C过程中就会报错,这是由于Matlab和C语言的编程差异导致的[11]。Matlab程序中变量的数据类型可以发生改变,而C语言程序中却不可以。这样,变量a就从double变量变成了复数变量了。这样的情况在转C中会报错。
2.3 if语句转C
转C时,Matlab程序中的if语句后面不能是非标量的形式(如数组或结果为数组的表达式),否则报错。Matlab中,if后面的非标量,只有当全体为真时才认为是真(true),否则为假(false),所以转C时可以据此做一定的的修改,使得if后面为一个值的标量即可。endprint
2.4 被忽略的变量
Matlab程序中,会被“忽略”的变量需要事先“声明”,即预分配内存[12],否则会出错。Matlab程序可以在运算中动态新增变量,有些变量虽然写在代码中,但是可能由于没有执行到这段代码,所以内存中也就没有出现这个变量,即被“忽略”了。但是在转C过程中,这些变量却要求事先“声明”,这是Matlab与C语言编程差异导致的。
转C时会报错第7)行变量a33未被声明过,因为第5)行变量赋值语句在选择语句中,属非一定执行语句,所以被“忽略”了。
2.5 变量信息问题
把某项数据固定的写在Matlab函数的变量中,则转出的C代码中,这个变量的信息(长度,数据类型等)都是固定了的,若在Matlab程序中对这个变量的信息有修改,则这些修改未必会反映到转C中,如果错误使用这些信息,将导致程序逻辑错误。其他问题导致的变量信息修改未反映到转C中也会出现这样的情况。
2.6 错误的预分配内存
错误的预分配内存会导致转出来的的C代码逻辑错误,即Matlab转的C代码中,某些算法的运行结果、次数、或者数值等出现偏差,从而导致和原来的Matlab程序最终结果不一致。分配过多有可能会造成预分配内存前后的Matlab程序运行逻辑不一致,这属于错误的转C规范。分配过少则有可能导致Matlab转出来的C程序逻辑错误,甚至转出来的C程序在运行时会出现堆栈溢出[13]的现象。
多分配会导致原来的变量冗余,从而有可能导致后面的算法偏离原先设计。如果多分配內存是无法避免的,那么也可以在后面的算法中,加入修正语句,或避开这些冗余量的影响,也可以达到正确效果。预分配内存过少,则在转C的C代码后初始化这个变量时会保留错误的该变量信息(如数组长度,数据类型等)和申请内存不足,如果后面的算法使用了这些错误的信息,则程序运行时会偏离原先设计,甚至会有可能越界访问内存而导致C程序运行时堆栈溢出。所以应避免预分配内存过少。
2.7 转C致软件崩溃
Matlab函数的转C致使Matlab软件崩溃,直接退出Matlab软件程序的情况。这样的情况不常遇见,但很棘手,是转C的一大障碍。这个故障无法通过反馈分析原因,只能一次次调整程序试出来怎样的形式才不至于Matlab崩溃。在实践中发现,Matlab崩溃似乎和复数或复数表达式作为参数调用其它函数再转C有关。所以,建议转C的函数尽量避免用复数作为参数参与调用,改用实部虚部做参数,而后在函数内合成复数的方式。另外,避免用作参数的变量重新赋值,尤其是改变长度的幅值。
3 转C特殊处理办法
3.1 动态预分配内存
提供了一种预分配内存的思路,具体是,下一步的预分配内存,是要根据上一步的结果来做出操作的,所以这样需要预分配的内存长度是未知的。这种情况下,用一个变量作为参数来预分配合适长度的内存。而这个变量的值,则根据上一步运算结果和算法要求而得到。如此,转出来的C程序就可以根据实际算法运算情况来预分配不同长度的“变量”,而不是固定长度的数组。
预分配内存是Matlab函数转C前,规范Matlab代码时一步重要的工作。转C时,大部分的变量都是依照“第一次赋值语句”来声明变量的,而部分变量在Matlab里的第一次出现却不是赋值语句,而是循环语句里的引用赋值语句(例如:a(i)=i)。这样的情况在Matlab程序中会实时地自动生成和加长对应的变量(即会根据算法迭代而增加变量长度),而转C过程却无法适应这种情况,所以要求对这个变量先预分配内存。预分配内存的方法是在其调用之前加一句相对应赋值语句,以起到“第一次赋值语句”的作用。如果这里的“变量”长度是不固定的,则可以采用动态预分配内存方式。
3.2 外置接口转C
避开某些转C限制的特殊处理方法,一种妥协思维。Matlab自带的Coder转C工具有其支持转C函数库,包括大多数普通常用的函数和信号处理函数。然而,有些函数在Matlab转C过程中却有不同的限制。
重采样[14]是很多算法和系统中的重要环节。在设计算法之时,一般使用Matlab信号处理的resample函数,然而其在转C时却有重采样数据的长度必须为一个确定值,上、下采样率必须为确定值的常量。在很多算法或系统中,需要重采样的数据,其长度是不确定的。对于这个情况,设定一个预分配内存足够大的另一个“变量”,充当外置接口,专门作为resample的参数,把需要处理的数据值转移到其中,多余的部分忽略(相当于补0值),然后对整体做处理、转C,最后再从结果中提取出有用的部分(对应有用输入参数部分的结果)即可。
经过验证,确认了这一方法的可靠性,多余部分(补0值的部分、非有用部分)的存在不影响前面的有用输入参数部分的运算结果,从整体结果中取出的有用结果与单单输入有用参数所得到的结果相同。通过这种方式,使得在一定数据长度内,既能满足resample转C的这项限制要求,又适应了不同长度数据作为输入的情况。当然这里不同的长度的数据只要不超过前面所说的“一定长度”即可。
3.3 简化函数
通过对Matlab某些不支持转C的函数进行简化函数处理(去除冗余语句),使得它需要的部分功能可以转C。但是,并不是所有的不支持转C函数都能通过这种方式从而能转C,只是个别函数能实现。
一些不支持转C的函数,可能为了适应多种情况而使用了一些不支持转C的方法,为了能够转C,将这些方法去除或替换。但是这样一来,原函数的功能就有一定程度上的削弱或缺失。如果,可以确定这些削弱或缺失的功能是算法中所不需要的,那么就可以放心的通过这样的处理方式以使得程序能够转C,不然,无法通过这种方式使得这个函数能够转C。整个流程很简单,关键是要自己摸索着查找修改点,然后对其进行正确的简化操作,并确定这样子的操作不会对整个算法有影响。endprint
Matlab库函数中的medfilter1、lpc函数是不支持转C的,通过上述思路调整后得以转C,到达设计要求。以lpc函数为例,大致介绍简化函数转C流程:
①在matlab库函数相应的文件夹下找到lpc函数,将其复制到当前工程目录下;
②再用Code Generation Readiness Tool对matlab函数整体分析;
③提示lpc函數中try/catch语句不支持转C。
④进入工程目录下的lpc函数,将try/catch相关语句去掉并保存,通过可行性分析后再转C。
⑤再比较matlab程序和C程序的结果,得以验证这样操作能够满足要求。
4 结束语
Matlab程序转C代码是一项集工程创建、代码规范[15]、错误分析与修正、对比验证于一身的工程,有其一套行之有效的工作流程和解决方案。符合转C条件的Matlab程序,通过Matlab Coder工具,按照转C流程进行操作能够将完整Matlab程序转成C代码。通过Matlab程序的转C,缩短了算法研发与实际应用之间的距离,提高了工程师的研发效率。本文研究了MATLAB程序转C代码的方法,介绍了以Matlab2014a版本Coder为基础的转C流程,给出了转C注意事项与处理意见,并且针对一些以前没有解决办法的转C阻碍提出了创新性的解决思路与方法。但是也应看到,Matlab转C也并不是包治百病的,除了转C流程第二第三步提到的限制外,在Matlab编程中还应该注意到Matlab和C语言的编程差异以及适应性问题[11],这些都在一定程度上限定了Matlab算法编程。同时,我们可以看到,通过对某些转C限制进行某种“妥协”,也可以绕开这样的限制,以达到我们的目的。
参考文献:
[1]印金国.遥控信号调制与Matlab程序实现[J].电脑编程技巧与维护,2006(10):25-29.
[2]刘浩,韩晶.MATLAB R2014a完全自学一本通[M].北京:电子工业出版社,2015.
[3]廖灿灿,张树群,雷兆宜.Matlab Coder生成C代码的研究与应用[J].计算机与现代化,2013(3):175-178.
[4]Online MATLAB. MATLABR2014a,MATLAB Coder[EB/OL],http:/ /www.mathworks.cn /products /matlab-coder/,2014-10-10.
[5]Online MATLAB.从 MATLAB 代码生成 C 和 C++ 代码[EB/OL]https://cn.mathworks.com/products/matlab-coder.html,2014-10-10.
[6]李海奎.基于MATLAB函数库增强VB数值计算能力的研究[J].计算机工程与应用,2003,39(23):57-59.
[7]陆楠.自动C/C++代码转换工具提高MATLAB设计效率[J].电子设计技术,2011(7):38.
[8]黄根岭,张清淼,周利红.MATLAB/Simulink在通信原理教学中的应用[J].郑州铁路职业技术学院学报,2015(3):94-97.
[9]刘春辉.C++、java、matlab面象对象编程之比较[J].硅谷, 2010(6):75.
[10]蒋鹏.MATLAB编程的特点与优化[J].数字技术与应用, 2016(2):183.
[11]陈荣荣.C++与Matlab的基本语法比较[J].电脑编程技巧与维护,2011(14):32-34.
[12]李月洁,李兰友,尤一鸣.内存预分配管理[J].仪器仪表用户,2006,13(5):104-105.
[13]李宛娜.堆栈溢出技术剖析[J].哈尔滨职业技术学院学报, 2008(1):115-116.
[14]宗志毅,王晓音,杨莘元.多率信号处理中的重采样技术[J].电子信息对抗技术,2003,18(3):37-41.
[15]李大勇.浅谈软件开发中代码规范的问题[J].电子技术与软件工程,2015(15):50.endprint