本节书摘来自华章出版社《信息物理融合系统(CPS)设计、建模与仿真——基于 Ptolemy II 平台》一书中的第2章,第2.1节,作者:[美]爱德华·阿什福德·李(Edward Ashford Lee),更多章节内容可以访问云栖社区“华章计算机”公众号查看
第2章 图形化建模
本章主要介绍如何使用Ptolemy图形用户界面(Graphical User Interface,GUI)Vergil建立Ptolemy II仿真模型的教程。图2-1展示了在Vergil中建立的一个简单Ptolemy II模型。该模型显示在图形编辑器中,这是Ptolemy II中多种可选的输入机制之一。例如,也可在Java或XML中定义模型。
图2-1 Vergil 窗口的例子以及运行模型的结果窗口
2.1开始
执行本章示例需安装Ptolemy II。一旦安装,你将要调用Vergil,它将显示图2-2所示的初始欢迎窗口。“Tour of Ptolemy II”链接将带你进入图2-3所示页面。
图2-2 初始欢迎窗口
图2-3 Tour of Ptolemy II的页面
2.1.1 信号处理模型执行范例
进入“Tour of Ptolemy II”页面,在“Basic Modeling Capabilities”下列出的第一个例子(频谱分析)是图2-1所示的模型。该模型创建了一个正弦信号,加载正弦载波,添加噪声,然后估计功率谱。可通过工具栏中的run按钮(指向右边的蓝色三角形)执行该模型。如图2-1所示,两个信号图将在窗口显示。右图显示功率谱,左图显示时域信号。注意4个峰值,这表明是正弦调制。可通过双击图2-1窗口右上角附近的框图中的参数来调节信号和载波的频率,以及噪声的数量。
表达式(Expression)角色的图标(图形块)显示了表达式的计算:
表达式中的标识符——signal、carrier以及noise指的是输入端口。表达式(Expression)角色是灵活的,它可以有任意数量的输入端口(可以有任意的名称),它使用了丰富的表达式语言来表示输入与输出之间的函数。它还可以进行模型参数的描述,这些表述式含在模型中。(表达式语言将在第13章详述。)
右击并选择[Documentation→Get Documentation]显示该角色说明文档。图2-4显示了Expression角色说明文档。
图2-4 显示角色说明文档
图2-1中的3个角色都是复合角色(composite actor),这意味着它们的实现本身就是一个Ptolemy II模型。你可以调用Open Actor快捷菜单来查看Signal Source角色的具体实现,如图2-5所示。该图说明如何生成正弦信号。
图2-5 在复合角色上调用 Open Actor 来查看其具体实现
2.1.2 模型的创建和运行
通过选择菜单栏中的[File→New→Graph Editor]来创建一个新模型。如图2-6所示窗口。页面左侧显示了组件库,组件可以拖到模型建立区中。通过执行以下步骤创建一个简单的模型。
打开Actors库,然后打开Sources。在Sources→Generic下找到Const角色并且拖动一个实例到建模区中。
打开Sinks→GenericSinks并且拖动一个display角色到页面中(见2.1.3节)。
从Const角色右边的输出端口拖曳出一个连接,连到Display角色的输入端口。
打开Directors(指示器)库并且拖曳SDF Director到页面。指示器进行模型的功能控制,比如模型将要执行多少次迭代(默认情况下,只有一次迭代)。
完成以上步骤后将得到如图2-7所示的模型。在该模型中,Const角色将创建一个字符串,Display角色将显示这个字符串。通过双击或右击Const角色图标设置字符串变量为“Hello World”,并且选择[Customize→Configure],将显示图2-8所示的对话框。输入字符串的value(参数)值"Hello World"(用引号标记)并单击Commit(提交)按钮。(引号标记保证表达式将表示为一个字符串。)如果运行该模型,窗口将显示文本“Hello World”。
图2-6 一个空的Vergil 图形编辑器
图2-7 Hello World示例
图2-8 Const参数编辑器
如果用File(文件)菜单来保存模型,Ptolemy II模型的文件名可以“.xml”或“.moml”作后缀以便Vergil在下一次可以打开文件。
2.1.3 建立连接
上面构造的模型包含了两个角色和一个连接。移动一个角色(通过单击或拖动),连接就将会自动重新规划路径。我们现在还可以探索如何建立和操作更复杂的连接。
首先,在一个新的图形编辑器中建立一个模型,该图形编辑器包括一个SDF指示器、一个Ramp角色(在Sources→SequenceSources库中)、一个Display角色和一个Sequence-
Plotter角色(在Sinks→SequenceSinks库中),如图2-9所示。
假设Ramp角色的输出连接到Display角色和SequencePlotter角色。在这种情况下,图中需要有一个明确的关系。关系(relation)指的是一个分流器(splitter),它可以连接两个以上的端口。如图2-11所示,图中它由黑色菱形来表示。黑色菱形可以由以下几种方式创建:
如图2-10所示,拖动一个连接的末端到一个已存在连线的中间。
在背景上使用Control键+ 单击,关系就会出现。
如图2-11所示,单击工具栏上的黑色菱形按钮。
注意如果在关系上直接单击和拖动,被选择和移动的关系将不会建立连接。当单击和拖动关系时,需要按住control键才能建立连接。
图2-11 通过单击工具栏中的黑色菱形亦可建立关系
补充阅读:信号源
3个Actors→Sources库中包含的信号源如下所示。
Const 通过参数设置(见第13章)来产生一个值(或表达式)。
StringConst 产生字符串和引用其他参数(见13.2.3节)。
Subscriber 输出从Publisher接收的数据。
SubscriptionAggregator 使用指定的操作(目前为加法或乘法)把来自多个Publisher的数据整合。
CurrentTime、CurrentMicrostep、DiscreteClock和PoissonClock是第7章所描述的计时源。
TriggeredSinewave和Sinewave根据当前时间或特定的采样率分别产生正弦输出。
InteractiveShell将执行阶段打开的Shell窗口的输入值输出。
Interpolator和Pulse两者都产生波形,一个通过插值来产生,另一个通过在不同值间填充零得到。
Ramp 产生一个稳步增加或减少的输出序列。
Sequence 产生任意值序列。
SketchedSource 产生一个可用鼠标指定序列。
补充阅读:Sinks库
在Actors→Sinks库中的角色作为信号的目的地。大多数是绘图仪,将在第17章中描述。少数其他的包含在Sinks→GenericSinks库中,如下所示。这些接收器在它们的输入端口接收任何数据类型。
Discard 丢弃所有输入。在大多数域中,断开输出具有相同效果(会话Rendezvous域是个例外)。
当模型执行时,Display在新的文本窗口中进行输入值的显示。
MonitorValue在显示模型的Vergil图标中显示它的输入。
Publisher给Subscriber和 SubscriptionAggregator的实例建立已被命名的连接(见2.1.3节)。
Recorder内部存储输入值,定制需要访问这些值的Java代码。
SetVariable设置定义在模型中的变量和参数的值。因为这个变量可以不被另一个没有连接到SetVariable的角色所读取,SetVariable可能导致模型的非确定性。即模型执行的结果可能取决于调度决策,它决定了SetVariable是否在角色读取受影响的变量之前执行。为了降低该风险,SetVariable有一个称为delayed(延迟)的参数,在默认情况下它设置为真。当它为真时,受影响的变量只会设置在指示器当前迭代的末端。对于大多数指示器(值得注意的是,除了PN或会话外),这个设置将保证确定性行为。SetVariable角色有一个输出端口,它可以保证其他指定角色能在SetVariable执行后才开始执行。即使delayed(延迟)设置成假,这种方法也能消除不确定性。
在图2-11所示的模型中,关系(Relation)用于将单个端口的输出信号广播到其他角色。这个单端口处仍仅一条连接——连接到关系。
即使目前建立的是一个简单的图来说,但布置图标以及控制端口和关系之间的连线,依然是很乏味的。幸运的是,PtolemyII 包含了一个智能的自动布局工具,你可以在菜单命令[Graph→Automatic Layout] 中找到它(或者Ctrl+T调用)。该工具是由Sp?nemann et al. (2009)提供。然而,偶尔也需要手动调整布局。为了具体地控制连接的路径,你可以沿着一个连接使用多重关系,并且独立地控制每一个连接的位置。用于单个连接的多重关系称为关系组(relation group)。在关系组中,关系的连接顺序是没有意义的。
Ptolemy II 支持两种类型的端口,采用实心和空心三角形来表示。实心三角形指定单输入(或单输出)端口,而空心三角形允许多个输入(或输出)。例如,Ramp角色的输出端是一个单端口(single port),而Display和SequencePlotter角色的输入端是多端口(multiport)。在多端口中,每个连接都被视作一个单独的信道(channel)。
从库中选择另一个信号源并将它添加到模型中。连接这个新增的源到SequencePlotter或者Display,从而实现到这些块的多端口输入。然而,并不是所有数据类型的输入都可以被接受。比如SequencePlottercan只接受double类型或者能无损地转换成double类型的输入,如int类型。在下一节中,将讨论数据类型以及它们在Ptolemy II模型中的使用。