主要内容

使用 MATLAB Coder 生成 C 代码

在本教程中,您使用 MATLAB® Coder™ 为 MATLAB 函数生成 C 源代码。首先生成只能接受具有固定预分配大小的输入的 C 代码。然后生成可以接受许多不同大小的输入的 C 代码。

您还可以使用 codegen 命令在 MATLAB 命令行中生成代码。有关教程,请参阅通过命令行生成 C 代码

MATLAB Coder 从 MATLAB 函数(而不是脚本)生成代码。如果您的 MATLAB 代码是脚本形式,请在生成代码之前围绕脚本创建一个包装函数。

教程文件:欧几里德距离

打开此示例以获取本教程的文件。

教程文件的描述

本教程使用 euclidean_data.mateuclidean.meuclidean_test.m 文件。

  • MATLAB 数据文件 euclidean_data.mat 包含两部分数据:

    • x 是一个 3×1 列向量,表示三维欧几里德空间中的一个点。

    • cb 是一个 3×216 数组。cb 中的每列都表示三维欧几里德空间中的一个点。

  • MATLAB 文件 euclidean.m 包含 euclidean 函数,该函数在本示例中实现核心算法。该函数接受 xcb 作为输入,计算 xcb 中每个点之间的欧几里德距离,并返回以下量:

    • 列向量 y_min,它等于 cb 中表示与 x 距离最近的点的列。

    • 列向量 y_max,它等于 cb 中表示与 x 距离最远的点的列。

    • 二维向量 idx,其中包含 cb 中向量 y_miny_max 的列索引。

    • 二维向量 distance,其中包含计算出的到 x 的最小和最大距离。

    function [y_min,y_max,idx,distance] = euclidean(x,cb)
    % Initialize minimum distance as distance to first element of cb
    % Initialize maximum distance as distance to first element of cb
    idx(1)=1;
    idx(2)=1;
    
    distance(1)=norm(x-cb(:,1));
    distance(2)=norm(x-cb(:,1));
    
    % Find the vector in cb with minimum distance to x
    % Find the vector in cb with maximum distance to x
    for index=2:size(cb,2)
        d=norm(x-cb(:,index));
        if d < distance(1)
            distance(1)=d;
            idx(1)=index;
        end
        if d > distance(2)
            distance(2)=d;
            idx(2)=index;
        end
    end
    
    % Output the minimum and maximum distance vectors
    y_min=cb(:,idx(1));
    y_max=cb(:,idx(2));
    
    end
  • MATLAB 脚本 euclidean_test.m 将数据文件 euclidean_data.mat 加载到工作区中。接着,它调用函数 euclidean 来计算 y_miny_maxidxdistance。然后,脚本在命令行中显示计算出的量。

    加载 euclidean_data.mat 是在调用核心算法之前执行的预处理步骤。显示结果是后处理步骤。

    % Load test data 
    load euclidean_data.mat
    
    % Determine closest and farthest points and corresponding distances
    [y_min,y_max,idx,distance] = euclidean(x,cb);
    
    % Display output for the closest point
    disp('Coordinates of the closest point are: ');
    disp(num2str(y_min'));
    disp(['Index of the closest point is ', num2str(idx(1))]);
    disp(['Distance to the closest point is ', num2str(distance(1))]);
    
    disp(newline);
    
    % Display output for the farthest point
    disp('Coordinates of the farthest point are: ');
    disp(num2str(y_max'));
    disp(['Index of the farthest point is ', num2str(idx(2))]);
    disp(['Distance to the farthest point is ', num2str(distance(2))]);

提示

请使用测试脚本将预处理和后处理步骤与实现核心算法的函数分隔开。这种做法使您能够轻松地重用您的算法。您需要为实现核心算法的 MATLAB 函数生成代码。不需要为测试脚本生成代码。

MATLAB 函数生成 C 代码

使用 MATLAB Coder 为函数 euclidean 生成 C 源代码。您在此步骤中生成的代码只接受具有固定预分配大小的输入。

运行原始 MATLAB 代码

运行测试脚本 euclidean_test.m。输出显示 yidxdistance

Coordinates of the closest point are: 
0.8         0.8         0.4
Index of the closest point is 171
Distance to the closest point is 0.080374


Coordinates of the farthest point are: 
0  0  1
Index of the farthest point is 6
Distance to the farthest point is 1.2923

准备 MATLAB 代码以进行代码生成

MATLAB 编辑器中的代码分析器会在您输入代码时持续检查代码。它会报告问题,并提出修改建议,以最大程度地提高性能和可维护性。

  1. 在 MATLAB 编辑器中打开 euclidean.m。MATLAB 编辑器右上角的代码分析器消息指示标记为绿色。分析器未在代码中检测到错误、警告或改进机会。

  2. 在函数声明后面添加 %#codegen 指令:

    function [y,idx,distance] = euclidean(x,cb) %#codegen
    %#codegen 指令提示代码分析器识别特定于代码生成的警告和错误。

    代码分析器消息指示标记变为红色,表示它检测到代码生成问题。

    Code Analyzer window containing sample code, showing red indicator and underlining corresponding to detected code generation issues

  3. 要查看警告消息,请指向带下划线的代码片段。警告指示,代码生成要求先对变量 idxdistance 进行完全定义,然后才可以对它们进行下标索引。出现这些警告是因为代码生成器在变量首次在代码中出现时会确定其大小。要解决此问题,请使用 ones 函数同时分配和初始化这些数组。

    idx = ones(1,2);
    distance = ones(1,2)*norm(x-cb(:,1));

    代码分析器显示“未发现错误”图标,表示它没有再检测到代码生成问题。

    Code Analyzer window containing sample code, showing green indicator

    有关使用代码分析器的详细信息,请参阅使用代码分析器检查代码中的错误和警告

  4. 保存文件。

    现在您即可使用 MATLAB Coder 编译您的代码。

注意

MATLAB Coder 中,编译 MATLAB 代码指从 MATLAB 代码生成 C/C++ 代码。在其他情况下,“编译”指 C/C++ 编译器的操作。

打开 MATLAB Coder 并创建工程文件

要使用 MATLAB Coder 生成代码,您必须首先创建一个扩展名为 .coderprjMATLAB Coder 工程文件。此文件包含您提供给代码生成器的信息,包括 MATLAB Coder 入口函数的路径、其输入类型、全局变量以及代码生成配置参数。

  1. 在 MATLAB 工具条中,在 App 选项卡中的代码生成下,点击 MATLAB Coder。此 App 将打开“创建 MATLAB Coder 工程”对话框。

  2. 提供工程文件的名称以及您要在其中放置该文件的文件夹。对于此示例,在当前工作文件夹中创建一个名为 euclidean.coderprj 的文件。

MATLAB Coder 工具条选项卡将打开。在该选项卡中,每个部分对应于生成代码时执行的不同步骤,包括为 C/C++ 代码生成准备您的 MATLAB 代码、生成代码、对生成的代码执行验证、查看代码生成报告以及导出生成的代码。

Screenshot of the MATLAB Coder toolstrip.

MATLAB Coder 面板也会打开。后续步骤部分显示消息以指导您完成代码生成步骤,输入输出部分摘要显示代码生成的输入和输出。

Screenshot of the MATLAB Coder panel.

指定 MATLAB 入口函数

入口函数是顶层 MATLAB 函数,您可以从中生成代码。对于此示例,您的入口函数是 euclidean

MATLAB Coder 面板的后续步骤部分指示您必须向空工程添加入口函数。

要添加入口函数,请执行以下操作:

  1. 在 MATLAB Coder 面板的输入部分中点击添加入口函数按钮,或在工具条中点击入口函数按钮。

  2. 入口函数选项卡将打开。输入您的入口函数的名称 euclidean

App 对入口函数运行代码生成就绪工具。此工具会筛查 MATLAB 代码中是否存在代码生成不支持的功能和函数。如果 App 发现入口函数或其依赖项之一存在问题,它会在入口函数选项卡中显示警告消息。点击消息旁边的链接以在单独窗口中打开代码生成就绪工具,您可以在其中查看并修复问题。在此示例中,App 在 euclidean 函数中未检测到代码生成就绪问题。有关详细信息,请参阅代码生成就绪工具

注意

代码分析器和代码生成就绪工具可能无法检测到所有代码生成问题。消除这两个工具检测到的错误或警告后,请使用 MATLAB Coder 生成代码,以确定您的 MATLAB 代码是否存在其他合规性问题。

某些支持 C/C++ 代码生成的 MATLAB 函数和对象具有特定的代码生成限制。这些限制和相关使用说明位于其对应参考页的扩展功能部分。有关详细信息,请参阅 C/C++ 代码生成支持的函数和对象

定义输入类型

由于 C/C++ 使用静态定型,代码生成器必须在代码生成时(也称为编译时)确定 MATLAB 文件中变量的类、大小和其他属性(如复/实性)。因此,您必须指定所有入口函数输入的属性。要指定输入属性,您可以:

  • 通过以下方式指示 App 自动确定输入属性:

    • 提供使用样本输入调用入口函数的脚本。

    • 在命令行窗口中执行使用样本输入执行入口函数的命令。

  • 直接指定属性。

在此示例中,要定义输入 xcb 的属性,请指定测试文件 euclidean_test.m,代码生成器使用该文件来自动定义类型:

  1. 入口函数选项卡的顶部,将自动定义输入类型参数设置为使用脚本

  2. 输入测试文件 euclidean_test.m 并点击运行按钮。

测试文件 euclidean_test.m 会使用预期的输入类型调用入口函数 euclidean。App 确定输入 xdouble 类型的 3×1 数组,输入 cbdouble 类型的 3×216 数组。

Screenshot of Entry Points pane with entry-point function and input types defined.

生成并运行 MEX 函数

运行生成的 MEX 工具条按钮从您的入口函数生成 MEX 函数,运行 MEX 函数并报告问题。MEX 函数是可从 MATLAB 内部调用的生成的代码。执行此步骤是一个很好的做法,因为您可以检测并解决在生成的独立 C/C++ 代码中更难诊断出来的运行时错误。默认情况下,MEX 函数包括内存完整性检查。这些检查执行数组边界和维度检查,还检测为 MATLAB 函数生成的代码中是否存在内存完整性冲突问题。有关详细信息,请参阅Control Run-Time Checks

  1. MATLAB Coder 工具条选项卡中,打开运行生成的 MEX 下拉菜单。

  2. 指定使用示例输入调用入口函数的测试文件。对于此示例,请使用您用来定义输入类型的测试文件 euclidean_test.m

App 将生成一个 MEX 函数。它运行测试脚本 euclidean_test,将对 euclidean 的调用替换为对生成的 MEX 的调用。如果 App 在 MEX 函数生成或执行过程中检测到问题,它将在命令行窗口中提供警告和错误消息。您可以点击这些消息,导航到有问题的代码并修复问题。在此示例中,App 未检测到问题,并在代码生成面板的输出部分中显示此消息。

Screenshot of output pane saying that the test file ran without errors.

提示

在从您的 MATLAB 代码生成独立的 C/C++ 代码之前,请生成 MEX 函数。在某些情况下,代码生成器可能会生成与 MATLAB 具有不同数值行为的代码。因此,一个最好的做法是运行生成的 MEX 函数,并确保它具有与您的 MATLAB 函数相同的运行时行为。如果生成的 MEX 函数的执行结果不同于 MATLAB 的结果,或者出错,您必须先修复这些问题,然后生成独立的代码。否则,您生成的独立代码可能不可靠并且具有未定义的行为。

生成 C 源代码

  1. 在工具条的 MATLAB Coder 选项卡的准备部分中,确保输出类型设置为静态库(.lib)。使用其他代码配置设置的默认值。

  2. 点击工具条中的生成代码按钮。

    MATLAB Coderwork\codegen\lib\euclidean 文件夹中为您的工程生成 C 源文件,其中 work 是包含您的教程文件的文件夹。输出部分指示代码生成成功,并显示指向生成的输出文件夹和代码生成报告的链接。

    Output pane saying code generation is successful.

  3. 点击代码生成报告以在报告查看器中查看报告。如果代码生成器在代码生成过程中检测到错误或警告,报告将说明问题并提供有问题的 MATLAB 代码的链接。有关详细信息,请参阅Code Generation Reports

点击生成代码按钮仅生成源代码,并不编译源代码以创建二进制文件。要在一个步骤中同时生成源代码并编译进制文件,请打开生成代码下拉菜单并选择生成代码并编译选项。另外,您可以选择生成 MEX 函数或其他 C/C++ 编译类型,而不是生成 C 静态库。MEX 和 C/C++ 编译类型可使用不同代码配置设置。当您在 MEX 和 C/C++ 代码生成之间切换时,请确认您选择的设置。

为了将 MATLAB 代码转换为高效的 C/C++ 代码,代码生成器引入了优化,在某些情况下,这会导致生成的代码与原始 MATLAB 代码在行为上有所不同。请参阅生成的代码和 MATLAB 代码之间的差异

将生成的 C 代码与原始 MATLAB 代码进行比较

要将生成的 C 代码与原始 MATLAB 代码进行比较,请在 MATLAB 编辑器中打开 C 文件 euclidean.ceuclidean.m 文件。

euclidean.c 文件中,生成的入口函数的签名为:

void euclidean(const double x[3], const double cb[648], double y_min[3], double
               y_max[3], double idx[2], double distance[2])

const double x[3] 对应于您的 MATLAB 代码中的输入 xx 的大小为 3,对应于您在从 MATLAB 代码生成代码时使用的示例输入的总大小 (3×1)。

const double cb[648] 对应于您的 MATLAB 代码中的输入 cbcb 的大小为 648,对应于您在从 MATLAB 代码生成代码时使用的示例输入的总大小 (3×216)。在本例中,生成的代码使用一维数组来表示 MATLAB 代码中的二维数组。

生成的代码有四个额外的输入参量:数组 y_miny_maxidxdistance。这些数组用于返回输出值。它们对应于原始 MATLAB 代码中的输出参量 y_miny_maxidxdistance

代码生成器将保留您的函数名称和注释。如果可能,代码生成器会保留您的变量名称。

注意

如果您的 MATLAB 代码中的某个变量设置为常量值,它在生成的 C 代码中将不会显示为变量。在这种情况下,生成的 C 代码将包含该变量的实际值。

使用 Embedded Coder®,您可以在 MATLAB 代码和生成的 C/C++ 代码之间进行交互式追溯。请参阅Interactively Trace Between MATLAB Code and Generated C/C++ Code (Embedded Coder)

为可变大小输入生成 C 代码

euclidean.c 中的 C 函数只能接受与您在代码生成期间指定的样本输入具有相同大小的输入。但是,对应的 MATLAB 函数的输入数组可以具有任意大小。

假设您希望生成的 C 代码中的 xcb 的维度具有以下属性:

  • xcb 的第一个维度的大小为可变大小,但不超过 3

  • x 的第二个维度具有固定大小,其值为 1

  • cb 的第二个维度的大小为可变大小,但不超过 216

要指定这些输入属性,请执行以下操作:

  1. 入口函数选项卡中,在自动定义输入类型下输入测试文件 euclidean_test.m 并点击运行按钮。测试文件使用预期的输入类型调用入口函数 euclidean。App 确定输入 xdouble 3 x 1,输入 cbdouble 3 x 216。这些类型指定固定大小的输入。

  2. euclidean 函数对应的部分下,在第三列中点击大小设定。下拉菜单将打开,您可以使用它来编辑输入的大小。您可以通过使用 : 前缀来指定可变大小,最高不超过指定的限制。例如,:3 表示对应维度具有可变大小,但不超过 3。将 x 的类型更改为 double :3 x 1,将 cb 的类型更改为 double :3 x :216

Define variable-size inputs for the euclidean function.

按照与以前相同的步骤生成代码。euclidean.c 中生成的 C 代码的函数签名现在为:

void euclidean(const double x_data[], const int x_size[1], const double cb_data[],
               const int cb_size[2], double y_min_data[], int y_min_size[1],
               double y_max_data[], int y_max_size[1], double idx[2], double
               distance[2])
参量 x_datacb_datay_min_datay_max_data 对应于原始 MATLAB 函数中的输入参量 xcb 以及输出参量 y_miny_max。现在,C 函数接受四个额外的输入参量,即 x_sizecb_sizey_min_sizey_max_size,它们在运行时指定 x_datacb_datay_min_datay_max_data 的大小。

另请参阅

主题