如何在复杂的Simulink模型中,实现不同逻辑块不同周期的调用?这里的复杂,可以明确为不同的逻辑块,具有不同的运行周期,且它们之间的逻辑运算,存在相互的依赖关系。例如,逻辑块1、逻辑块2、逻辑块3的运行周期分别是10ms、100ms、1000ms,而逻辑块1的输出,是逻辑块2的输入;逻辑块2的输出,是逻辑块3的输入;逻辑块3的输出,又反过来成了逻辑块1的输入。
针对上述问题,目前,我探索出的一种方法是:Stateflow+函数调用撕裂模块+函数调用子系统模块。 即:将不同运行周期的逻辑块,封装在函数调用子系统模块中;而这些模块的调用,则由Stateflow和函数调用撕裂模块来统筹,即通过Stateflow生成不同周期的函数调用信号,再通过函数调用撕裂模块将一个调用信号撕裂成多个具有先后顺序的调用信号,以实现所有子系统模块的统筹运行。
下面,我将阐述探索这个问题的原委,以便梳理自己的思路,加深自己的印象。
这个问题的背景是:控制器内存资源不够,需要从软件方面降低运行时的负载。设想一下这样的应用场景:我们要在一块设计好的单片机开发板上,采用基于模型开发的方式,设计其控制算法,最终实现控制器的开发。刚开始,这个控制器承载的功能较少,相应的控制算法也比较简单。此时,我们采用了一种简单粗暴的方式,将所有的逻辑算法,都封装在一个短周期的Task中,每个周期将所有的逻辑算法都调用一遍。这样做,没有问题。然而,随着这个控制器承载的功能愈来愈多,其控制算法也愈来愈复杂。若仍然采用所有算法统一调用的方式,则会导致控制器负载和内存占用率过高,最终导致控制功能不正常。为了降低负载,我们自然而然地会想到,给控制算法划分优先级。优先级高的算法,仍然封装在短周期的Task中,而优先级低的算法,则封装在长周期的Task中。这样,只有在长、短周期的Task都需要运行的时间段,控制器的负载才是比较高的。从而,在整体上降低了控制器的运行时负载。那这样的分周期调用算法,我们在基于模型开发时,即在Simulink模型中,该如何来模拟,进而实现算法测试呢?
这个测试需求,可以分解为两点:1. 不同算法,设定不同的调度周期;2. 不同算法,有明确的先后运行顺序,包括同周期的算法间和不同周期的算法间。为了实现这个测试需求,我开始了我的探索之路。
首先,搭建一个简单的算法,以辅助验证自己的想法。即:输入+1后给到输出,并将其封装在一个函数调用子系统模块中,同时进行如下设置:1. 输出接口的初始输出值为0;2. 内部function块的采样时间类型为触发。设置好后,通过复制,得到6个函数调用模块,用以模拟不同的算法块。同时,将模型求解器设置为定步长,步长为0.01s。
对于不同的调度周期,我们首先想到的方案,可能就是函数调用生成模块+函数调用撕裂模块。为此,搭建了如下模型。图中,模块T1、T2的采样时间分别为10ms、50ms。运行模型,不会报错。查看模块Fcn1_01、Fcn2_01的输出,发现两者的数据确实分别是10ms、50ms更新一次。需求的第一点满足。再看看两者时刻0的数据,分别为1、2。模块Fcn1_01先于模块Fcn1_03运行,前者的输入采用后者的初始输出值,算出结果为1,没问题。模块Fcn2_01后于模块Fcn1_01运行,前者输入采用后者输出,算出结果为2,这貌似也没问题。但模块Fcn2_01后于模块Fcn1_01运行,这样的先后顺序是确定的吗?
为此,修改模型,如下图所示。再运行模型,也不会报错。查看模块Fcn1_01、Fcn2_01的输出,两者时刻0的数据分别为3、2。分析可知,这次,模块Fcn2_01又先于模块Fcn1_01运行。因此,采用函数调用生成模块+函数调用撕裂模块的方式,不同周期算法块的运行顺序与连接关系有关,不满足需求的第二点。
另外,若连接关系下图所示, 运行模型,会直接报错,显示模块Fcn1_01、Fcn1_03、Fcn2_01的输入在一个环内。这也是模块间的运行先后顺序不明确导致的
为了明确模块运行的先后顺序,尝试方案Stateflow+函数调用子系统模块+函数调用生成模块+函数调用撕裂模块,即借助Stateflow触发事件的先后,来明确函数调用生成的先后。修改模型如下图所示。但事与愿违,这样的用法会直接报错,显示函数调用子系统中的函数调用生成模块不能设置具体的采样周期。
为此,针对报错,优化模型,采用方案Stateflow+函数调用撕裂模块+函数调用子系统模块,即直接用Stateflow触发不同周期的事件,模拟底层软件的任务调用。由于事件的触发有先后顺序,则不同周期算法块的运行先后顺序得以明确。再通过函数调用撕裂模块,同周期算法块的运行先后顺序也得以明确。如此,所有算法块的运行先后顺序都是明确的。修改模型如下图所示(Stateflow中的逻辑很简单,在此不展示)。运行不报错,结果符合预期。NICE!
再审视一下这个模型的运行:Stateflow的运行,与模型求解器设置相关,定步长,0.01s运行一次,从而周期性触发10ms、50ms的事件,相当于C语言中的主调函数;封装在函数调用子系统模块中的算法,其运行与模型求解器解耦,只受Stateflow控制,相当于C语言中的接口函数。至此,有一种同宗同源的感jio。
以上,是整个探索过程。将探索出的方案应用到测试模型中,也一切正常。
最后,希望这篇博文,能够给到有这样应用需求、同时还在探索的朋友一些参考和帮助。欢迎一起探讨。