传感器比较

我们会以一个例子开始对架构的研究。本例类似于我以前的书[ItPMwM]里介绍过的另一个例子。在书里,我们将研究控制系统在使用几种不同的传感器模型时的性能。

单层模型

我们的系统结构如下:

Flat system model

All the components within the magenta box represent the plant model. In this case, it is a simple rotational system involving two rotating inertias connected via a spring and a damper. One of the inertias is connected to a rotational ground by an additional damper. The green box identifies the sensor in the system. The sensor is used to measure the speed of one of the rotating shafts. Similarly, the magenta box identifies the actuator. The actuator applies a torque to the other shaft (the one whose speed is not measured). Finally, all the components in the blue box represent the control system, which tries to keep the measured speed as close as possible to the setpoint supplied by the signal generator at the top of the diagram.

我们可以用Modelica代码表示如下:

within ModelicaByExample.Architectures.SensorComparison.Examples;
model FlatSystem "A rotational system with no architecture"
  Modelica.Mechanics.Rotational.Components.Fixed fixed
    annotation (Placement(transformation(extent={{10,-90},{30,-70}})));
  Modelica.Mechanics.Rotational.Components.Inertia inertia(J=0.1)
    annotation (Placement(transformation(extent={{-20,-50},{0,-30}})));
  Modelica.Mechanics.Rotational.Components.Inertia inertia1(J=0.3)
    annotation (Placement(transformation(extent={{40,-50},{60,-30}})));
  Modelica.Mechanics.Rotational.Sources.Torque torque(useSupport=true)
    annotation (Placement(transformation(extent={{-60,-50},{-40,-30}})));
  Modelica.Mechanics.Rotational.Components.SpringDamper springDamper(c=100, d=3)
    annotation (Placement(transformation(extent={{10,-50},{30,-30}})));
  Modelica.Mechanics.Rotational.Components.Damper damper(d=4)
    annotation (Placement(transformation(extent={{40,-80},{60,-60}})));
  Modelica.Mechanics.Rotational.Sensors.SpeedSensor speedSensor annotation (
      Placement(transformation(
        extent={{-10,-10},{10,10}}, rotation=90, origin={70,0})));
  Modelica.Blocks.Math.Feedback feedback annotation (Placement(transformation(
        extent={{10,-10},{-10,10}},
        origin={70,40})));
  Modelica.Blocks.Sources.Trapezoid trapezoid(period=1.0)
    annotation (Placement(transformation(extent={{40,70},{60,90}})));
  Modelica.Blocks.Math.Gain gain(k=20) annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        rotation=180, origin={-30,40})));
equation
  connect(springDamper.flange_a, inertia.flange_b) annotation (Line(
      points={{10,-40},{0,-40}},
      color={0,0,0}, smooth=Smooth.None));
  connect(springDamper.flange_b, inertia1.flange_a) annotation (Line(
      points={{30,-40},{40,-40}},
      color={0,0,0}, smooth=Smooth.None));
  connect(torque.support, fixed.flange) annotation (Line(
      points={{-50,-50},{-50,-70},{20,-70},{20,-80}},
      color={0,0,0}, smooth=Smooth.None));
  connect(damper.flange_b, inertia1.flange_b) annotation (Line(
      points={{60,-70},{70,-70},{70,-40},{60,-40}},
      color={0,0,0}, smooth=Smooth.None));
  connect(damper.flange_a, fixed.flange) annotation (Line(
      points={{40,-70},{20,-70},{20,-80}},
      color={0,0,0}, smooth=Smooth.None));
  connect(torque.flange, inertia.flange_a) annotation (Line(
      points={{-40,-40},{-20,-40}},
      color={0,0,0}, smooth=Smooth.None));
  connect(speedSensor.flange, inertia1.flange_b) annotation (Line(
      points={{70,-10},{70,-40},{60,-40}},
      color={0,0,0}, smooth=Smooth.None));
  connect(feedback.y, gain.u) annotation (Line(
      points={{61,40},{-18,40}},
      color={0,0,127}, smooth=Smooth.None));
  connect(gain.y, torque.tau) annotation (Line(
      points={{-41,40},{-80,40},{-80,-40},{-62,-40}},
      color={0,0,127}, smooth=Smooth.None));
  connect(trapezoid.y, feedback.u1) annotation (Line(
      points={{61,80},{90,80},{90,40},{78,40}},
      color={0,0,127}, smooth=Smooth.None));
  connect(speedSensor.w, feedback.u2) annotation (Line(
      points={{70,11},{70,32}},
      color={0,0,127}, smooth=Smooth.None));
  annotation (
    Diagram(graphics={
        Rectangle(
          extent={{-52,60},{94,20}}, lineColor={0,128,255},
          pattern=LinePattern.Dash, lineThickness=0.5),
        Rectangle(
          extent={{54,16},{84,-18}}, lineColor={128,255,0},
          pattern=LinePattern.Dash, lineThickness=0.5),
        Rectangle(
          extent={{-66,-22},{-36,-56}}, lineColor={255,0,128},
          pattern=LinePattern.Dash, lineThickness=0.5),
        Rectangle(
          extent={{-26,-22},{88,-98}}, lineColor={170,85,255},
          pattern=LinePattern.Dash, lineThickness=0.5)}),
    experiment(StopTime=4));
end FlatSystem;

请注意,在这个特定模型里,sensor组件是Modelica.Mechanics.Rotational.Sensors包内SpeedSensor模型的一个实例。这是个报告精确解轨迹的“理想”传感器。换句话说,该传感器不引入任何一种测量误差。

事实是在模拟后我们看到,即便使用精确的速度测量,控制系统仍不太能够跟踪给定的速度轨迹:

../../../_images/AFS.png

我们会在稍后讨论为什么。但问题显然不是由测量误差带来的,因为所测量的速度是恰好等于实际轴速度。

现在设想我们要用一个更现实的传感器模型,如之前编写的取样保持传感器模型,以观察测量误差可能对系统性能有何额外影响。其中一点方法是将FlatSystem模型内的以下代码:

  Modelica.Mechanics.Rotational.Sensors.SpeedSensor speedSensor annotation (
      Placement(transformation(
        extent={{-10,-10},{10,10}}, rotation=90, origin={70,0})));
  Modelica.Blocks.Math.Feedback feedback annotation (Placement(transformation(

替换为:

  Components.SpeedMeasurement.Components.SampleHold speedSensor(sample_rate=0.036)
    annotation (Placement(transformation(
        extent={{-10,-10},{10,10}}, rotation=90, origin={70,0})));
  Modelica.Blocks.Math.Feedback feedback annotation (Placement(transformation(

注意唯一的改变这里是speedSensor组件的类型。模拟这个系统,我们将看到控制系统以下的性能表现:

../../../_images/AFS_SH.png

在这种情况下,我们可以系统性能每况愈下。虽然我们最初无法完全紧跟所需的速度,现在系统(由于测量误差)已变得不稳定。

带层级系统

在这里,我们想稍进一步探索上述性能问题,以一方面了解传感器的特性(如:sample_rate)会如何影响系统性能。另一方面,我们也希望进一步考虑对于控制系统本身的改进。

如果我们要更换传感器、执行器和控制策略,那么第一步应该是将这些子系统组织为模型。如此,我们最终会得到了以下系统模型:

Hierarchical system model

我们在系统级只会看到四个子系统在。这些模型各自对应于刚才提到的子系统。我们的Modelica模型现在变得更为简单了,因为模型内仅有如下声明:

  Implementation.BasicPlant plant
    annotation (Placement(transformation(extent={{-10,-40},{10,-20}})));
  Implementation.IdealActuator actuator
    annotation (Placement(transformation(extent={{-50,-40},{-30,-20}})));
  Implementation.IdealSensor sensor
    annotation (Placement(transformation(extent={{20,-40},{40,-20}})));
  Implementation.ProportionalController controller
    annotation (Placement(transformation(extent={{-10,-10},{10,10}})));
  Modelica.Blocks.Sources.Trapezoid setpoint(period=1.0)
    annotation (Placement(transformation(extent={{-50,20},{-30,40}})));

每个子系统( plantactuatorsensorcontroller)是由Implementations包内的一个子系统模型来实现的。这是一种对模型的改进。原因是这意味着如果我们想改变控制器模型,我们可以简单通过改变与controller子系统所关联的的类型,然后系统就会采用另外这个子系统的另一种实现。相对于删除现有的控制器组件、拖拽新组件、然后(正确地!)重新连接所有关联部件这一系列繁复的步骤,这无疑是一种进步。

但是,我们在将传感器切换为采样保持版本后,仍然需要更改模型的文本,即:

  Implementation.BasicPlant plant
    annotation (Placement(transformation(extent={{-10,-40},{10,-20}})));
  Implementation.IdealActuator actuator
    annotation (Placement(transformation(extent={{-50,-40},{-30,-20}})));
  Implementation.SampleHoldSensor sensor
    annotation (Placement(transformation(extent={{20,-40},{40,-20}})));
  Implementation.ProportionalController controller
    annotation (Placement(transformation(extent={{-10,-10},{10,10}})));
  Modelica.Blocks.Sources.Trapezoid setpoint(period=1.0)
    annotation (Placement(transformation(extent={{-50,20},{-30,40}})));

此方案仍然有数个问题。首先,回想我们是通过更改类型名去改变子系统的实现。问题是:“什么类型的可以用在这里?”如果我将sensor子系统的类型改为BasicPlant呢?这会没有任何意义。但是,仅通过观察模型其实我们不会知道这点。但更大的问题是,为创建一个模型我们最终会得到两款几乎相同的模型。正如我们我们在本书前面所学到的一样,大家应该紧记DRY (不要重复自己)原则。而在这些模型中,我们看到了很多的冗余。

处理冗余

试想我们以IdealSensor模型为出发点建立层级模型:

within ModelicaByExample.Architectures.SensorComparison.Examples;
model HierarchicalSystem "Organzing components into subsystems"
  Implementation.BasicPlant plant
    annotation (Placement(transformation(extent={{-10,-40},{10,-20}})));
  Implementation.IdealActuator actuator
    annotation (Placement(transformation(extent={{-50,-40},{-30,-20}})));
  Implementation.IdealSensor sensor
    annotation (Placement(transformation(extent={{20,-40},{40,-20}})));
  Implementation.ProportionalController controller
    annotation (Placement(transformation(extent={{-10,-10},{10,10}})));
  Modelica.Blocks.Sources.Trapezoid setpoint(period=1.0)
    annotation (Placement(transformation(extent={{-50,20},{-30,40}})));
equation
  connect(actuator.shaft, plant.flange_a) annotation (Line(
      points={{-30,-30},{-10,-30}},
      color={0,0,0},
      smooth=Smooth.None));
  connect(actuator.housing, plant.housing) annotation (Line(
      points={{-30,-36},{-10,-36}},
      color={0,0,0},
      smooth=Smooth.None));
  connect(plant.flange_b, sensor.shaft) annotation (Line(
      points={{10,-30},{20,-30}},
      color={0,0,0},
      smooth=Smooth.None));
  connect(controller.command, actuator.tau) annotation (Line(
      points={{-11,0},{-70,0},{-70,-30},{-52,-30}},
      color={0,0,127},
      smooth=Smooth.None));
  connect(sensor.w, controller.measured) annotation (Line(
      points={{41,-30},{60,-30},{60,0},{10,0}},
      color={0,0,127},
      smooth=Smooth.None));
  connect(setpoint.y, controller.setpoint) annotation (Line(
      points={{-29,30},{0,30},{0,12}},
      color={0,0,127},
      smooth=Smooth.None));
end HierarchicalSystem;

Now, we want to create a variation of this model where the sensor component can assume different types.

在前面,我们通过继承处理冗余。我们当然可以使用继承来将HierarchicalSystem内的子系统转移到另一个模型,然后改变此模型的参数,如:

model Variation1
  extends HierarchicalSystem(setpoint(startTime=1.0));
end Variation1;

但我们并不希望改变参数,要改变的是类型。假设我们能以某种方式“覆盖”以前的选择,这样就可以做到如下的事情:

model Variation2 "What we'd like to do (but cannot)"
  extends HierarchicalSystem(setpoint(startTime=1.0));
  Implementation.SampleHoldSensor sensor
    annotation (Placement(transformation(extent={{20,-40},{40,-20}})));
end Variation2;

这就是我们实际上想要做的。但是,这不是合法的Modelica代码。再者这个方法有若干个其他问题。首先,原模型开发人员可能不希望允许这样的变化。第二个问题是,我们最终会有(不同类型的,且不会少于)两个sensor组件。即使模型真的“覆盖”了sensor组件其任何先前的声明,另外一个问题是,我们可能会键入名称错误的变量名,最后系统里有两个传感器。最后,我们仍然没有办法知道,将sensor改成SampleHoldSensor是否有意义。在这里这样改是有意义的,但在一般情况下我们如何保证呢?

幸运的是,一种方式可以几乎达到我们的目的。但为了解决这些附带问题,我们需要更严格地考虑这个问题。

首先,我们需要指示该组件允许被更换。为了达到这一点,我们可以通过如下方式声明sensor

replaceable Implementation.IdealSensor sensor
  annotation (Placement(transformation(extent={{20,-40},{40,-20}})));

使用了replaceable关键字表明,我们继承该模型时,该变量的类型是可以改变的(或者说,可以“重新声明”)。但要记住,这个模型也有如下的语句:

connect(plant.flange_b, sensor.shaft);
connect(sensor.w, controller.measured);

这就带来了一个问题。 如果我们更换的sensor组件没有w连接器,那么会发生什么?在这种情况下,这个connect语句将产生错误。此时,我们会说这两种传感器型号不是插件兼容的。当对于Y的每个公有变量X都有一个具有相同的名字的对应变量,模型X插件兼容于Y模型。此外,在X内每个这样的变量本身必须是插件兼容它在Y内的对应变量。这样可以确保如果你将Y类型的组件到变更为类型X,你需要的一切(参数,连接器等)都将继续存在,并仍将是兼容的。不过,请注意,倘若X是插件兼容于Y,这意味着Y插件兼容于X(我们将马上看到这样的例子)。

“插件兼容性”是非常重要的。原因是在一般情况下,我们希望确保任何重声明都是“安全的”。要做到这一点,我们必须确定在更改sensor组件时,使用的任何类型均插件兼容于原来的类型。在这种情况下,这意味着这个类型必须有一个w连接器(同样地,这个连接器必须插件兼容于重声明前的w)。另外,这个类型必须有一个shaft连接器(同上,其必须插件兼容于以前的shaft)。

所以接下来的问题是,我们的SampleHoldSensor能否实现满足插件兼容性要求?此模型是插件兼容于IdealSensor模型么?首先,让我们来看看IdealSensor模型:

within ModelicaByExample.Architectures.SensorComparison.Implementation;
model IdealSensor "Implementation of an ideal sensor"
  Modelica.Mechanics.Rotational.Interfaces.Flange_a shaft
    "Flange of shaft from which sensor information shall be measured"
    annotation (Placement(transformation(extent={{-110,-10},{-90,10}})));
  Modelica.Blocks.Interfaces.RealOutput w "Absolute angular velocity of flange"
    annotation (Placement(transformation(extent={{100,-10},{120,10}})));
protected
  Modelica.Mechanics.Rotational.Sensors.SpeedSensor idealSpeedSensor
    "An ideal speed sensor" annotation (Placement(transformation(
        extent={{-10,-10},{10,10}})));

此组件的公有接口包含两个连接器:wshaft。观察SampleHoldSensor模型:

within ModelicaByExample.Architectures.SensorComparison.Implementation;
model SampleHoldSensor "Implementation of a sample hold sensor"
  parameter Modelica.SIunits.Time sample_rate(min=Modelica.Constants.eps);
  Modelica.Mechanics.Rotational.Interfaces.Flange_a shaft
    "Flange of shaft from which sensor information shall be measured"
    annotation (Placement(transformation(extent={{-110,-10},{-90,10}})));
  Modelica.Blocks.Interfaces.RealOutput w "Absolute angular velocity of flange"
    annotation (Placement(transformation(extent={{100,-10},{120,10}})));
protected
  Components.SpeedMeasurement.Components.SampleHold sampleHoldSensor(
      sample_rate=sample_rate)
    annotation (Placement(transformation(extent={{-10,-10},{10,10}})));

我们可以看到,其公有接口也包含连接器w``和` ` shaft。此外,两者与IdealSensor模型上的连接器具有完全相同的类型。因此,SampleHoldSensor模型插件兼容于IdealSensor模型。所以我们应该可以用SampleHoldSensor替换IdealSensord的实例。在替换后,我们的connect语句仍然有效。

那么,如果我们的HierarchicalSystem模型声明如下:

within ModelicaByExample.Architectures.SensorComparison.Examples;
model HierarchicalSystem "Organzing components into subsystems"
  replaceable Implementation.BasicPlant plant
    annotation (Placement(transformation(extent={{-10,-40},{10,-20}})));
  replaceable Implementation.IdealActuator actuator
    annotation (Placement(transformation(extent={{-50,-40},{-30,-20}})));
  replaceable Implementation.IdealSensor sensor
    annotation (Placement(transformation(extent={{20,-40},{40,-20}})));
  replaceable Implementation.ProportionalController controller
    annotation (Placement(transformation(extent={{-10,-10},{10,10}})));
  replaceable Modelica.Blocks.Sources.Trapezoid setpoint
    annotation (Placement(transformation(extent={{-50,20},{-30,40}})));
  // ...
end HierarchicalSystem;

然后我们可以以如下方式实现最初创建此模型变体的目的,而不必重复:

model Variation3 "DRY redeclaration"
  extends HierarchicalSystem(
    redeclare Implementation.SampleHoldSensor sensor
  );
end Variation3;

上述模型的还有几点值得注意。首先是重声明的语法和正常声明几乎相同。不同在于,其前面加上了redeclare关键字。还要注意的是,重新声明是extends子句的一部分。具体地讲,重声明语句如同其他所有修改一样,是一个在扩展子句内的更改。如果我们想既重声明sensor组件又要改变我们工作点的startTime参数,则两者均为extends子句内的更改,如:

model Variation3 "DRY redeclaration"
  extends HierarchicalSystem(
    setpoint(startTime=1.0),
    redeclare Implementation.SampleHoldSensor sensor
  );
end Variation3;

约束类型

回想一下,在本节的前面,SampleHoldSensor模型的公有接口包括:

parameter Modelica.SIunits.Time sample_rate=0.01;
Modelica.Mechanics.Rotational.Interfaces.Flange_a shaft;
Modelica.Blocks.Interfaces.RealOutput w;

IdealSensor模型的公有接口则只有:

Modelica.Mechanics.Rotational.Interfaces.Flange_a shaft;
Modelica.Blocks.Interfaces.RealOutput w;

如果重声明受限于新类型和原始类型的插件兼容性,那么我们可能会遇到下面的问题。万一我们在系统初始模型中使用了SampleHoldSensor传感器,即:

within ModelicaByExample.Architectures.SensorComparison.Examples;
model InitialSystem "Organzing components into subsystems"
  replaceable Implementation.BasicPlant plant;
  replaceable Implementation.IdealActuator actuator;
  replaceable Implementation.SampleHoldSensor sensor;
  replaceable Implementation.ProportionalController controller;
  replaceable Modelica.Blocks.Sources.Trapezoid setpoint;
equation
  // ...
  connect(plant.flange_b, sensor.shaft);
  connect(sensor.w, controller.measured);
  // ...
end InitialSystem;

进一步试想,我们想将sensor组件重定义为dealSensor,如:

model Variation4
  extends InitialSystem(
    setpoint(startTime=1.0),
    redeclare Implementation.IdealSensor sensor // illlegal
  );
end Variation4;

现在我们有一个问题。原来的sensor组件有个名为sample_rate的参数。但是,我们试图把该组件替代没有这个参数的类型。换句话说,IdealSensor模型插件兼容于SampleHoldSensor模型。因为此新模型缺少一些原模型SampleHoldSensor具有的内容:sample_rate

但是,当我们观察InitialSystem模型的源代码后,我们看到了sample_rate参数从未使用过。因此,并不存在真正的理由阻止类型转换。出于这个原因,Modelica包括约束类型这个概念。

要了解重声明,重要的是需要清楚,其实有两个重要类型与原声明相关。第一个重要类型是原声明的类型。第二则是什么类型有可能,而且可让系统运行。这第二个类型被称为约束类型。因为只要任何重声明的类型插件兼容于约束类型,该模型应该仍然工作。这第二类叫做约束类型,因为只要任何重新声明是插件兼容约束类型,模型应该仍能正常工作。因此,上述的InitialSystem模型内原声明的类型是SampleHoldSensor。但只要新类型的插件兼容IdealSensor,该模型仍然能工作。

当我们表明一个组件是replaceable时,我们可以在最末加入constrainedby去指示约束型,如:,

replaceable Implementation.SampleHoldSensor sensor
  constrainedby Implementation.IdealSensor;

上述声明的意思是sensor组件可以通过任何的插件兼容于IdealSensor的模型来重声明。但是如果不进行重声明,那么默认会声明为SampleHoldSensor传感器。出于这个原因,声明SampleHoldSensor时使用的原始类型被称为默认类型

注意,我们原来的InitialSystem模型的定义并没有指定约束类型。模型只指定了初始类型。在这种情况下,默认类型和约束类型被假定为初始类型。

在下一节讨论了如何使用自上而下架构驱动方法去开发这样的系统模型时,我们将继续使用相同系统架构。

[ItPMwM]Michael M. Tiller, “Introduction to Physical Modeling with Modelica” http://www.amazon.com/Introduction-Physical-Modeling-International-Engineering/dp/0792373677