Simulink 模型验证之持续集成
作者 David Boissy、Paul Urban、Krishna Balasubramanian、Pablo Romero Cumbreras、Colin Branch 和 Jemima Pulipati,MathWorks
如果您使用的是 R2022a 或更高版本,并且拥有 Simulink Check™ 许可证,则可以使用 CI/CD Automation for Simulink Check 支持包。通过支持包,您可以为您的团队定义一个流程模型,并设置您的 CI 系统以自动将流程中的任务作为 CI 中的管道运行。支持包包括对多个 CI 平台的支持,以便您可以自动生成特定于您的项目和流程的管道,无需手动更新。
主要特点:
- 内置任务:通过使用内置任务来自动执行基于模型的开发和验证工作流程中的常见任务,例如使用 Model Advisor 检查建模标准、使用 Simulink Test™ 运行测试以及使用 Embedded Coder® 生成代码。您无需管理开发和验证活动的各种脚本,而是可以重新配置和使用这些内置任务来实现一致的执行和有组织的任务结果。
- 增量构建:识别更改对项目的影响并自动仅重新运行受影响的任务以帮助提高构建效率。Process Advisor 应用程序及其构建系统可以重新运行具有过时结果的任务,并自动跳过最新的任务。
- 自动管道生成:使用支持包的模板文件自动生成管道,这样您在对项目或流程进行更改时无需手动更新 CI/CD 配置文件。您可以轻松自定义管道生成器的设置,将任务分成不同的作业,并行运行模型任务,并更改其他管道行为。
有关详细信息,请参阅持续集成和集成到 Jenkins。
此文章分为两部分。本文为其中的上篇。第 1 部分介绍如何使用 GitLab® 进行版本控制,以及如何使用 Jenkins®进行持续集成(CI)。第 2 部分,使用 GitLab 进行持续集成以验证 Simulink 模型介绍如何使用 GitLab 进行版本控制和 CI。
持续集成 (CI) 越来越受欢迎,并且正在成为基于模型设计不可或缺的一部分。但什么是 CI?它有哪些优点?它试图解决什么问题?Simulink® 在 CI 生态系统中扮演什么角色?如何在您的项目中充分利用 CI?
如果您熟悉基于模型的设计,但对 CI 很陌生,那么您可能会问自己这些问题。在这篇技术文章中,我们会探讨一个常见的 CI 工作流,并将其应用于基于模型的设计中。然后,我们将使用 Jenkins、GitLab 和 Simulink Test 来演示该工作流程的示例。
此示例中使用的工程可以直接在此下载。
什么是 CI?
CI 是一种敏捷方法的最佳实践。采用该实践时,开发人员会定期将源代码变更提交并合并到一个中央仓库中。然后这些“变更集”会自动构建、验证和发布。图 1 展示了这个基本的 CI 工作流以及开发工作流。
在工作流的开发部分,模型和测试会经过开发、测试、合并、评审,然后提交到开发人员桌面端的版本控制系统。然后,版本控制系统会触发工作流的自动化 CI 部分。CI 工作流的关键部分包括:
构建:源代码和模型变成了目标文件和可执行文件。
测试:测试是作为质量阀而执行的。
打包:将可执行文件、文档、工件以及其他可交付项包装在一起,以交付给最终用户。
部署:将软件包部署到生产环境中。
这四个步骤合在一起被称为 CI 管道。这种管道通常是自动化的。根据系统的不同,管道可能需要几分钟到几天的时间来完成。值得注意的是,这些步骤会创建大量工件,例如材料清单、测试结果和报告。
CI 工作流通常与版本控制系统相关的开发人员工作流搭配使用。在这些工作流中,开发人员通常将其变更保存在本地仓库中,并在部署之前使用本地 CI 管道来认证变更。
CI 有哪些优点?
实施了 CI 的团队通常会列举以下优点:
- 可重复性:CI 管道为构建、测试、打包和部署提供了一致且可重复的自动化流程。可重复的自动化使开发人员可以专注于必要的工作,并节省在项目上投入的时间。这也是降低风险的一个重要方面,通常是认证的必要条件。
- 质量保证:手动测试虽然有效,但是它通常基于几天前的快照,并且缺乏可重复性。使用 CI 时,变更的测试使用的总是最新的代码库。
- 缩短开发时间:可重复的流程加上内置的质量保证可以更快地交付高质量的产品。自动化部署意味着您的代码随时可以用于生产环境。
- 改进协作:使用 CI,开发人员有明确的流程来管理变更集,并将代码合并到生产线中。一致的流程使管理大型团队成为可能,并可降低培训新开发人员的成本。
- 审计就绪的代码:CI 工作流提供了详尽的审计跟踪。对于通过 CI 管道的每一个变更,都可以确定是谁做出了变更、谁审核了变更、变更的性质、依赖关系、测试及其结果,以及在此过程中生成的任何数量的相关报告和交付物。
基于模型的设计如何融入 CI?
在设计上,CI 工作流和工具是语言和领域中立的。这意味着挑战在于让基于模型的设计贯穿 CI 工具、系统和流程,换句话说,就是让 Simulink 和相关工具成为 CI 工作流程的通用语。
这可以通过将基于模型的设计的三个关键组成部分集成到 CI 工作流中来完成:验证、代码生成和测试(图 2)。基于模型的设计强调早期验证,这在 CI 管道中体现为“构建”阶段之前的“验证”阶段。代码生成发生在“构建”阶段。生成代码的静态分析以及通过对其仿真来完成的动态测试在“测试”阶段完成。
以下概述了我们如何教会 CI 工作流使用基于模型的设计的语言:
开发:MATLAB®、Simulink、代码生成器和工具箱用于开发活动。使用 MATLAB 工程组织工作、与他人协作并与版本控制系统交互。
测试:Simulink Check 用于在仿真和代码生成之前执行模型质量检查。使用 Simulink Test 开发、管理和执行基于仿真的测试。使用 Simulink Coverage™ 测量覆盖率和评估测试有效性。质量检查、测试结果和覆盖率指标又可进一步用作开发人员认证其工作的质量阀。
合并:使用 MATLAB 的“比较文件和文件夹”功能比较和合并 MATLAB 文件。使用模型比较工具比较和合并 Simulink 模型。
评审:评审是将变更提交到版本控制系统之前质量过程的最后一步。对 MATLAB 脚本和 Simulink 模型的变更将在这步进行评审。作为提交前的最后质量阀,来自预认证的测试结果也会进行评审。
提交:MATLAB 工程提供了与版本控制系统集成的接口。
验证:之前用作本地验证工具的 Simulink Check 也会被用于 CI 系统内的自动验证。
构建:MATLAB Coder™、Simulink Coder™ 和 Embedder Coder 用于生成软件在环 (SIL) 测试的代码。
测试:用于本地测试的工具 Simulink Test 也用于 CI 系统内的自动化测试。
打包和部署:打包是指将可执行文件、文档、交付物以及其他可交付项包装在一起,以交付给最终用户。部署是指发布打包的软件。在基于模型的设计工作流中,这些阶段在不同的组织和团队中有很大差异,但通常涉及将不同的版本和认证工件打包到一个可交付给其他团队的产品中。
现代开发工具和实践使开发人员能够更早地创建更稳健的系统和测试功能并将其快速迭代。当 CI 系统集成到工作流后,单元级测试和系统级测试将自动执行。这意味着开发人员可以专注于开发新功能,而不是验证功能是否已正确集成。
以下案例研究描述了结合 CI 和基于模型的设计的工作流。
案例研究:在 CI 系统中验证、构建和测试的 Simulink 模型
在此示例中,我们使用基于模型的设计和 CI 对汽车车道跟随系统执行基于需求的测试(图 3)。
每次 Jenkins 构建模型,我们将使用的管道(图 4)都会执行。
图 4. 车道跟随示例管道。
管道中的各个阶段如下:
- 验证标准合规性:MATLAB 单元脚本运行简单的模型顾问检查。该评估标会准确保模型没有未连接的线。
- 构建模型:MATLAB 单元测试文件为我们的模型构建产品级 SIL 代码。如果构建成功且没有警告,则为通过评估标准。
- 执行测试用例:Simulink Test 中的一个测试套件使用多个驾驶场景来测试车道跟随控制器。使用三个评估标准来验证控制器的运行是否令人满意:
- 防撞:在驾驶场景中,自主车辆在任何时候都不会与前车发生碰撞。
- 安全距离保持:自主车辆和前车之间的时间间隔超过 1.5 秒。两辆车之间的时间间隔定义为算出的车头距与自主车辆速度的比率。
- 车道跟随:车道中心线横向偏差在 0.2 米以内。
- 打包工件:前面的每个阶段都会生成工件,包括一份模型顾问报告、一个生成的可执行文件,以及一组可存档供将来使用或参考的测试结果。
工作流步骤
工作流包括以下步骤(图 5):
- 触发 Jenkins 构建,并观察验证和构建阶段是否通过。
- 检测到 Jenkins 测试用例失败。
- 在桌面端 MATLAB 上重现该问题。
- 通过放宽评估标准来修复模型中的问题。
- 在本地运行测试,确保测试用例通过。
- 合并和评审测试分支上的变更。
- 提交更改至 Git™ 并在 Jenkins 中触发构建。
- 在 Jenkins 中验证、构建和测试。
左上角显示了我们第一次失败的 CI 循环。它显示了 CI 测试失败、本地重现、标准放宽以及 CI 工作流的成功完成。
工作流详细信息
- 我们首先通过选择 Build Now(立即构建)在 Jenkins 中触发构建。Simulink Check 检查和代码生成成功通过。

- 接下来我们在第二个验证阶段检测到测试用例失败。测试套件
LaneFollowingTestScenarios
中的测试用例LFACC_Curve_CutInOut_TooClose
未通过评估标准。

- 为了更好地了解失败原因,我们使用 Simulink Test 在本地将其重现。我们打开测试文件
LaneFollowingTestScenarios.mldatx
并运行测试案例LFACC_Curve_CutInOut_TooClose
。请注意,它未达到“安全距离”评估标准。也就是说,我们需要更灵活地设定前车与自主车辆之间的时间间隔。

- 了解问题后,我们现在修复问题。打开
LaneFollowingTestBenchExample.slx
模型并导航至名为 Collision Detection/Test Assessments 的 Test Sequence 模块。第一个评估断言:自主车辆和先导车辆之间的时间间隔低于 1.5 秒的时长每次不应超过 2 秒。
GlobalAssessments % 确保自主车辆和先导车辆之间的时间间隔小于 % 1.5 秒的时长每次不超过 2 秒。 verify(duration(time_gap < 1.5, sec) < 2); % 确认未检测到碰撞 verify(~collision); % 验证与车道中心线的横向偏差绝对值超过 0.2m 的 % 时长每次不超过 5 秒。 verify(duration(abs(lateral_deviation) > 0.2, sec) < 5);
该评估对于测试使用的激进驾驶操作来说过于严格。为了本例的目的,我们放宽了评估标准,以确保时间间隔小于 0.8 秒的时长每次不超过 5 秒。
GlobalAssessments % 确保自主车辆和先导车辆之间的时间间隔小于 % 0.8 秒的时长每次不超过 5 秒。 verify(duration(time_gap < 0.8, sec) < 5); % 确认未检测到碰撞 verify(~collision); % 验证与车道中心线的横向偏差绝对值超过 0.2m 的 % 时长每次不超过 5 秒。 verify(duration(abs(lateral_deviation) > 0.2, sec) < 5);
- 该问题在我们的仿真中似乎得到了修复。为了确认,我们进行本地测试,既保存模型并在测试管理器中重新运行测试。请注意,它通过了新的评估标准。

- 我们已经修复问题并在本地进行了验证。现在,我们使用模型比较工具来评审变更,然后再将其提交到版本控制。

我们还可以使用模型比较工具的发布功能来评审代码。

- 修复错误后,我们使用 MATLAB 工程将这些变更推送到 GitLab,并添加一条提交消息来记录对评估标准的变更。
随后我们注意到最新的变更已提交至 GitLab。
GitLab 会自动触发 Jenkins 构建。Jenkins 工程控制板显示构建状态和进度。

- Jenkins 构建运行。现在验证、构建和测试管道阶段已可通过。
我们现在可以发起合并请求,将测试分支中的更改合并到主分支中。在 GitLab 中,在Repository(存储库)下,我们选择 Branches(分支),然后点击位于测试分支上的最新提交旁边的Merge request(合并请求)。
我们填写表单并提交合并请求。
作为分支的所有者,可以点击 Merge(合并)按钮接受合并请求。所有变更现已合并至主分支。

使用示例:工具、资源和需求
以下部分概述了可帮助您入门的资源、所需的工具及其配置方法。
配置系统
Jenkins 是我们的 CI 系统,GitLab 是我们的版本控制系统。MATLAB、Jenkins 和 GitLab 必须得配置后才能协同工作。以下教程可提供设置方面的帮助。
这些教程专门针对 GitLab 和 Jenkins,但其中的概念也适用于其他版本控制和 CI 系统。
所需工具
本示例需要以下工具:
- Jenkins 安装版本 2.7.3 或更高。使用 Jenkins 进行持续集成。
- MATLAB Plugin for Jenkins 1.0.3 或更高版本。MATLAB、Simulink 和 Simulink Test 都利用此插件与 Jenkins 进行通信。在 GitHub 上了解更多信息。
- 所需的其他插件:
- GitLab 帐户。使用 GitLab 作为源代码管理系统。GitLab 还提供云版本。MATLAB 工程包括用于与 GitLab 通信的 Git 接口。
CI 的许可注意事项
如果您计划在大量主机或云上执行 CI,请联系 continuous-integration@mathworks.com 寻求帮助。注意:MATLAB 和 Simulink 编码器和编译器产品等转换产品可能需要客户端访问许可证 (CAL)。
附录:配置 MATLAB、GitLab 和 Jenkins
步骤 1:配置 MATLAB 工程以使用源代码管理
我们示例中的第一步是配置我们的工程以使用 GitLab 的源代码管理。
- 创建名为的新目录
MBDExampleWithGitAndJenkins
,将示例加载到其中,然后打开 MATLAB 工程MBDExampleWithGitAndJenkins.prj
。 - 在 GitLab 中,创建将作为远程仓库的新工程。将其命名为
MBDExampleWithGitAndJenkins
,并记录托管它的 URL。 - 在 MATLAB 中,转换工程以使用源代码控制。在 Project(工程)选项卡上,点击 Use Source Control(使用源代码管理)。

点击 Add Project to Source Control(将工程添加到源代码管理)。

- 点击 Convert(转换)。

- 完成后点击 Open Project(打开工程)。

该工程现已处于本地 Git 源代码管理下。
步骤 2:提交变更并将本地仓库推送到 GitLab
- 在 Project(工程)选项卡上,点击 Remote(远程库)。

- 指定 GitLab 中的远程源的 URL。

点击 Validate(验证)以确保与远程仓库的连接成功,然后点击 OK(确定)。该工程现已配置为使用 GitLab 推送和拉取变更。
- 点击 Commit(提交)以执行初始提交。


- 点击 Push(推送),将所有变更从本地仓库推送到远程 GitLab 仓库。

- 刷新 GitLab 控制板并查看 MATLAB 工程的内容。
步骤 3. 创建测试分支
在此步骤中,我们会创建一个测试分支,用于在与主分支合并之前测试和验证变更。
- 点击Branches(分支)。

- 展开 Branch and Tag Creation(分支和标签创建)部分,将分支命名为 Test,然后点击 Create(创建)。
- 在分支浏览器中查看 Test。在测试分支中,单击 Switch(切换),然后单击 Close(关闭)。
- 在 MATLAB 中,选择 Push(推送)将这些更改推送到 GitLab 并观察 GitLab 中的测试分支。
- 配置构建触发器,当向 GitLab 中的测试分支发出推送请求时运行构建。在 Build Triggers(构建触发器) 部分中,选择 Advanced > secret token(高级 > 机密令牌)。GitLab 使用此令牌请求构建并向 Jenkins 进行身份验证。记下机密令牌和 GitLab webhook。
- 配置构建环境。选择 Use MATLAB version(使用 MATLAB 版本),然后输入 MATLAB 根目录。
- 配置构建步骤。
点击 Add build step(添加构建步骤),然后选择 Run MATLAB Command(运行 MATLAB 命令)。输入命令 openProject('SltestLaneFollowingExample.prj');
LaneFollowingExecModelAdvisor
打开项目并运行模型顾问检查。
点击 Add build step(添加构建步骤),然后再次选择 Run MATLAB Command(运行 MATLAB 命令)。输入命令:openProject('SltestLaneFollowingExample.prj');
LaneFollowingExecControllerBuild.
点击 Add build step(添加构建步骤)并选择 Run MATLAB Tests(运行 MATLAB 测试)。选择 TAP test results(TAP 测试结果)和 Cobertura code coverage(Cobertura 代码覆盖率)完成构建配置。
此操作将解析 TAP 测试结果,并且在选中 TAP Extended Test Results(TAP 扩展测试结果)后显示这些结果。输出包含已执行测试用例的概览、结果摘要和来自 MATLAB 控制台的日志。

TAP 插件还收集最新测试执行的结果,并显示如下所示的运行状况图表。您可以通过点击该图表访问以前的任何构建。

步骤 6:发布 HTML 报告
点击 Add post-build action(添加构建后操作)> Publish HTML Results(发布 HTML 结果)。输入将发布 HTML 报告的相对根路径,以及该路径中索引页的文件名。
添加与要发布的 HTML 报告一样多的条目。在此场景中,有两个 Web 报告:模型顾问摘要报告和代码生成报告。这些是使用 MATLAB 内置函数创建的标准报告。您可以添加自定义 HTML 报告。
Jenkins 任务主页面上的每个 HTML 报告链接都指向最新构建的报告。如果您勾选了 Always link to last build(始终链接到上次构建)下的复选框 Publishing options(发布选项),无论构建状态如何,插件都会发布最后一次构建的报告。如果未激活该复选框,该插件将只链接到最新的“成功”构建。
步骤 7:配置 GitLab 以触发 Jenkins 执行构建
配置 GitLab,当主分支上发生新的推送时触发 Jenkins 中的自动构建。为此,请导航至Settings > Webhook(设置 > Webhook)。使用在配置构建触发器时由 Jenkins 提供的 Webhook URL 和机密令牌,然后选择Push events(推送事件)。
注意:在 URL 部分使用完全限定的域名代替 localhost,以便 GitLab 可以找到 Jenkins 安装。

在 Test(测试)下拉框中,选择 Push Events(推送事件)以测试集成。GitLab 应显示消息 Hook executed successfully:HTTP 200,并且 Jenkins 将启动构建。

步骤 8:配置 Jenkins 通过 GitLab 身份验证
要在 GitLab 上自动发布 Jenkins 构建状态,必须配置 Jenkins 使其请求能够通过 GitLab 的身份验证。
- 在 GitLab 上创建个人访问令牌,并选中 API 作用域。

- 复制令牌并在 Jenkins Configure System 下创建一个 GitLab 连接。
注意:连接可以在多个 Jenkins 任务上重复使用。如果用户至少具有 maintainer(维护者)权限,则可以全局配置。
步骤 9:将 Jenkins 集成到 GitLab 管道中
要将 Jenkins 集成到 GitLab 管道中,您必须在 Jenkins 中配置 GitLab 连接,并将任务状态发布到 GitLab。
- 在 Jenkins 任务的 General(常规)部分选择 GitLab Connection(GitLab 连接)。
- 添加一个构建后操作,将构建状态发布到 GitLab。
注意:此操作没有参数并将使用现有的 GitLab 连接在 GitLab 上发布构建状态。它还会为每个提交和合并请求创建双向可追溯性。

步骤 10:可视化基于需求的测试指标 (R2020b)
基于需求的测试指标让您可以评估基于需求的测试活动的状态和质量。可以使用模型测试仪表板将测量结果可视化。
- 根据如下所示的函数创建一个名为
collectModelTestingResults.m
的文件。此函数将初始化指标引擎基础架构,并收集所有可用的模型指标。
function collectModelTestingResults() % R2020a 添加的指标功能 if exist('metric') metricIDs = [... "ConditionCoverageBreakdown" "CoverageDataService"... "DecisionCoverageBreakdown" "ExecutionCoverageBreakdown"... "MCDCCoverageBreakdown" "OverallConditionCoverage"... "OverallDecisionCoverage" "OverallExecutionCoverage"... "OverallMCDCCoverage" "RequirementWithTestCase"... "RequirementWithTestCaseDistribution" "RequirementWithTestCasePercentage"... "RequirementsPerTestCase" "RequirementsPerTestCaseDistribution"... "TestCaseStatus" "TestCaseStatusDistribution"... "TestCaseStatusPercentage" "TestCaseTag"... "TestCaseTagDistribution" "TestCaseType"... "TestCaseTypeDistribution" "TestCaseWithRequirement"... "TestCaseWithRequirementDistribution" "TestCaseWithRequirementPercentage"... "TestCasesPerRequirement" "TestCasesPerRequirementDistribution"... ]; % 收集所有指标以便初次处理 collect all metrics for initial reconcile E = metric.Engine(); execute(E, metricIDs); end end
- 将此文件添加到您的工程和路径中。
- 配置 Jenkins 通过调用
new collectModelTestingResults function
两次来收集指标结果。第一次调用会初始化与 Simulink 测试管理器的指标集成。第二个使用导出的 Simulink Test Manager 结果收集度量结果。- 点击 Add build step(添加构建步骤),然后再次选择 Run MATLAB Command(运行 MATLAB 命令)。输入命令:
openProject('SltestLaneFollowingExample.prj'); collectModelTestingResults
将此构建步骤置于 Run MATLAB Tests(运行 MATLAB 测试)构建步骤前。 - 点击 Add build step(添加构建步骤),然后再次选择 Run MATLAB Command(运行 MATLAB 命令)。再次输入命令:
openProject('SltestLaneFollowingExample.prj'); collectModelTestingResults
将此构建步骤置于 Run MATLAB Tests(运行 MATLAB 测试)构建步骤后。
- 点击 Add build step(添加构建步骤),然后再次选择 Run MATLAB Command(运行 MATLAB 命令)。输入命令:
- 在 Run MATLAB Tests(运行 MATLAB 测试)构建步骤中选中 Simulink Test Manager results(Simulink 测试管理器结果)。

- 将度量结果存档在派生目录中。您还必须归档导出的测试管理器结果,因为这将允许在加载回 MATLAB 时对指标结果进行全面导航。
点击 Add post-build action(添加构建后操作),然后选择 Archive the artifacts(归档工件)。输入路径 derived/**,matlabTestArtifacts/*.mldatx
来归档保存到该目录的所有文件。
注意:要在测试机器以外的机器上使用 MATLAB 查看这些结果,请执行以下操作:
- 下载存档的工件(派生目录和测试结果 MLDATX 文件)。
- 提取并复制到用于运行 CI 任务的工程的同一版本的本地副本中。
- 在 MATLAB 中打开该工程并启动模型测试仪表板。
CI 生成的结果将显示在仪表板上。
Jenkins® 是 LF Charities Inc. 的注册商标。
2024 年发布