同步系统

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;

倘若你仔细观察,你会发现xy都在离散的时间点上被计算。此外,两变量的采样时点都是在仿真开始时以及之后的每0.1秒。但问题是,它们真的一样吗?为了更容易解决这个问题,我们加入变量e以计算两者间的差。

../../../_images/SIS.png

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

../../../_images/SIS_e.png

现在,让我们考虑一下模型:

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都将被赋予一个值。显然,运行这个模型后我们可以看到,误差永远为零:

../../../_images/SSS.png

在这样的做法里,每个信号都是基于一个共同的“节拍”(或时钟)进行取样的。这是一个很好的避免确定性问题的方式。但是,如果你有两个不同频率的信号,且已知在特定的时点两者会被同时取样,那么情况又是如何呢?请考虑如下例子:

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是计数器。每次此变量改变时,我们会更新xy的值。所以,直到这一点都是与先前的模型相同的。然而,我们增加了第三个信号z。而该信号仅在当tick值是为奇时才会进行采样。所以相比之下,xy的采样次数为其的两倍。但我们可以肯定的是,每次z更新时,xy是在完全相同的时点进行更新。对模型进行仿真我们会得到一下结果:

../../../_images/SSI.png

这是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语言编译器可以确信这两个时钟xy是相同的。因为两者均是用可确切比较的整数量进行定义的。也就是说,在执行仿真时,我们可以确信这两个时钟会同时触发。

如果我们想创建一个正好是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运算符定义另一个触发频率为x3倍的时钟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