分享

MBD的Simulink使用技巧③:虚拟子系统与原子子系统的代码生成

 伊伊爸 2022-08-20 发布于湖北
全文约2500字,你将看到以下内容:
  • 虚拟子系统的代码生成

  • 原子子系统的代码生成

  • 结束语
autoMBD最近发布了《autoMBD原创技术文章合集》

合集包含156页丰富的MBD入门基础
和MBDT硬件支持包的使用
还包含基于MBD的电机控制算法开源项目——AMBD-MC
合集配备了丰富的视频讲解

和大量的模型、文档和软件资源

如何获取请参考@所有读者:autoMBD发布《autoMBD原创技术文章合集》

本篇文章继续介绍Simulink生成代码相关的内容,之前的内容可查看往期文章:

特别提示:在本篇文章中使用到的虚拟子系统模型、原子子系统模型,可以在autoMBD资源库的“临时资源分享”文件夹中找到该模型(资源序号tA24)。资源库链接的获取可以在《autoMBD原创技术文章合集》中找到(见文章开头)。文末有活动福利!!

1  虚拟子系统的代码生成

实际中,为了使模型更加简洁,我们通常会使用到虚拟子系统(Virtual Subsystem)虚拟子系统表示它只是看起来是子系统,实际上,对于生成代码来说是假的、不存在的。

紧接着上一篇文章中的PI控制器模型,可以为模型中的PI控制器部分构建一个虚拟子系统,并为子系统命名为PI_Controller,如下图所示:

图片

框选PI控制器模块 - From autoMBD

图片

构建PI控制器虚拟子系统 - From autoMBD
此外,虚拟子系统还可以通过Subsystem模块来构建虚拟子系统:

图片

虚拟子系统模块 - From autoMBD
构建的虚拟子系统PI_Controller,不影响原模型的任何功能,不影响原模型的执行顺序,仅仅只是视觉上的子系统。子系统模型内部如下图所示:

图片

模型的内部信号 - From autoMBD

具体地,虚拟子系统的输入、输出端口(即上图中的Err端口和PI_Ctrl端口是“假的”;与输入、输出端口连接的传递方向第一根信号线即上图中的Err信号线和统外部输出的PI_Ctrl信号线也是“假的”

“假的”意味着不会生成任何代码,修改这些端口和信号线的名称、属性不会影响代码的生成。点击“Generate Code”生成新的代码:

图片

生成代码 - From autoMBD

选中上述的“假”端口或“假”信号线,可以在Code面板中看到提示:“xxx is a virtual block. Code is not generated for virtual blocks.”

2  原子子系统的代码生成

与虚拟子系统相对应的是原子子系统(Atomic Subsystem)。

虚拟子系统如上述所说,不影响代码生成;但子子系统可以通过配置相关属性,为子系统内部的模型生成单独的函数接口,可以生成inline函数或可重用函数,自定义函数接口形式、函数名称,甚至可以生成单独的源文件

所以,生成代码的过程中,一定要注意区分原子子系统与虚拟子系统

原子子系统的与虚拟子系统外观非常相似,但仔细观察可以发现,原子子系统的边框更加粗,如下图所示:

图片

原子子系统和虚拟子系统 - From autoMBD

需要特别强调的是,所有的触发子系统都是原子子系统,例如上图中的Enable and Triggered Subsystem、If Action Subsystem等,都是原子子系统。

虚拟子系统和原子子系统可以相互转换。但触发子系统不能转换为虚拟子系统,并且在虚拟子系统中添加触发模块后,虚拟子系统会自动转换为原子子系统。

下面是原子子系统的实践。

在PI控制器示例模型中,选择刚刚创建的虚拟子系统,在工具栏中出现“SUBSYSTEM BLOCK”标签,进入SUBSYSTEM BLOCK,可以看到一个“Is Atomic Subsystem”的按钮,点击该按钮即可将虚拟子系统转换为原子子系统,如下图所示:

图片

虚拟子系统转换为原子子系统 - From autoMBD

当然也可以通过“Atomic Subsystem”模块直接创建原子子系统,这里不再赘述。

可以通过对子系统右键单击,来查看原子子系统和虚拟子系统的模块参数,具体对比它们的区别,如下图所示:

图片

查看子系统模块参数的方法 - From autoMBD

图片

查看子系统模块参数:Main - From autoMBD

图片

查看子系统模块参数:Code Generation- From autoMBD

上图中,左边窗口是虚拟子系统的模块参数,右边窗口是原子子系统的模块参数。

可以看到,在Main标签中,原子子系统多了两项参数:

  • 最小化代数环
  • 采样时间

对于代数环,是因为原子子模型会改变模型的执行顺序,这可能会产生代数环。原子子系统可以使用与模型其他部分不同的采样时间。

在Code Generation配置页面中,虚拟子系统是不可编辑的状态,而原子子系统是可以编辑的。

默认情况下,原子子系统的Function packaging的参数设置为“Auto”。设置为“Auto”,那么该原子子系统的代码生成方式和虚拟子系统是相同的。除了Auto,Function packaging还可以设置为:

  • Auto(默认)
  • Inline
  • Nonreusable function
  • Reusable function

对于Inline的代码生成方式,实际上Auto和虚拟子系统选择的就是Inline的代码生成方式,Inline的方式具体为,子系统内的模型会嵌在上层模型中,是上层模型代码的一部分。

对于不可复用函数(Noresuable function)和可复用函数(Reusable function)的代码生成方式,该配置会为原子子系统内的模型生成单独的函数,并且上层模型生成的代码是通过函数调用的方式使用原子子系统内的算法

它们的区别从名称也能看出来,前者生成不可复用的函数,后者生成可复用的函数。什么是可复用和不可复用?可以简单地理解:

  • 可复用函数只进行数学运算,它所需的数据全部来自参数输入,只要输入参数相同,不管多少次调用,它的输出结果就是确定不变的,所以是可复用的;
  • 不可复用函数也进行数学运算,但除了输入参数(有时候甚至没有输入参数),它还依赖一些其他外部数据(例如全局变量),这些数据其他函数也能修改,所以多次调用不可复用函数,不能保证相同参数情况下返回相同的结果,因为外部数据可能在不同的调用之间被其他函数修改。

可以看到,不可复用函数会产生代码之间的耦合,而可复用函数没有这种耦合。但并不是说不可复用函数就不好,可复用函数也有缺点,它会占用更多的内存,这在嵌入式中尤为敏感。

总的来说可复用函数和不可复用函数有各种的应用场景,要根据实际情况具体分析,选择恰当的形式。

Tips:以我的经验来看,嵌入式领域手写代码的开发中,除了软件库,不可复用函数还更多一些。

在本文的PI控制器示例模型中,以原子子系统的可复用函数代码生成为例,配置Code Generation如下图所示:

图片

配置原子子系统可复用函数Code Generation- From autoMBD

点击“Generate Code”,可以看到两个新生成的子系统.c/.h文件

图片

两个新生成的子系统.c/.h文件- From autoMBD

可以看到这两个源文件文件名与原子子系统的模块名一致(所以要规范建模)。它们是独立于整个系统模型的,所以可复用,用户在自己的工程中包含这两个文件,即可使用原子子系统内的模型算法了。

打开“PI_Controller.c”文件,可以查看原子子模型内的PI控制器所生成的代码:

/* 原子子模型生成的可复用函数 代码生成于“PI_Controller.c” *//* Output and update for atomic system: '<Root>/PI_Controller' */real_T autoMBD_example_P_PI_Controller(real_T rtu_Err, DW_PI_Controller_autoMBD_exam_T *localDW){ real_T rty_PI_Ctrl_0;
/* Sum: '<S1>/Sum1' incorporates: * DiscreteIntegrator: '<S1>/Discrete-Time Integrator' * Gain: '<S1>/Kp' */ rty_PI_Ctrl_0 = 2.0 * rtu_Err + localDW->DiscreteTimeIntegrator_DSTATE;
/* Update for DiscreteIntegrator: '<S1>/Discrete-Time Integrator' incorporates: * Gain: '<S1>/Ki' */ localDW->DiscreteTimeIntegrator_DSTATE += 3.0 * rtu_Err * 0.001; return rty_PI_Ctrl_0;}
可以看到生成的可复用函数为autoMBD_example_P_PI_Controller(),用户也可以指定其函数名。函数的输入参数为误差Err和离散积分模块的状态变量,状态变量是通过输入参数传入,从而实现了函数复用。
查看Step函数,可以观察原子子系统可复用函数的调用过程:
/* Step函数中对原子子系统可复用函数的调用过程 代码生成于“模型名.c”*//* Model step function */void autoMBD_example_PI_aSubs_step(void){  /* Outputs for Atomic SubSystem: '<Root>/PI_Controller' */
/* Outport: '<Root>/PI_Ctrl' incorporates: * Inport: '<Root>/Feedback' * Inport: '<Root>/Req_Ctrl' * Sum: '<Root>/Sum2' */  /* 调用autoMBD_example_P_PI_Controller()执行PI控制器算法 */ autoMBD_example_PI_aSubs_Y.PI_Ctrl = autoMBD_example_P_PI_Controller (autoMBD_example_PI_aSubs_U.Req_Ctrl - autoMBD_example_PI_aSubs_U.Feedback, &autoMBD_example_PI_aSubs_DW.PI_Controller);
/* End of Outputs for SubSystem: '<Root>/PI_Controller' */
对比上一篇文章中没有子系统模型的Step函数,原子子系统模型生成可复用函数后,模型的Step函数得到了极大的简化,剩下一行代码语句了(代码中是通过换行将代码拆成了三行,实际上是一行代码语句)。

对于原子子系统不可复用函数的代码生成,以及其他配置选项,由于过程是类似的,读者可以自行探索、体验,这里不再赘述。

3  结束语

为了收集读者对《autoMBD原创技术文章合集》的反馈,特地举行反馈征集活动。在8月31日之前,所有读者可以向作者反馈阅读和使用《autoMBD原创技术文章合集》的好评、差评、想法、感受或建,参与到活动中。

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

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多