番外篇(1)模块次序表、代数环及其检测算法

文章目录

  • 模块次序表
  • 直通模块与端点模块
  • 代数环简介
  • 模块次序表需要注意的其它细节
    • 缺少一次更新的加法器
    • 直通模块交叉1:DFS的问题
    • 直通模块交叉2:BFS的问题
    • 重排算法与代数环检测
    • 其它容易出错的例子
    • 总结
  • 建立模块次序表与检测代数环
  • 可参考的MATLAB文档

模块次序表

  各个模块的先后计算顺序(或者称作模块的更新)要遵循一定的规则,比方说下面这个最简单的例子:
在这里插入图片描述
3个模块从左到右分别是输入模块、放大器、输出模块,更新时必须按从左到右的顺序更新;
番外篇(1)模块次序表、代数环及其检测算法_第1张图片
而上面这个例子中,虽然也必须从左到右,但上下两路的更新顺序可以交换。由于用户在使用该库时很难考虑到模块的更新顺序,因此在库代码中需要根据模块的连接方式确定模块的更新顺序,这就需要建立一个表,称作“模块次序表(Module Sequence Table)”。一个端点模块对应一个次序表,表内存放每个模块的ID,按照模块的更新顺序排列,表头为端点模块。仿真的时候通过这个排序列表进行反向计算,从直通模块运算到端点模块。

直通模块与端点模块

  在上面两个例子中,积分器和输出模块要最后计算,所以被称作端点模块。所有单元模块分为直通模块和端点模块两种,打个比方,端点模块是火车头,直通模块是车厢。各个端点模块均有其特殊性,详细介绍如下。
  把积分器设定为端点模块的原因是它是求解微分方程的核心,它的内部更新函数为空,而在它外面使用四阶龙格库塔法。
  把输出模块设定为端点模块的原因是积分器和输出模块之间可能有其他的直通模块,或者某个仿真中都没有积分器,而一个仿真器中必须至少有一个端点模块才能进行仿真。
  还有一个端点模块是单位延迟器,详细介绍见下一篇文章 单位延迟模块介绍与连续离散混合仿真。
  传输延迟模块没有被设定为端点模块的原因是,端点模块的处理极为复杂,而且在回路中使用传输延迟模块的应用很少,所以为了方便起见,传输延迟模块被暂时设定为直通模块。如果有了相关方面的应用或需求请及时联系我修改。
  图3所示的系统对应的微分方程为
x ( t ) = k ( u ( t ) − x ( t − τ ) ) x(t)=k(u(t)-x(t-\tau)) x(t)=k(u(t)x(tτ))
对应的传递函数为
G ( s ) = k 1 + k e − τ s G(s)=\frac{k}{1+k\text{e}^{-\tau s}} G(s)=1+keτsk
对于图3的简单情况,如果输入是常数或是离散的阶梯函数,可以将传输延迟模块替换成单位延迟模块并将采样周期设置为 τ \tau τ,结果相同。如果有输入函数较为复杂或者模型复杂导致不能这样替换的话我再考虑修改。
  端点模块的另一个作用就是打破一个形成代数环的回路,在之前的例程介绍中简单提到了代数环(见 simucpp系列教程(二)例程解析(第一部分)中的代数环),一个回路中必须至少有一个端点模块,否则无法确定这个回路中各个模块的更新顺序,就会报代数环错误。代数环的详细介绍见下文。

代数环简介

代数环_百度百科
  “在数字计算机仿真中,当输入信号直接取决于输出信号,同时输出信号也直接取决于输入信号时,由于数字计算的时序性,而出现的由于没有输入无法计算输出,没有输出也无法得到输入的“ 死锁环” ,称之为代数环。”
  原理很好理解,下面给几个是或者不是代数环的例子。
番外篇(1)模块次序表、代数环及其检测算法_第2张图片
上面这个图很常见,它不是代数环;
番外篇(1)模块次序表、代数环及其检测算法_第3张图片
上面这个图就形成代数环了,把积分器模块换成了(单输入单输出)函数模块。
番外篇(1)模块次序表、代数环及其检测算法_第4张图片
上面这个图也是,可能simulink里不是,但在simucpp里为了方便起见我判定它是。那个图标表示传输延迟模块,注意与单位延迟模块区分。简单来说就是,在一个环路中必须有端点模块,否则这个环路就是一个代数环。
番外篇(1)模块次序表、代数环及其检测算法_第5张图片

图1

注意这里的环路必须是沿着箭头方向形成一个环,而图1就不算一个环,更不是代数环。

模块次序表需要注意的其它细节

缺少一次更新的加法器

番外篇(1)模块次序表、代数环及其检测算法_第6张图片
  对于上图中的情况,模块sum2跟随sum1接到了积分器后面,这就导致积分器当前步长的计算值并没有在sum2中更新,输出模块接受的值仍然是积分器上一个步长的计算值。而且仍然不能仅仅将sum2接到输出模块后面,因为另一个接在sum1和sum2之间的输出模块会产生相同的结果。目前使用的方法是在仿真的最后一步再更新一遍端点模块及与之相连的直通模块。这也是为什么会有Simulate_FinalStep()的原因。在这个函数里需要对各个模块再更新最后一次,在需要单步仿真的场合需要特别注意。

直通模块交叉1:DFS的问题

番外篇(1)模块次序表、代数环及其检测算法_第7张图片

图2

对于图2中的情况,DFS为87512364,即模块更新顺序为46321578,3在6之前,结果不对。BFS为87563412,结果正确。看似BFS没问题,实际上不是。看下一个例子。

直通模块交叉2:BFS的问题

番外篇(1)模块次序表、代数环及其检测算法_第8张图片

图3

对于图3中的情况,正确的更新顺序是12345,但是,如果在代码中先连接gain1和sum1,再连接gain2和sum1,模块次序表按照广度优先搜索给出的更新顺序是 13245,深度优先搜索给出的更新顺序是 31245,3都在2的前面。这种情况下假设输入为 cos ⁡ ( t ) \cos(t) cos(t),则 t = 0 t=0 t=0时保存的结果就是0。这种情况不管使用DFS还是BFS结果都一样。因此不管用DFS还是BFS,都要在建表时进行额外调整。

重排算法与代数环检测

  当出现重复模块时有两种情况,一种情况是代数环,而另一种就是前面提到的直通模块交叉(暂时没遇到可能的第三种情况)。检测代数环需要再进行一次BFS,不然会漏掉一些情况(这个情况我没能及时记下来)。如果没有出现代数环,那就说明出现了直通模块交叉,需要使用重排算法。我目前用的是BFS,建表顺序为54231,应调整为54321。重排算法为,将该重复模块从之前的位置删除,并加入现在的位置。
  以图1为例,BFS为5437612,在1处出现第一个重复,将其删除并重新添加,调整为5437621。图2中BFS为87563412虽然正确,但出现了重复,仍然调整为87564123。图3中将54231调整为54321。

其它容易出错的例子

就当是记个笔记,考验一下自己或同行的代数环检测算法。
番外篇(1)模块次序表、代数环及其检测算法_第9张图片
上图中的2是直通模块或者端点模块,分别代表是或者不是代数环。
最后说一下图1,并不是说检测出重复就算代数环,图1出现了重复但不是。

总结

计算顺序确定的基本原则是在保持模块特性的前提下不违背模块的依赖关系。体现在7点:

  • 直通模块(具有直通性的模块)可直接计算;
  • 多输入模块输出等其输入更新完再计算;
  • 有延迟特性的模块输出值计算要考虑次序;
  • 有内部更新状态的模块需要更新其状态;
  • 信号源模块可优先计算;
  • 有控制信号模块要满足控制依赖关系;
  • 输入模块或输出模块在模块化计算顺序和层次化计算顺序表现不同。

建立模块次序表与检测代数环

  综合考虑前面的各种情况,下面介绍使用广度优先搜索建立模块次序表和检测代数环的方法。simulink中有完善的解决代数环的方法,而目前在simucpp中只是简单的报错提示。
  具体检测算法是,从一个端点模块出发,使用广度优先搜索算法搜索它的每一个孩子模块(输入模块),每搜索到一个模块到就排在该端点模块的次序表后面,直到搜索到自己、其它端点模块或没有孩子模块时停止;如果在检测过程中发现一个模块已经位于次序表中,此时就需要检测是不是代数环,如果这个模块是直通模块那就形成了代数环,如果是端点模块就不是代数环。
  建表步骤如下

  1. 搜索下一个模块并加入所有模块统计表,跳过端点模块,没有下一个模块时结束;
  2. 检测该模块是否出现在统计表中,出现则前往3,没出现前往1;
  3. 从这个重复模块开始重新运行BFS,依然跳过端点模块,如果又搜索到了该重复模块的父亲模块,则认为出现了代数环,程序终止,否则继续;
  4. 对模块进行重排,并返回1。

可参考的MATLAB文档

Control and Display Execution Order

你可能感兴趣的:(simucpp系列教程,算法,simulink,微分方程)