文章目录
前言
见《【研发日记】Matlab/Simulink技能解锁(一)------在Simulink编辑窗口Debug》
见《【研发日记】Matlab/Simulink技能解锁(二)------在Function编辑窗口Debug》
见《【研发日记】Matlab/Simulink技能解锁(三)------在Stateflow编辑窗口Debug》
见《【研发日记】Matlab/Simulink技能解锁(四)------在Simulink Debugger窗口调试》
见《【研发日记】Matlab/Simulink技能解锁(五)------七个Simulink布线技巧》
串行架构
如果Simulink模型中只有一条信号流,把各个模块串联了起来,那么各模块执行顺序就是信号流经的顺序。这是一种最常规的执行顺序,对于开发人员是比较基础的,所以本文不再赘述。
并行架构
如果Simulink模型中有两条并行的信号流,那么在其中任何一条信号流内部仍然是上面的串行顺序。两条信号流之间没有依赖关系,他们的先后顺序是自动分配的,对软件的功能也影响不大。如果自己的软件中有要求,也可以通过设定模块优先级,人为调整他们的先后顺序,示例如下:
Tips:人为设定的优先级如果跟软件运行的信号依赖关系相冲突的话,Simulink就会自动忽略设定的优先级。
上述模型编译出来的代码,执行顺序与上图一致,代码如下:
cpp
/* Model step function */
void ExecutionOrdre_step(void)
{
int32_T rtb_PulseGenerator;
/* DiscretePulseGenerator: '<Root>/Pulse Generator1' */
rtb_PulseGenerator = ((ExecutionOrdre_DW.clockTickCounter < 50) &&
(ExecutionOrdre_DW.clockTickCounter >= 0));
if (ExecutionOrdre_DW.clockTickCounter >= 999) {
ExecutionOrdre_DW.clockTickCounter = 0;
} else {
ExecutionOrdre_DW.clockTickCounter++;
}
/* End of DiscretePulseGenerator: '<Root>/Pulse Generator1' */
/* Outport: '<Root>/Out2' incorporates:
* Bias: '<Root>/Bias1'
* Gain: '<Root>/Gain1'
*/
ExecutionOrdre_Y.Out2 = 10.0 * (real_T)rtb_PulseGenerator + 6.0;
/* DiscretePulseGenerator: '<Root>/Pulse Generator' */
rtb_PulseGenerator = ((ExecutionOrdre_DW.clockTickCounter_c < 50) &&
(ExecutionOrdre_DW.clockTickCounter_c >= 0));
if (ExecutionOrdre_DW.clockTickCounter_c >= 999) {
ExecutionOrdre_DW.clockTickCounter_c = 0;
} else {
ExecutionOrdre_DW.clockTickCounter_c++;
}
/* End of DiscretePulseGenerator: '<Root>/Pulse Generator' */
/* Outport: '<Root>/Out1' incorporates:
* Bias: '<Root>/Bias'
* Gain: '<Root>/Gain'
*/
ExecutionOrdre_Y.Out1 = 5.0 * (real_T)rtb_PulseGenerator + 2.5;
}
Tips:查看执行顺序,除了使用上述的"执行顺序查看其",编译生成的C代码,还可以使用Simulink Debuger窗口,参见《【研发日记】Matlab/Simulink技能解锁(四)------在Simulink Debugger窗口调试》
并行模型还有一种情况是通过Mux、Signal Bus这类模块捆绑在一起的虚拟总线,他们的执行顺序跟上面的并行模型类似,示例如下:
Tip1:为什么有的模块上面不标顺序?因为那是虚拟模块并不执行,或者有的是合并到相邻的模块中一起执行了。
Tip2:如果将上述虚拟总线改成非虚拟总线,那么执行顺序在总线的地方就不是并行,而是串行了。因为非虚拟总线是以结构体来对待的,作为整体一次处理。
环形架构
如果Simulink模型中有环形的信号流,那么执行顺序通常是从Memory、Delay这样的循环模块上开始的,示例如下:
Tips:把Memory、Delay这样的模块作为软件执行的起点时,它是没有输入值的,所以输出值由模块的初始值设定的。
上述模型编译出来的代码,执行顺序与上图一致,代码如下:
cpp
#include "ExecutionOrdre.h"
#include "ExecutionOrdre_private.h"
/* External inputs (root inport signals with default storage) */
ExtU_ExecutionOrdre_T ExecutionOrdre_U;
/* External outputs (root outports fed by signals with default storage) */
ExtY_ExecutionOrdre_T ExecutionOrdre_Y;
/* Model step function */
void ExecutionOrdre_step(void)
{
/* MATLAB Function: '<Root>/MATLAB Function' incorporates:
* Inport: '<Root>/In2'
* Memory: '<Root>/Memory'
*/
ExecutionOrdre_Y.Out5 += ExecutionOrdre_U.In2;
}
另外一种环形模型是代数环,不提倡使用,所以不再介绍。
星形架构
星形模型是指Simulink模型中有Data Store这样的模块,它往往有多个写入和读出模块,并且散落在各个地方。这种模型默认的执行顺序是先执行全部的写入模块,然后是全部的读出模块,最后是Memory模块,示例如下:
上述模型编译出来的代码,执行顺序与上图一致,代码如下:
cpp
/* Model step function */
void ExecutionOrdre_step(void)
{
/* Outport: '<Root>/Out6' incorporates:
* Constant: '<Root>/Constant1'
* DataStoreWrite: '<Root>/Data Store Write'
*/
ExecutionOrdre_Y.Out6 = 2.0;
/* Outport: '<Root>/Out7' incorporates:
* Constant: '<Root>/Constant1'
* DataStoreWrite: '<Root>/Data Store Write'
*/
ExecutionOrdre_Y.Out7 = 2.0;
}
Tip1:多个写入模块,最后一个执行的会覆盖前面写入的数据。如果想要修改写入顺序,可以通过模块优先级来调整
Tip2:也有特殊的情况不是按照默认顺序执行的,会引发一些预想不到的Bug,参见《研发日记,Matlab/Simulink避坑指南(一)------Data Store Memory模块执行时序Bug》。
嵌套架构
嵌套模型是指Simulink模型中有层级嵌套的子系统。常见的虚拟子系统不会影响执行顺序,所开可以把它看成透明容器来对待。另外的使能子系统和while子系统是真正意义上的子系统(Atomic子系统),他们是嵌套在上一级模型的之中的一个单独执行的整体,示例如下:
上述模型编译出来的代码,执行顺序与上图一致,代码如下:
cpp
/* Model step function */
void ExecutionOrdre_step(void)
{
real_T rtb_Gain4_c;
int32_T rtb_PulseGenerator5;
/* DiscretePulseGenerator: '<Root>/Pulse Generator4' */
rtb_PulseGenerator5 = ((ExecutionOrdre_DW.clockTickCounter < 50) &&
(ExecutionOrdre_DW.clockTickCounter >= 0));
if (ExecutionOrdre_DW.clockTickCounter >= 999) {
ExecutionOrdre_DW.clockTickCounter = 0;
} else {
ExecutionOrdre_DW.clockTickCounter++;
}
/* End of DiscretePulseGenerator: '<Root>/Pulse Generator4' */
/* Outport: '<Root>/Out8' incorporates:
* Bias: '<Root>/Bias4'
* Gain: '<Root>/Gain4'
*/
ExecutionOrdre_Y.Out8 = 5.0 * (real_T)rtb_PulseGenerator5 + 2.5;
/* Outputs for Atomic SubSystem: '<Root>/Atomic Subsystem' */
/* DiscretePulseGenerator: '<S1>/Pulse Generator4' */
rtb_PulseGenerator5 = ((ExecutionOrdre_DW.clockTickCounter_m < 50) &&
(ExecutionOrdre_DW.clockTickCounter_m >= 0));
if (ExecutionOrdre_DW.clockTickCounter_m >= 999) {
ExecutionOrdre_DW.clockTickCounter_m = 0;
} else {
ExecutionOrdre_DW.clockTickCounter_m++;
}
/* End of DiscretePulseGenerator: '<S1>/Pulse Generator4' */
/* Gain: '<S1>/Gain4' */
rtb_Gain4_c = 3.1415926535897931 * (real_T)rtb_PulseGenerator5;
/* DiscretePulseGenerator: '<S1>/Pulse Generator5' */
rtb_PulseGenerator5 = ((ExecutionOrdre_DW.clockTickCounter_c < 50) &&
(ExecutionOrdre_DW.clockTickCounter_c >= 0));
if (ExecutionOrdre_DW.clockTickCounter_c >= 999) {
ExecutionOrdre_DW.clockTickCounter_c = 0;
} else {
ExecutionOrdre_DW.clockTickCounter_c++;
}
/* End of DiscretePulseGenerator: '<S1>/Pulse Generator5' */
/* Outport: '<Root>/Out9' incorporates:
* Bias: '<S1>/Bias4'
*/
ExecutionOrdre_Y.Out9 = rtb_Gain4_c + 100.0;
/* Outport: '<Root>/Out10' incorporates:
* Bias: '<S1>/Bias5'
* Gain: '<S1>/Gain5'
*/
ExecutionOrdre_Y.Out10 = 6.2831853071795862 * (real_T)rtb_PulseGenerator5 +
200.0;
/* End of Outputs for SubSystem: '<Root>/Atomic Subsystem' */
/* DiscretePulseGenerator: '<Root>/Pulse Generator5' */
rtb_PulseGenerator5 = ((ExecutionOrdre_DW.clockTickCounter_a < 50) &&
(ExecutionOrdre_DW.clockTickCounter_a >= 0));
if (ExecutionOrdre_DW.clockTickCounter_a >= 999) {
ExecutionOrdre_DW.clockTickCounter_a = 0;
} else {
ExecutionOrdre_DW.clockTickCounter_a++;
}
/* End of DiscretePulseGenerator: '<Root>/Pulse Generator5' */
/* Outport: '<Root>/Out11' incorporates:
* Bias: '<Root>/Bias5'
* Gain: '<Root>/Gain5'
*/
ExecutionOrdre_Y.Out11 = 10.0 * (real_T)rtb_PulseGenerator5 + 6.0;
}
Stateflow架构
如果Simulink模型中有stateflow ,就不能使用上文的"执行顺序查看器"标注循序了,需要使用Sequence Viewer模块,参考《【研发日记】Matlab/Simulink技能解锁(三)------在Stateflow编辑窗口Debug》。
分析和应用
上文六种Simulink架构,在模型设计开发中的应用非常广泛,尤其是应用于大型项目的开发时作用非常明显,例如一些整车控制软件,车路协同项目等等。项目中把这些架构的执行顺序烂熟于心后,不仅能为算法开发搭建出更好的架构,也能有利于软件异常问题的分析和定位,大大提高Bug查找速度,减少开发时间投入。这些Simulink模型架构主要适用于软件开发工程中,早期模型搭建和原型机调试验证的阶段。当软件开发进入后期升级迭代阶段时,也可用于装车阶段出现问题的分析排查。
总结
以上就是本人在研发中使用Simulink设计模型架构时,一些个人理解和分析的总结,主要介绍了几种模型架构的使执行顺序,并分析了这几种架构的特点和适用场景。
后续还会分享另外几个最近解锁的Matlab/Simulink新技能,欢迎评论区留言、点赞、收藏和关注,这些鼓励和支持都将成文本人持续分享的动力。
另外,上述例程使用的Demo工程,可以到笔者的主页查找和下载。
版权声明,原创文章,转载和引用请注明出处和链接,侵权必究!