同步系统¶
Modelica语言3.3版引入了一个新功能以解决有关非确定性离散行为的问题[Elmqvist] 。本节将先介绍这些问题在3.3版本之前的表现。然后,我们将用例子展示这些新特性能如何帮助解决这类问题。
先考虑下列模型:
model IndependentSampling "Sampling independently"
Real x "Sampled at 10Hz via one method";
Real y "Sampled at 10Hz via another method";
Real e "Error between x and y";
Real next_time "Next sample for y";
equation
when sample(0,0.1) then
x = time;
end when;
when {initial(), time>pre(next_time)} then
y = time;
next_time = pre(next_time)+0.1;
end when;
e = x-y;
end IndependentSampling;
倘若你仔细观察,你会发现x
和y
都在离散的时间点上被计算。此外,两变量的采样时点都是在仿真开始时以及之后的每0.1秒。但问题是,它们真的一样吗?为了更容易解决这个问题,我们加入变量e
以计算两者间的差。

对模型进行仿真,我们会得到如下的x
、y
轨迹。当然,两个轨迹看上去是一样的。但要真正确定它们之间是否存在任何不同,我们绘制了误差值e
:

现在,让我们考虑一下模型:
model SynchronizedSampling "A simple way to synchronize sampling"
Integer tick "A clock counter";
Real x, y;
Real e "Error between x and y";
equation
when sample(0,0.1) then
tick = pre(tick)+1;
end when;
when change(tick) then
x = time;
end when;
when change(tick) then
y = time;
end when;
e = x-y;
end SynchronizedSampling;
在这里,我们设置了一个共同的信号以触发两个变量的赋值操作。通过这种方式,我们可以确定,当tick
信号变为真时,无论是x
还是y
都将被赋予一个值。显然,运行这个模型后我们可以看到,误差永远为零:

在这样的做法里,每个信号都是基于一个共同的“节拍”(或时钟)进行取样的。这是一个很好的避免确定性问题的方式。但是,如果你有两个不同频率的信号,且已知在特定的时点两者会被同时取样,那么情况又是如何呢?请考虑如下例子:
model SubsamplingWithIntegers "Use integers to implement subsampling"
Integer tick "Clock counter";
Real x, y, z;
equation
when sample(0,0.1) then
tick = pre(tick)+1;
end when;
when change(tick) then
x = time;
end when;
when change(tick) then
y = time;
end when;
when mod(tick-1,2)==0 then
z = time;
end when;
end SubsamplingWithIntegers;
在这种情况下,变量tick
是计数器。每次此变量改变时,我们会更新x
和y
的值。所以,直到这一点都是与先前的模型相同的。然而,我们增加了第三个信号z
。而该信号仅在当tick
值是为奇时才会进行采样。所以相比之下,x
和y
的采样次数为其的两倍。但我们可以肯定的是,每次z
更新时,x
和y
是在完全相同的时点进行更新。对模型进行仿真我们会得到一下结果:

这是Modelica语言在3.3版本之前所采用的方法。但3.3版本引入了一些新的功能以帮助我们表述这些情况。
请考虑以下模型:
model SamplingWithClocks "Using clocks to sub and super sample"
Real x, y, z, w;
equation
x = sample(time, Clock(1,10));
y = sample(time, Clock(1,10));
z = subSample(x, 2);
w = superSample(x, 3);
end SamplingWithClocks;
现在我们不再使用when
语句。相反,我们会使用一个增强版的sample
(取样)函数。这个sample
函数的首个参数是待取样的表达式,第二个参数则是取样的时间间隔。让我们逐行阅读并讨论下面的代码。首先,我们有:
x = sample(time, Clock(1,10));
注意,我们已经不再使用0.1
,而且不再能看到表示为实数值的时钟间隔。相反,我们使用Clock
表达式为x
定义一个有理数值的时钟间隔。这一点很重要,因为这种做法允许我们对不同时钟进行精确比较。让我们看下一行:
y = sample(time, Clock(1,10));
再一次,我们看到在时钟间隔的有理表示。这意味着在实践中,Modelica语言编译器可以确信这两个时钟x
与y
是相同的。因为两者均是用可确切比较的整数量进行定义的。也就是说,在执行仿真时,我们可以确信这两个时钟会同时触发。
如果我们想创建一个正好是x
两倍慢的时钟,那么我们可以使用subSample
运算符来完成。在z
的定义里我们可以看到这种情况:
z = subSample(x, 2);
Modelica语言编译器可以在幕后对这些时钟间的关系进行推理。编译器知道每隔\(\frac{1}{10}\)秒时钟x
就会触发。因此Modelica语言编译器可以使用subSample
运算符所提供的信息推论出,每隔\(\frac{2}{10}\)秒时钟z
就会触发。理论上,这意味着z
也可以被定义为:
z = sample(time, Clock(2,10));
但通过使用subSample
运算符相对x
去进行对z
的定义,我们可以确保无论x
是如何定义的,z
的触发频率总是x
的一半。
类似地,我们可以使用superSample
运算符定义另一个触发频率为x
3倍的时钟w
。
w = superSample(x, 3);
同样,我们可以直接使用sample
以对w
进行如下定义:
w = sample(time, Clock(1,30));
但使用superSample
我们可以确保w
的采样速度总是x
的三倍、z
的六倍(因为z
也是基于x
定义的)。
Modelica语言的同步时钟特性是相对较新。因此,并非所有的Modelica语言编译器都支持这些特性。要了解更多有关这些同步功能及其应用,可以参考[Elmqvist]以及/或者3.3版以后的Modelica规范。
[Elmqvist] | (1, 2) “Fundamentals of Synchronous Control in Modelica”, Hilding Elmqvist, Martin Otter and Sven-Erik Mattsson http://www.ep.liu.se/ecp/076/001/ecp12076001.pdf |