涵盖的代码MathWorks有限授权
Daryl Ning, MathWorks
参加本次演讲,学习如何从MATLAB中自动生成可读和可移植的C代码®算法。使用MATLAB Coder™,您可以使用自动C代码生成从算法平稳过渡到实现,从而减少开发时间。这个C代码不需要任何进一步的MATLAB库,可以自由分发。
本演示演示了如何使用命令行方法或图形化项目管理工具:
记录日期:2014年8月19日
你好,我的名字是Daryl Ning,今天我们将讨论如何使用MATLAB编码器自动将MATLAB转换为C代码。首先,让我们从一个使用欧几里得距离测量的例子开始,我将展示如何使用MATLAB函数,并使用MATLAB编码器生成通用的NCC代码。让我们回到MATLAB。在MATLAB中,你可以看到我们的原始函数,这个函数被称为欧几里德函数。欧几里得接受某个输入向量x,然后将这个向量与向量的代码本进行比较,然后返回距离最小的向量。你可以看到我们调用了另一个函数norm。
我们有一个for循环,还有一个if语句。但它只是一个非常基本的MATLAB函数。现在,当我们通过MATLAB编码器运行这个时,我们会得到这个C代码。如果你看一下这段C代码,你会看到我们有一个叫做norm的辅助函数,这是C代码,我往下滚动一点。你会看到这是我们的主要入口点函数,叫做欧几里得函数和C代码。希望从这里,你能看到我们生成的C代码首先是泛型的,它是可读的。这里是一个已经成功使用MATLAB编码器的客户。iSonea开发了用于喘息检测和哮喘管理的移动应用程序和服务软件。使用MATLAB Coder,他们能够减少手工编码的工作量,加速算法开发迭代,最重要的是,他们的代码维护开销减少了。
你可以从这里的引用中看到,他们说没有其他的环境或编程语言可以在相同的时间内产生类似的结果。让我们看一下议程。首先,我们将讨论MATLAB生成c代码的动机。然后,我们将讨论生成C代码的三步工作流程。然后我们将查看一些用例并快速总结。那么,为什么工程师要把MATLAB翻译成C语言呢?原因有很多。首先,他们可能需要在进程中实现C代码,或者将这些代码交给软件工程师,因此他们需要原始的NCC代码。或者,他们可能需要将MATLAB算法与现有的C环境集成,因此他们希望获取源代码并创建某种静态或动态库。
他们这样做的另一个原因可能是因为他们想要在桌面上将MATLAB算法作为独立的可执行程序进行原型化。最后,有些人只是想用MATLAB的C代码来加速自己的用户编写的MATLAB算法,基本上是为了更快的仿真。因此,如果我们从技术计算工作流的角度来考虑这个问题,我们有访问数据阶段,探索和发现阶段,以及共享阶段,我们将展示的是如何从算法开发到部署,部署是独立的NCC代码。但更重要的是,我们如何在这些阶段之间快速地来回移动。如果我们考虑一下我们在这里做什么,我们从什么开始,我们从MATLAB环境开始。在MATLAB环境中,你可以做很多事情。所以你可以可视化你的数据,你可以开发你的算法,你甚至可以开发像应用程序一样围绕你的算法的用户界面。
现在,如果你想把所有这些,你想把可视化,算法,和用户界面部署到一些目标上,我们对此的解决方案是叫做MATLAB Compiler的东西。现在,MATLAB编译器可以从MATLAB算法中创建独立的可执行文件和C共享库,包括可视化和围绕它的任何用户界面。这样做的缺点是,这个应用程序将需要称为MATLAB编译运行时的东西,所以有一些开销。有一个引擎需要在后台运行。MATLAB编译器不会将MATLAB算法转换为C代码。但是,如果您只是想采用算法并创建独立的NCC代码,那么这就是MATLAB编码器的来源。因此,MATLAB Coder能够为大多数MATLAB算法生成C代码,并为您提供独立可读的C代码,就像您在初始示例中看到的那样。然后,无论您想要创建可执行程序还是库,都可以使用这些C代码并对其进行任何操作。
因此,这条路径是我们将在本演示的其余部分中讨论的,但我将在最后简要介绍MATLAB编译器的用例。因此,让我们从介绍性演示开始,我将向您展示如何使用MATLAB编码器应用程序来帮助您进行代码生成过程。让我们回到MATLAB。好的,我们在这里要看的只是一个简单的矩阵乘法。你可以看到这里我有一个基本函数,它只是把两个输入相乘,叫做mymult。现在,如果我想从这个函数生成C代码,我要做的就是在MATLAB中找到我的apps选项卡,然后打开我的MATLAB Coder应用程序,如果我向下滚动到我的代码生成应用程序,我要点击MATLAB Coder。第一件事是它让我给我的项目起个名字,所以我要叫它mymult。
现在,当项目打开时,我首先需要添加入口点文件。要做到这一点,我只需点击添加文件,点击mymult。现在,因为我们要生成C代码,我需要定义输入的大小和类型。要做到这一点,我可以点击这里定义它们。对于输入a,让我们开始的时候保持简单,我说输入是一个双精度体,它是一个标量。b也是一样,它是一个双精度体,也是一个标量。完成之后,我可以点击build选项卡,我可以将输出类型更改为C静态库,首先,我将生成C代码。我不会编译它。然后我可以点击构建。当我单击构建时,MATLAB编码器现在将继续并从我的MATLAB函数生成C代码。 If there are any errors or any troubles that it runs into, it will give me some warnings here. But it was successful, and then I can click on View Report. And what you'll see here is that here is my main entry point function, so I've got returns a double and simply multiplies a and b.
现在,我将讨论所有这些函数或者这些C和头文件它们会在稍后的演示中生成,但是现在让我们回到这个函数看看我们如何改变输入定义并生成不同的代码。所以如果我回到这里的概述标签,我可以改变这里的输入定义来生成一个不同的乘法。或者,我也可以使用某种测试台自动定义这些大小和类型。如果我回到MATLAB,你会看到我已经创建了一个测试台,它叫mymult。你可以在这个测试台上看到它将一个大小为3 × 4的双输入与一个大小为4 × 5的双输入相乘,它只是创建了一些随机矩阵来做这个。我可以使用这个测试台在MATLAB编码器项目中自动定义我的类型。回到这里,我能做的是点Auto Define Types,我只需要指定那个测试台,我的测试台。如果我运行那个,MATLAB Coder会做的是运行那个测试台,从那个测试台,它会找出实际输入数据类型和大小是什么。
我可以点击使用这些类型,你可以看到MATLAB编码器自动更新了我的输入定义。现在,当我点击Build时,我可以生成一个矩阵乘法,而不是标量乘法。如果我单击View Report,您将看到现在我的主要入口点函数有一个嵌套的四个循环,它执行我在测试台中定义的矩阵乘法。当您手动将MATLAB转换为C时,会遇到哪些挑战?因为这通常是大多数人在没有MATLAB编码器的情况下会做的事情。所以他们会找一个人在MATLAB中做算法设计,然后他们会把它重新编码成C或c++进行部署,不管他们想要原始的C代码还是一些库或可执行文件。但是在这里,通常你需要验证C代码的行为方式和MATLAB代码一样,因为通常你的MATLAB代码是你的黄金参考,你想要确保手工实现的行为和原始的MATLAB算法一样。
这里有一个验证步骤。不仅如此,通常情况下,算法会发生变化。这可能是为了改进算法,也可能是设计的要求发生了变化。但不可避免的是,算法会改变,这意味着你必须在手工编写的C代码中做出适当的改变。这种迭代在任何设计周期中都会发生很多很多次。这里的问题是我们实际上有一个独立的功能和实现规范。我们需要同时管理MATLAB代码和C代码。因此,这使得在开发过程中修改需求变得非常困难,因为这意味着任何需求更改都将导致更多的迭代。而且要使参考MATLAB代码与C代码保持同步是非常困难的。最重要的是,每个人都是人,所以您可能还会引入一些需要调试和修复的手动编码错误。
所以整个过程或者整个手工过程非常耗时和昂贵。在这样做时,有一些实现方面的考虑。例如,还记得我们最初的函数吗,它是一个简单的乘法。这里,我们把函数写成a = foo (b, c)其中a = b * c,怎么实现呢?它可以是元素对元素的乘法,可以是点积,也可以是矩阵乘法。或者,数据类型可以是不同的。它们可以是逻辑的,可以是整数,可以是任何东西。因此,当在C语言中实现它时,它不仅仅是简单地说返回b乘以C。这个简单的乘法可能是完全不同的东西,这取决于最初的算法设计者打算如何将它集成到一些更大的代码库中。这就是多态性的一个例子。您还必须考虑如何处理内存分配。
MATLAB是非常容易使用的,因为它将只是简单地动态分配内存,当你需要它。但是当您实现C代码时,您需要决定是使用静态内存分配还是动态内存分配,还是两者的组合。与多态相关,你是在处理矩阵还是数组,因为这将影响实现。您是否在MATLAB代码中使用定点数据类型,以及如何将其转换为C中的整数类型?所以最终的结果是一个基本的MATLAB函数,像这样,它是用于卡尔曼滤波器的,可以是5行MATLAB代码,因为它基本上是很多矩阵运算。这可能会变成数百行C代码。当我们使用MATLAB到C语言的自动转换时,我们的想法是,算法设计和代码生成都是在MATLAB中完成的。所以我们最初的算法设计者可以简单地生成一个MEX下跌。现在,如果您不熟悉什么是MEX文件,那么MEX文件基本上是编译的C代码。它就像一个可以直接从MATLAB调用的库。
MEX文件确实有一个围绕C代码的包装器,它允许您将MATLAB数据类型传递给C,并且当您从MATLAB生成C代码时创建MEX文件的想法是这样我们可以验证生成的C代码的行为与MATLAB相同。一旦我们验证了这个C代码的行为与MATLAB相同,我们就可以继续创建库或可执行文件。或者,我们可以继续对算法进行迭代。但由于现在没有人工翻译,整个过程变得更加精简。所以有了MATLAB编码器,你基本上可以在MATLAB中维护一个设计,并且更快地进入C语言。更快地使用C语言的好处是,你实际上可以对你的原始算法执行更多的测试,并花时间在MATLAB中改进算法,而不是仅仅由于时间限制而实现一些实际上不适合实现的东西。
因此,让我们来看看从MATLAB生成C代码的三步工作流程。它在这里。你可以看到,第一步是你需要准备你的MATLAB算法来生成代码。所以当你准备你的MATLAB算法时,你需要决定你要做的实现选择,但也要确保你的MATLAB算法使用支持的语言特征。万博1manbetx所以当从MATLAB生成C代码时,我们不支持所有的MATLAB语言,但是我们支持大部分的MATL万博1manbetxAB语言。我稍后会讲到这个。所以一旦你准备好了你的代码,你就需要测试你的MATLAB代码是兼容的。您需要验证您所拥有的MATLAB代码实际上可以生成您的代码,然后您可以对这些代码执行迭代以进行优化,最后,您可以生成MEX文件以根据您的原始测试台架验证c代码实现。一旦你完成了这个验证,你就可以使用原始的C代码,NCC代码,并将其实现为源代码、库或可执行文件。这取决于你想怎么用它。
为了给你们一个用MATLAB算法把它转换成C语言的工作流程的例子,我将用牛顿-拉夫森算法来做一个更广泛的例子。我们再来看看MATLAB。让我们关闭一些。让我们打开牛顿-拉夫森算法。好的,这就是用牛顿搜索技术来求一个数的n次方根。在我看代码之前,让我给你们举个例子来说明我的意思。这是我的试验台。所以这里的NRT函数取10的四次方根。我要取10的四次方根,我要指定一个容差值。使用这个是因为算法是迭代的,所以我想告诉它什么时候停止。 So if I was to execute this, you'll see that the fourth root of 10 using this algorithm is about 1.7783. So let's take a look at the algorithm itself.
你可以看到它返回第n个根。它还返回迭代次数以及在每次迭代中计算的值的历史记录。它的输入个数是可变的。如果我往下滚动——我应该让它最大化——我们可以在算法中看到如果输入指定的值小于0,我们就会返回0。或者,如果用户指定的值大于零,那么我们将执行牛顿搜索算法。如果我们看一下,牛顿搜索算法就像你在这里看到的,基本上是一个while循环,所以它会循环并迭代找到第n个根。您将注意到的一件事是,它将在50次迭代后自动停止。再往下,你会看到这里有一个子函数,这个子函数用来计算导数。我们有一个main函数。我们有一个子函数。
这将返回第n个根和历史记录,如果我们返回到主调用函数,当我们得到历史记录时,我们也可以计算出达到最终值所需的迭代次数。这就是这个函数的作用。因此,让我们使用MATLAB编码器从这个函数生成C代码。但我要做的第一件事是使用代码准备工具。因为也许我想做的是首先感受一下,准备好代码生成需要多长时间。我能做的就是选择我想要生成代码的函数。我右键点击它,你可以看到下面有一个选项,上面写着,检查代码生成准备。当我选择它时,它会告诉我从这个函数生成C代码有多难。你可以在这里看到,代码生成准备得分是5,所以MATLAB认为它可能只需要很小的改变,就可以让这个MATLAB代码生成C代码。
它甚至按代码结构进行了分解,因此您可以看到内部函数生成C代码是多么困难。在目前的状态下,它看起来相当不错。我们再打开Coder应用。这一次,我把我们的项目命名为NRT。我们和上次做的一样。我们将添加入口点文件NRT.m。然后,我将使用我的测试台自动定义类型,而不是指定输入是什么。这是我的试验台。让我们运行那个,Coder app能找出输入数据类型是什么。在这种情况下,这很简单。 They're just three scalar doubles. So the first thing I need to do then when I generate C code if you remember our three-step workflow is to check that it actually can generate C code. And we can do this by trying to create a MEX function. So if I just try to create a MEX function to start with, if I click on build, this will try to generate C code and then turn it into a MEX function for verification.
如果出现错误,它会告诉我。你可以在这里看到它实际上遇到了错误。我要打开我的错误报告,你可以看到它遇到的错误列表。如果我只选择第一个,你会看到它突出显示了这条线。当我把鼠标悬停在这里的时候,这里有一个未定义的函数变量,h,对一个局部变量的第一次赋值将决定它的类。它告诉我的是,当我第一次在这个函数中赋值一个局部变量时,它将被定义为class。通过这样说,我实际上把它定义为一个标量。问题是,如果你看到这里的另一个红色下划线——这是在一个循环中,我们实际上在做的是在这个循环中增加h的大小。这就是我们得到这个错误信息的原因。但是我知道迭代的次数最多是50次,所以我能做的就是把内存预先分配给这个值,h,这是一个历史记录,希望这能解决我们的问题。
如果我回到MATLAB,使用牛顿搜索算法,这就是我们遇到问题的地方。我在这里加一条线,h等于0,有50个元素的向量。所以我们先预分配内存,然后保存它。看看这个能不能解题。如果我再去代码生成应用程序试着构建它,代码生成成功了。我不会看报告,因为它实际上是一个MEX函数。我们可以看一下,但它有很多包装器代码这让它看起来有点乱。在任何情况下,仅为验证而生成MEX函数。所以我们可以做的是,我们可以使用那个MEX函数来验证生成的C代码的行为是否与原始的MATLAB算法相同。为了做到这一点,我们在下面有这个验证部分。 You can see we'll use our original test file test bench.
但重要的是这个复选框。它说它将把入口点调用重定向到MEX函数。这意味着,当我们运行这里的测试台,而不是调用NRT,这是我们的MATLAB函数,它将调用我们的MEX函数,这是编译的C代码。所以我们的MEX函数实际上在这里被调用,NRT下划线MEX。因此,这所要做的就是将任何对NRT的调用重定向到NRT下划线MEX。如果我点击运行,它会运行测试台,但是当它运行测试台时,它实际上运行它调用我们的MEX函数。如果你看一下命令窗口,你会发现我们得到了相同的答案。这就是我们的验证我们生成的C代码和MATLAB代码的行为是一样的。现在,很明显你可以在这里建立一个更复杂的测试平台,但这只是对这个例子的简化。现在我们已经实际验证了C代码的行为与MATLAB相同,我们现在要做的是生成没有MEX包装的原始C代码。 So let me just change our output time to C static library, and we'll just generate the code and click on Build.
现在,这将为这个函数创建原始的NCC代码。所以我们可以看一下报告。所以我们来看看这份报告。我们可以看到,首先这里有一个辅助函数。看起来它处理的是一些无限的数,一些非有限的数。如果我往下滚动,你会看到这里是NRT的主要入口点函数,然后是它为实现这个特定函数而生成的所有C代码。这里我们有175行C代码。我认为我应该指出的另一件事是,如果你回到MATLAB代码,在MATLAB代码中,它也会给你一个原始MATLAB代码的报告。当您将鼠标悬停在其中一些变量上时,您可以看到它为您提供有关大小,类和复杂性的信息。当您试图调试代码时,这有时会很有用。 I just thought I'd point that out.
但是如果我们回到C代码,你会注意到的第一件事是我们生成了很多函数。那么为什么要生成这么多函数呢?我们首先看到的是初始化和终止函数。现在,无论何时生成C代码,我们总是生成一个初始化函数和一个终止函数。调用initialize是一个很好的做法。好的,在这里,初始化函数。在调用主入口点函数之前将它集成到目标代码中,然后调用终止函数。显然在initialize函数中,它会初始化状态之类的东西。也许你脑子里有持久的记忆。在调用主入口点函数之前这样做很重要。 Terminate may be used. If you've opened up maybe some file pointers, it will close down some file pointers. Things like that. But the important thing to remember is always call the initialize function first before calling the entry point function, and then call the terminate to finish if required. Now, there may not always be something inside the initialize functions and the terminate functions, but it's a good practice to simply keep them in your target code because if you happen to change your algorithm and we do generate some code which happens to be put inside that initialize function, after you've generated the code there is no need to change the code in your target environment.
你可以让它保持原样,因为它已经调用了initialize,它会调用你的入口点,它会调用你的terminate。我们在这里的其他函数是这些get imf C, get nan -这些都是处理非有限数的代码。我们这样做是因为MATLAB可以处理非有限数,如果你碰巧生成需要处理它的代码,那么你就准备好了。但如果你不需要这种支持,你也可以关闭它。万博1manbetx要做到这一点,我们会去到More Settings,在More Settings中我有一些选项。其中一个选项是速度,你可以看到我实际上可以关闭对非有限数的支持。万博1manbetx如果我这样做并点击关闭然后重建它,MATLAB将在没有非有限数支持的情况下重建代码。万博1manbetx现在,如果您的代码确实需要支持非有限数,它将在这里抛出一个错误,并告诉您需要再次选中该框。万博1manbetx然而,在这种情况下,我们不需要它。所以当我看报告时,你可以看到现在我们有更少的C和头文件,如果我们现在向下滚动,你会注意到我们在入口点函数上面的原始帮助文件不再存在了。
看起来辅助函数就是用来处理非有限数的。再往下滚动,这就是我们所有的代码。我们现在从175行代码减少到115行代码,仅仅是通过移除非有限的数字支持。万博1manbetx我想指出的另一件事是你会看到这里实际上有评论。你可以看到这里有一个注释,在50次迭代结束后,这里有一个注释,确定迭代次数。现在,这些注释直接来自MATLAB代码,并且来自MATLAB代码的注释将帮助您跟踪MATLAB代码到C代码。它并不总是有效,因为在生成C代码时执行了一些优化。我们不只是做一行对一行的翻译。否则,它将是极其低效的。但是,它将帮助您在参考生成的C代码时跟踪原始MATLAB代码的位置。
但是我想指出的一点是在这个确定迭代之后,我们可以再次看到一堆C代码看起来像是在执行一个while循环来做一些检查。如果我们回到这里的MATLAB代码,你会看到这里是我们的确定迭代,但是在那之后,我们只有一行MATLAB代码。我们有一行MATLAB代码,但是它生成了多行C代码。为什么呢?如果我们看一下这行MATLAB代码,它要做的是确定需要多少次迭代来计算n次方根。虽然它只有一行MATLAB代码,它完成了工作,但它是一种非常低效的方法来找到有多少次迭代。这就是我想如果你把垃圾放进MATLAB编码器,你可能会得到垃圾。所以在构造算法时你需要更聪明一些这样才能生成漂亮的C代码。
这里,这行代码是不必要的。我们可以将这行代码替换为这样的东西,比如迭代等于历史的总和不等于零。我的意思是,这可能是另一种计算迭代次数的方法基于历史返回的逻辑向量不等于0。如果我保存这个,然后返回并重新生成C代码,然后查看报告,我会向下滚动到代码的区域,你可以看到,我们现在已经折叠了很多代码。之前大概有115行。现在,我们仅仅通过对MATLAB代码做一个非常微妙的改变,使算法更有效,就减少到大约92。你甚至可以更进一步。假设你想更详细地说明你想如何计算迭代次数,甚至把它写得有点像C代码。所以这里我可以说,迭代等于0,然后做一个for循环,实际上循环for JJ等于1,到length of history。我们可以说迭代等于迭代加1。 Else, we will break.
我需要在这里核对一下。如果JJ的历史不等于零,那么我们实际上会增加迭代的次数。这更像是做同样事情的C档方式。如果我保存那个,回到我的app,重建代码,看看报告,现在我们只有90行代码。我想在这里指出的一件事是,即使在我的MATLAB代码中,我写了一个for循环,里面有一个if语句,MATLAB编码器很聪明地看到了它,并说,好吧,而不是用if语句做for循环,让我们把它改成一个有几个条件的while循环。这是MATLAB Coder可能为你做的一个优化的例子。现在我们只剩下90行代码了。我想看的另一件事是这个入口点函数。
所以当我们看入口点函数时,我们得到了所有的输入。然后我们得到了我们的输出,它是n次方根,迭代的次数。但如果我们看一下这里,历史看起来有点奇怪。输出历史记录。我本来期望一个包含50个元素的向量来输出我们的历史值,但是我看到的是这个历史数据变量和历史大小。每当你看到这两个,我猜这两个变量,像这样的参数,这表明你的代码中有一些变量大小。好的,MATLAB所做的是返回一些数据,它也返回你在预分配内存中的位置,因为它并不总是知道因为它的大小会变化。现在,如果我们回到我们的代码,我们可以弄清楚为什么会这样。它发生在主函数NRT中。虽然牛顿搜索算法会为历史返回一个50个元素的向量,因为记住在牛顿搜索算法中,我们预先分配了50个元素,问题是这个NRT函数当它的值小于0时被调用,它会给历史分配一个0的值这意味着MATLAB编码器会看着这个说,历史可以是一个标量也可以是一个50个元素的向量。
这就是可变大小的用武之地。为了绕过这个给定历史为0的情况,我们可以把它定义为一个0向量。这意味着历史将永远是一个包含50个元素的向量。让我们保存它,回到MATLAB Coder app并重建它。当我们重建的时候,我们可以看看报告。你在这里看到的是,这更符合我们的预期。我们的输出只是一个包含50个元素的向量,这也清理了代码体的一部分。现在我们只剩下82行代码了,一开始我们有170行代码现在我们有82行代码只做了一些修改。你会注意到的另一件事是牛顿搜索算法实际上已经被内联到这个入口点函数中。
所以在MATLAB的主NRT中,我们有这个else语句如果输入大于0,我们调用牛顿搜索算法。但是在实际的C代码中,您将看到牛顿搜索算法——这里是else语句——牛顿搜索算法已经内联到代码中。现在,假设你不想让它内嵌。现在,Coder会内联它,如果它认为只有几行代码,这会使它更有效率。但是如果你愿意,你可以把它关掉。我们可以这样做的方法是在牛顿搜索算法中,在顶部这里,我可以说,看,coderdotinline never。永远不要内联这段代码。如果我这样做,我可以回到代码生成,重新构建它,当我重新构建它的时候,你会在这个报告中看到在else语句之后的入口点函数中,我现在实际上调用了牛顿搜索算法。如果我点击它,你可以看到这实际上是一个单独的C代码,有它自己的头文件。
好了,我们结束吧。希望这能让你们更好地了解如何使用这个工作流,从MATLAB代码中生成C代码。现在,我之前提到过MATLAB Coder只支持MATLAB语言的一个子集。万博1manbetx我们不能为所有的MATLAB语言生成C代码。这个维恩图告诉你我们支持什么。万博1manbetx浅蓝色区域内的所有东西都是支持的,我们支持可变大小的数据,我们支持定点持久性类,万博1manbetx全局变量,结构体,但你会注意到外面这里有些东西我们不支持。所以可视化是我们不支持的。万博1manbetx我们不会为plot或surf之类的东西生成C代码。如果你使用原生Java调用,我们不会生成C代码,如果你使用单元格数组,我们还不支持它们。万博1manbetx所以有一些东西我们不支持,但这些都是完整的文档。万博1manbetx In terms of features and functions, you can see here here, here's a table.
支持大多数MATLAB特性和函数。万博1manbetx支持所有不同的矩阵和数组、大多数数据类型以及编程结构。万博1manbetx在函数方面,我们支持大多数MATLAB的基函数。万博1manbetx我们从万博1manbetx信号处理工具箱、通信系统工具箱、计算机视觉系统工具箱中支持了很多功能,也从统计工具箱中支持了一些功能。但是,这些都是有充分记录的。既然我已经向您展示了如何生成C代码,那么让我们看一下一些用例。所以我提到用例首先是将算法与定制软件集成,加速MATLAB算法,在pc上原型算法以及在嵌入式进程上实现。让我们来看一个代码集成的例子。这里我将为缩放算法生成一些代码然后将其集成到Visual Studio父项目中。
所以我们回到MATLAB。我会关闭一些。让我打开我要讲的算法。为了向你们展示它是如何开始的,让我来运行这个小模拟。所以在左边,我们有一些正在稳定的图像。这里有一个缩放系数。我要做的是放大稳定后的图像。你可以看到当我移动滑动条时,我可以缩放它。同样地,我可以把它变小。现在,我不为整个应用程序生成代码。 It's just for the algorithm which does the zoom. So if I stop this, we'll take a look with this algorithm. The algorithm is called image process. It takes an input image as well as some factor. Obviously, if the factor is greater than one it will magnify, if the factor is less than one it will shrink it. So you can think if you're magnifying an image—let's just say you've made an image twice as big—you'd need to somehow interpolate between each of the pixels to make that image bigger.
这就是这个算法的作用。现在,我想这个算法做什么并不重要。我们只是想证明我们可以把它集成到另一个系统中。为了节省时间,我先打开一个已有的项目。现在,你会看到一件有趣的事情就是我定义的输入图像的类型是double +冒号∞乘以冒号∞。这意味着输入的维度是任意到无穷大的数乘以任意到无穷大的数,这意味着我不知道输入图像有多大,这意味着我必须使用动态内存分配。点击构建,在更多设置下你会看到我可以在内存下设置。你可以看到我可以启用可变大小支持,这实际上是默认的,然后还可以启用动态内存分配。万博1manbetx你可以在这里控制它。如果您想使用静态内存分配,显然可以取消选择,但这样我们就无法为这个项目生成代码。
我点击Build。它要做的是生成实现那个算法所需的C代码。但是因为我们使用的是动态内存分配,所以它还会包含一些其他的函数。所以你可以看到,如果我们看一下图像处理的入口点函数,你会看到输入图像是这种类型的EMX数组下划线真正的下划线t。现在,这是因为输入是未知的,MATLAB Coder需要做的是创建自己的数据结构来处理动态内存分配。现在,因为我们正在创建我们自己的数据结构,我们还创建了一个API来允许您使用这些自定义数据结构。因此,如果我看一下这个图像过程,下划线EMX api。cpp,在这里你会看到这些函数来帮助你处理这个自定义数据结构。我想这里你会看到的是EMX创建了真实的,这就是你如何创建这种数据结构。你可以看到它基本上被指定了一些数据以及大小。
同样地,因为我们创建了自己的数据结构,我们也有一个效用函数,一旦你完成它,它就会被销毁。现在我们已经生成了代码,让我们将其集成到另一个项目中。再一次,让我打开一个已有的项目。把这个解打开。这将在Visual Studio中打开。不好意思,让我在MATLAB之外打开这个。这里的想法是,我们刚刚创建了一些通用的NCC代码,我们想把它集成到另一个用C或c++编写的环境中。所以我在这里打开了一个现有的解决方案,这个解决方案实际上是这样做的,它从网络摄像头获取输入流,然后我们从网络摄像头获得的数据将输入到我们的缩放算法中,然后显示出来。这是我的文件。让我们看一下父项目中的原始main.CPP文件。
我们将使用一些开放的CV工具来与网络摄像头对话,并将其显示在我们的PC上。但如果你滚动这个主文件,主要看的是这里我们使用了那个实用函数,这个EMX create underscore real underscore t,我们创建了我们需要使用的那个自定义数据结构。这里是我们运行初始化函数的地方,这里是我们创建输出图像的地方因为我们有一些未知大小的输入图像和一些未知大小的输出图像。如果我往下滚动一点,这里有一个for循环,它只是从网络摄像头数据中填充数据,这里是MATLAB编码器对代码生成器的调用。这就是图像处理函数。处理完它之后,我们填充输出数组。我来解一下。好了,现在已经建好了。我把它关掉,然后进入我的发布目录。
你可以看到,这是我刚创建的视频webcam。EXE,我要做的是从MATLAB中调用它。所以我要用我的Bang Operator视频摄像头,把它放大到1.5倍。现在,你会看到我的笔记本电脑上有一个网络摄像头,所以我只是在移动它。我现在在办公室。这里有一把椅子,你可以看到这是放大了1.5倍的图像。这只是向你展示我们从MATLAB Coder生成的C代码运行在其他父Visual Studio项目中。好的,我们可以做的另一件事是我们可以将现有的C代码集成到代码生成过程中。所以这是有用的,如果你有一些现有的C代码,你想用,而不是MATLAB编码器生成的C代码,这可能是因为你有一些优化的实现,你目前使用的函数。
有很多方法可以做到这一点,我将向你们展示一个简单的方法,使用一个叫做Coder的东西。目标和编码器。那么让我们回到MATLAB,让我们看一下这个定制的卷积函数,我将对它进行积分。我在这里所做的就是做一个卷积。这是我的试验台。我可以运行这个。它有两个输入向量,然后我可以做一个卷积。这就是它所做的一切。但在实际的函数中,它被称为自定义的com下划线ceval。这是函数。我构建它的方式允许我调用外部的C代码。 So by the way, the actual C code I'm going to use is here. It's called custom underscore com dot c. It takes the input signal as well as the signal length, the input kernel as well as the kernel length, and then returns the output And then what we've got here is just some C code for a convolution. So in the MATLAB code itself, when I call this MATLAB function it branches off into two separate paths depending on what MATLAB is doing.
所以当我使用编码器时。目标,我可以说,如果我在MATLAB中执行如果我给它这个输入,它会测试它是否在MATLAB中运行。所以如果这个函数在MATLAB中运行,我将调用MATLAB的原生卷积函数,因为我需要因为我需要它在MATLAB中运行以进行仿真。否则,如果我不在MATLAB中运行,我会生成代码。所以如果我在生成代码,我想做的是调用一个叫做custom conv的外部C函数,当我调用这个外部C函数时,我将传入我的输入x,但是我将通过只读引用来传递它。我将传入x的长度,我也将传入内核,通过引用传入只读,以及长度,然后我将返回输出为y,这处理了调用C函数时的数据类型转换。所以当我为这个函数生成C代码时,它不会使用这个路径。它将使用这条路径并集成一些外部的C代码。
我们来证明一下。我们回到MATLAB -哦,我已经在MATLAB中了,但我要打开一个现有的项目,好,这里我有我的入口点函数。现在,我唯一要做的是在More Settings中生成C代码时,你可以看到在自定义代码中,我选择tab,我可以指定自定义C代码所在的位置以及包含目录一旦我指定了,我就可以构建它。当我构建它的时候,你会注意到,当你看C代码的时候,它并没有从。com函数生成C代码。相反,它做的是直接调用那个自定义下划线。com函数我之前给你们展示过。如果我点击它,它就在这里。再一次,我们在这里所做的是,我们已经能够将一些外部的C代码集成到代码生成过程中,当你有优化的例程时,这是很有用的。那么如果你想加速MATLAB算法呢?
我想说的第一件事是,与其看C代码生成,不如先看更好的算法。例如,如果你在做一个矩阵逆,也许你可以用不同的方法来解决同一个问题。看看使用优化库的不同更有效的实现。或者,看看通过使用额外的计算资源- matlab可以通过并行计算工具箱访问额外的进程和gpu。因此,在了解C代码生成之前,您可以先了解许多其他选项。所以说到加速算法,你应该做的第一件事就是优化你的MATLAB代码。研究并行计算选项。如果您正在处理DSP系统和通信,请查看系统对象。尝试使用MEX实现您自己的自定义代码,但是如果您不想编写自己的C代码,那么您可以在这里尝试MATLAB C代码生成。
因此,我在这里使用的示例只是再次运行牛顿-拉夫森算法,并将我们生成的MEX文件与原始MATLAB代码进行比较。如果我回到MATLAB,我所做的就是在这里写一个小脚本。我要运行1000次迭代。现在,我要做的第一件事是计算MATLAB代码的时间,你可以看到,在这个MATLAB代码中,我所做的就是调用NRT算法,我把它用一个勾号包裹起来,来计算执行的时间。我们在MATLAB中运行几次。在MATLAB中,平均需要0.003秒。现在,让我们对MEX代码计时。我之前所做的是,我已经把NRT算法转换成一个MEX文件,我使用的是我之前展示的例子的最后一次迭代。所以我要做同样的事情,但是我要运行MEX下跌1000次,看看平均时间。好了,当我运行几次的时候,我看到了一些变化。 I mean, we're looking at small numbers here, but we're looking at about 2.7, 2.8 by 10 to the negative five seconds to run the same algorithm.
如果我看一下加速因子——在这个特殊的例子中,我们看到的是一个大约11的加速因子,但这并不能说明你会得到什么。这取决于你写的MATLAB代码,但你可以加快速度。加速因子是不同的,我猜你经常会看到加速的地方是当你处理通信和信号处理的时候,几乎总是当你处理定点算法的时候,还有当你的原始MATLAB代码有很多带状态的for循环的时候,你不能对你的算法进行矢量化。这就是你经常看到加速的地方。你不会看到加速或者你可能看不到加速的地方是当你做很多线性代数的时候,比如矩阵计算,因为在MATLAB中它们已经非常快了因为它们调用IPP和BLAS库,它们已经是多线程计算了。因此,当您尝试进行这种类型的计算时,您不会看到太多的加速。
它们已经在MATLAB中优化过了。如果您有并行计算工具箱,我们也支持多核的MEX函数。万博1manbetx因此,如果您使用parfor循环,例如,当您生成MEX函数时,它也将使用开放的mp技术运行多核。但这需要你有一个支持open mp的编译器。万博1manbetx同样,如果您只是生成独立的C代码,而不是MEX文件,它也将使用这个打开的mp。但是,您需要一个支持open mp的编译器。万博1manbetx我想人们可能想做的另一件事是在PC上制作算法原型。所以这就是你可以使用生成的代码的地方,也提供一个main。C文件作为入口点,让MATLAB不仅生成独立的C代码,而且将其编译为XE。我们来看其中一个例子。我要再运行一次视频防抖演示。 So similar to the one you saw before when you saw the zoom.
这使用了一大堆计算机视觉系统的工具箱系统对象。如果我运行这个而不是看代码,你会看到这个视频稳定。现在,我前面提到,MATLAB Coder不支持可移植的C代码生成可视化。万博1manbetx现在,这个特殊的计算机视觉系统工具箱系统对象是一个例外。我们在这里支万博1manbetx持可视化。我们将支持这个窗万博1manbetx口的代码生成,这主要是因为它不是一个真正独立的C。它实际上是一个C库,它将包括——它将依赖于平台。但我们看看能用这个做什么。回到文件夹,打开项目,点击build。现在,我将这里的输出类型指定为c可执行文件。我必须指定一个main函数。
如果我看一下主函数,这就是它的样子。我必须包含头文件。在main函数中,很简单。我所做的就是调用初始化函数,调用入口点函数,然后调用终止函数,这就是我所做的。所以让我们回到项目和建设。当我点击build时,它会从MATLAB代码中生成C代码,一旦完成它会编译main。C文件。当它完成后,它会在我的当前目录下给我一个可执行文件,我可以运行它。还需要在前面展示的自定义代码部分的more settings选项卡中指定main.C文件的位置。这样代码生成就完成了。如果我回到当前文件夹,你会看到一个视频稳定器。exc。 Let me open that outside MATLAB. And there you have that same little stabilization application. If you want to work with Simulink, what we have is a MATLAB function block in Simulink. So in Simulink, we've got this MATLAB function block.
你可以合并MATLAB代码,当Simulink运行时,Simulink会将MATLA万博1manbetxB代码转换为C代码,并将其转换为库,因此它在Simulink中运行得更快。记得我们有这两个选项的时候,我一开始就给你们展示了这两个选项,我们讨论的是MATLAB Coder。现在让我简单地讨论一下MATLAB编译器。基本上,MATLAB编译器也允许你共享应用程序,允许你创建exe, dll,你也可以创建像Excel插件和Java类这样的东西。关于MATLAB Compiler的好处是它实际上支持几乎所有的MATLAB语言,大部分的MATLAB语言和工具箱。万博1manbetx这意味着你根本不需要做那么多的改变。但正如我之前所说,缺点是它还需要MATLAB编译器运行时引擎,虽然这是一个免版税的部署,但这确实意味着这些编译的应用程序只会运行在可以运行MATLAB的平台上,因为MATLAB编译器不会给你通用的NCC代码。
它所做的基本上是在你的代码周围写一个包装器,并将其编译成一个可以从MCR调用的表单。所以如果你在考虑你应该采取什么选择,你必须考虑你的要求是什么。所以对于MATLAB编码器,它会给你可移植和可读的C源代码。MATLAB编译器只会给你可执行文件或一些共享库。MATLAB编码器只支持MATLAB万博1manbetx语言的一个子集和一些工具箱。另一方面,MATLAB编译器几乎支持MATLAB的大部分功能和语言。万博1manbetxMATLAB编码器不需要运行时。它只是通用的C代码,你可以拿走并编译,而MATLAB可执行文件和为MATLAB编译器创建的软件库将需要MATLAB编译器运行时。但这两种解决方案都万博 尤文图斯是免费的。
一旦您生成了代码或生成了库,您就可以对它们做任何您想做的事情。我没有给你一个使用MATLAB编码器的固定点或嵌入式应用程序的例子,但我确实有一个用户故事。这是VivaQuant公司的产品,他们开发并验证了他们的嵌入式ECG传感设备。他们使用MATLAB以及我们的定点工具和MATLAB编码器在arm cortex m系列处理器上实现他们的算法。你可以在这里看到结果他们实际上加速了300%的发展。他们实际上能够最小化功耗和内存,并使用此工作流程执行更严格的测试。现在让我们快速总结一下。我们在这里展示的是如何从算法开发到通用的NCC代码,以及如何在两者之间快速地来回移动,对算法进行更改并自动生成C代码。这样做确实会加速你的发展。
如果你想想你的传统工作流程,通常你开发你的算法,你测试它,你手动将它转换成C语言,然后你做更多的测试和迭代,这个过程在这里,变得非常非常耗时。当您使用自动C代码生成时,您可以节省大量时间,这些时间可以用于改进算法和测试算法,以确保部署的内容具有生产质量。因此,这里的关键要点- MATLAB提供了从浮点和定点MATLAB代码到C代码的直接路径。它适用于需要源代码和需要小内存占用的应用程序,因此您无法负担在后台运行MATLAB编译器运行时的费用。自动生成可以帮助您加速设计迭代,并减少验证工作。欲了解更多信息,请访问我们的产品页面mathworks.com/products/matlab coder。s manbetx 845从这里,你可以申请一个试用许可证。或者,您可以与您的Mathworks客户经理联系以要求试用,或者您可以通过以下电子邮件地址或电话号码直接与我们联系。
你亦可选择下列网址:
选择中国网站(中文或英文)以获得最佳网站性能。其他MathWorks国家网站没有针对您所在位置的访问进行优化。