快速了解Modelica与OpenModelica建模
Modelica 里面倒是有不少的面向对象的建模的工具,也支持 PetriNet 的描述。OpenModelica 上面的 PNlib 工具包倒好像是不错的。
OpenModelica 是其一个实现。OpenModelica 环境由若干个子系统构顶成。包括文本模型与图形模型编辑器、编译器调试器与执行环境、笔记本、优化器以及 Eclipse 插件。其中的编译子系统用于将 Modelica 语言编译成 C 代码。面向 Eclipse 的插件被称为 MDT。最基本的方式自然是安装之后点击 OMShell 进入文本编辑环境。里面输入 Modelica 的代码。OMShell 命令会打开一个新的窗格,使用 OMShell-terminal 命令可以直接在 Linux 终端中进入 OpenModelica 的环境。这时进入的就是交互式模式。
model A Integer t= 1.5; end A;
instantiateModel(A);
model C
Integer a;
Real b;
equation
der(a) = b;
der(b) = 12.0;
end C
模型是 Modelica 中的基本元素,许多内容也就围绕着模型的定义来。模型可以用文件来定义,一般使用后缀.mo。使用 loadFile 命令就可以从文件中加载模型。使用 system 命令则可以执行操作系统的 Shell 的代码。
比如下面是使用 Modelica 写的冒泡排序的代码:
function bubblesort
input Real[:] x;
output Real[size(x,1)] y;
protected
Real t;
algorithm
y := x;
for i in 1:size(x,1) loop
for j in 1:size(x,1) loop
if y[i] > y[j] then
t := y[i];
y[i] := y[j];
y[j] := t;
end if;
end for;
end for;
end bubblesort;
从上面可以看出 Modelica 的代码风格做得非常 “冗余”。每个循环与条件语句必须显式被结束,每个函数体与模块也必须带显式的结尾处理。函数体中的输入、输出、局部变量与算法体必须各自区分开。
实际上 Modelica 的输出是通过基于 CORBA 的客户端实现的。更好的方式是通过 readFile 实现将文件中的函数等对象加载到当前工作区。在 OMShell 中,使用 cd() 函数可以返回当前工作路径,添加字符串参数之后可以切换工作路径。
OpenModelica 有一些标准模型,它们使用 loadModel() 函数加载。比如 loadModel(Modelica),加载 Modelica 的标准库。下面是一个模拟运行的示例文件:
loadFile("/usr/share/doc/omc/testmodels/dcmotor.mo")
list(dcmotor) // 列出模型源代码
instantiateModel(dcmotor) //对模型进行实例化,得到实例化后的代码
simulate(dcmotor, startTime=0, stopTime=10.0)
模拟之后我们可以得到一系列变量,然后用它们来画图。val(variableName, time) 函数就是用于得到模拟结果中的变量在特定时间的值的。
比如我们看如下的代码:
loadFile("/usr/share/doc/omc/testmodels/BouncingBall.mo");
list(BouncingBall);
simulate(BouncingBall, stopTime=3.0);
plot({h,flying});
在 OMShell 中运行的代码可以保存成.mos 格式(Modelica script)。然后通过 runScript 命令来运行。(使用 writeFile 可以将字符串写入特定文件)。
接下来我们演示一个开关的模拟的例子:
model Switch
Real v;
Real i;
Real i1;
Real itot;
Boolean open;
equation
itot = i+i1;
if open then v=0;
else i=0;
end if
1-i1=0;
1-v-i=0;
open = time >= 0.5;
end Switch;
simulate(Switch, startTime=0, stopTime=1);
val(itot,0);
类似于 Matlab,可以使用 clear() 清楚当前工作区,使用 list() 查看加载的模型。在 OpenModelica 当中,控制结构、函数、变量、类型都是非常正常的概念,还可以使用 typeOf() 函数查看变量的类型。Modelica 实现模拟主要是通过 simulate 函数。函数既可以输出成特定的模拟结果,也可以只保存特定的变量。也可以使用并行模拟(启动 omc 的时候指定并行的参数)。
Model 的 library 又称 package,使用 package 来定义,里面可以添加一些注释。
package Modelica
annotation(uses(Complex(version="1.0"),ModelicaServices(version="1.1")));
end Modelica;
在加载模块的时候,如果指定了对指定包的依赖关系,那么指定包也会加载上去:
model M
annotation(uses(Modelica(version="3.2.1")));
end M;
instantiateModel(M)
instantiateModel(Modelica.Electrical.Analog.Basic.Ground) //只加载其中的模块
list(Modelica) //查询名为Modelica的模块的模型。
quite() //退出Modelica环境
Modelica 的模型可以转换成 Matlab 的模型,只需要一个 exportDAEtoMatlab() 函数就可以完成转换了。
omc 命令是 modelica 的编译器。它可以读取一个.mo 文件然后编译模型。另外,omc 也可以执行.mos 格式的脚本命令,以非交互的方式运行。还可以在运行中执行调试工作。
OMEdit 是图形化的编辑模型的界面。里面可以使用预定义的模型、用户定义的模型,组件接口,以及模拟过程,还可以绘制图形。
在 Model 里面也可以定义 algorithm。以调试算法。OpenModelica 支持文学编程方法。在交互式 Notebookk 就可以实现。
面向 Eclipse 的 MDT 支持 Modelica 与 MetaModelica 的开发。在相关网站上可以下载到 OpenModelica 的插件。之后就可以在 Eclipse 环境下进行工作了。也有高亮,也可以绘制出来图形。还可以进行调试等工作(使用 GDB)。也有一些 3D 库,可以完成 3D 图形的建模等工作。除此之外,还可以方便地调用外部的 C 函数,或者调用 Python 的函数。另外,从 Python 中也可以调用 Modelica 的函数,使用 OMPython。
关于 Modelica
Modelica 当然是一种通用的建模与模拟的软件。但是其建模的方式也有其特殊之处。首先是使用了面向对象的技术,一个模型可以看成是一个对象,并且像编程语言那样使用 class 关键字来声明。然后变量有相应的类型。模型中的恒等式可以使用 DAE(微分代数方程,连续情形)或者 Event triggerg 来表示(离散时间)。比如如下的 VanDerPol 的方程模型:
class VanDerPol "Van Der Pol振子模型"
Real x(start=1) "描述变量x";
Real x(start=1) "y变量";
parameter Real lambda = 0.3;
equation
der(x) = y;
der(y) = -x + lambda(1-xx)*y; // 微分方程约束
end VanDerPol;
另外,Modelica 还可以处理连续与离散情形混合的情况,进行混合的建模。另外则是 Modelica 语言显著缩短了在建模的时候进行系统定义、系统分解、子系统建模、因果关系推测、实现以及模拟所花费的时间。
Simulink 是基于信号流进行建模的语言。但是 Modelica 语言则是完全按照物理模型的结构来的,中间不需要刻意去写成信号流图的形式。因此,我们可以直观地画出电路图就可以模拟,而不用转换成 Matlab 那种信号流的模式。另外,Modelica 也可以进行图形可视化的建模,通过拖动实现模块的组合。
比如如下的直流电机的建模:
model DCMotor
Resistor R(R=100);
Inductor L(L=100);
VsourceDC DC(f=10);
Ground G;
ElectroMechanicalElement EM(k=10,J=10,b=2);
Inertia load; //器件与器件的性能参数
equation
//连接方程,Modelica会根据连接方程自动推导函数关系,转化成ODE与DAE的形式
connect(DC.p, R.n);
connect(R.p, L.n);
connect(L.p, EM.n);
connect(Em.p, DC.n);
connect(DC.n, G.p);
connect(EM.flange, load.flange);
end DCMotor
中间的执行流程是图形或者文本模型经过处理变成 Modelica 的源代码。变成 Modelica 的模型。然后通过 Translator 变成混合 DAE 所描述的方程。然后使用分析器进行分析,再用优化器优化成 C 代码。最后编译并执行。模拟的时候,OpenModelica 中也可以选择交互式模拟的方式。
Modelica 在生物、机械、化学、工业中都有比较成功的应用。练习 OpenModelica 的时候,刚开始可以从图形化的 OMedit 开始,建立一个 RLC 电路的模拟。网上有相关教程:
- 打开 OMEdit,文件菜单中选择新建模型,输入模型名 RLCircuit(也就是新建 Class,在 OMEdit 中也可以新建 Modelica 的元模型)。
- 在左侧的库中选择 Modelica 下面的电路元件,将它们按标准连接起来。
- 点击模拟即可。
Modelica 与 SysML、ModelicaML、UML 也有合作。其中的 ModelicaML 就是为了支持软件与硬件建模而设计的 UML Profile。还可以将 UML/SysML 映射到 Modelica。
在 Modelica 语言中,类型有 Boolean 等。而变量可以加上 constant 或者 parameter,表示不变的量或者变化的量(后者允许模拟的时候交互式调整)。class 声明与 model 声明类似,但是 class 声明的可以被实例化,还有 protected、public 等修饰的变量。Modelica 中函数也可以看成是特别的类。但是多了 input/output、algorithm 等块体。函数中也有 protected 的成员。record 可以表示记录体结构、定义参数形式。Modelica 也支持所谓的多继承。
最好可能还是从 OMNotebook 开始。因为里面有许多可视化。如果是离散的,那么可以在变量前面加上 discrete 修饰词。比如如下的离散模型:
model SamplingClock
Integer i;
discrete Real r;
equation
when sample(2,0.5) then
i = pre(i) + 1;
r = pre(r) + 0.3;
end when;
end SamplingClock;
图形建模的时候有自己的规则。比如特别规定了 connector 为一种连接器类别。Modelica 中定义了许多物理系统的连接器。使用 connnect 函数可以连接许多内容。连接器需指定连接方程,默认的方程是两端信号值相等。如果是 flow 函数,则表示流量相等,方向也是相同的。
在 Modelica 中不能被实例化的类称为 partial class。DrControl 是专门用于讲控制理论的一个建模的手册,可以方便地写出控制的方程来。