4. 面向对象建模与组件复用
前三章我们建立了 Modelica 的方程思维:模型由变量、参数和方程构成,仿真器负责求解。但当系统规模从十几个方程增长到上千个方程时,仅靠"平铺"地写方程会迅速失控。面向对象建模正是解决这一问题的核心手段——它让我们把物理系统拆成可独立描述、可替换、可组合的组件,再通过连接关系自动生成整体方程。本章围绕类与继承、可替换组件、连接器与连接语义、包与模型库组织四条主线展开,最后用一个可复用组件案例把四者串起来。
类与继承:从"复制粘贴"到"扩展复用"
在 Modelica 中,model、block、record、connector 本质上都是类(class)。类封装了变量、参数、方程和内部子组件,是复用的最小单元。继承用 extends 关键字表达,语义是"把父类的全部内容原样纳入子类,再追加或修改"。
一个典型场景是电阻。基础电阻只有两个引脚和一条欧姆定律方程:
model Resistor "理想电阻"
Modelica.Electrical.Analog.Interfaces.Pin p, n;
parameter Modelica.Units.SI.Resistance R = 100 "电阻值";
Modelica.Units.SI.Voltage v;
Modelica.Units.SI.Current i;
equation
v = p.v - n.v;
i = p.i;
v = R * i;
end Resistor;
若要做温度相关电阻,不必重写引脚和方程,只需继承并覆盖:
model TempResistor "温度相关电阻"
extends Resistor;
parameter Modelica.Units.SI.Temperature T_ref = 300;
parameter Real alpha = 0.0039 "温度系数";
Modelica.Units.SI.Temperature T;
equation
R = R_ref * (1 + alpha * (T - T_ref));
end TempResistor;
这里 R 由参数变为方程决定的变量,父类方程 v = R*i 依然成立。继承的关键判断是:子类是否真的是父类的一种特化(is-a 关系)。若只是"用到"某个功能,应使用组合(在类中声明子组件实例),而非继承。滥用继承会导致参数污染和方程冲突,这是初学者最常见的建模陷阱。
可替换组件:让模型在结构层面可配置
继承解决"类型复用",可替换组件(replaceable / redeclare)解决"结构复用"。它允许在保持外部接口不变的前提下,替换内部某个子组件的类型。语法上,父模型用 replaceable 声明一个受约束的组件,并给出默认类型;使用时用 redeclare 换成满足同一约束的其它类型。
model DriveTrain "可配置传动系统"
replaceable Modelica.Mechanics.Rotational.Components.Inertia
load(J = 1) constrainedby
Modelica.Mechanics.Rotational.Interfaces.PartialElementaryOneFlangeAndSupport2;
end DriveTrain;
constrainedby 是安全阀:它规定替换类型必须兼容的接口,保证替换后连接关系仍然成立。实际工程中,电机模型、电池模型、控制器模型都常做成可替换,以便在同一系统框架下快速切换不同精度或不同厂商的模型。
连接器与连接语义:方程是如何"自动生成"的
连接器是组件之间唯一的交互界面,它把"物理接口"抽象为一组跨变量(across)和穿变量(through)。以电气引脚为例:
connector Pin
Modelica.Units.SI.Voltage v "电势";
flow Modelica.Units.SI.Current i "电流";
end Pin;
连接语义由 connect() 定义,规则可归纳为两条:
- 跨变量相等:所有被连接引脚的电势
v相等(等势)。 - 穿变量守恒:所有被连接引脚的电流
i代数和为零(基尔霍夫电流定律)。
这两条规则是自动生成的,不需要手写。也就是说,connect(p1, p2) 会展开为 p1.v = p2.v 和 p1.i + p2.i = 0。这正是 Modelica 区别于信号流建模的关键:连接是"物理守恒"的,而非"数据传递"的。下图展示了从组件到连接器再到全局方程的语义链。
包与模型库组织:让复用可检索、可维护
当组件数量增多,必须用 package 组织。包可以嵌套,形成层次化命名空间,例如 MyLib.Electrical.Components.Resistor。良好的库组织遵循几条经验:接口(connector、partial model)放在 Interfaces 子包;具体组件按物理域分目录;示例模型放在 Examples 子包,便于一键仿真验证。
Modelica 标准库(MSL)本身就是范例:Modelica.Electrical.Analog.Basic 存放基础元件,Modelica.Electrical.Analog.Interfaces 存放引脚和偏模型。自定义库时,建议每个组件都配一个 Examples 下的最小测试模型,这既是文档,也是回归测试。
可复用组件案例:RC 低通滤波器
把上述机制落到一个具体案例。目标:构建一个可复用的 RC 低通滤波器组件,要求电阻和电容均可替换,并能在不同参数下快速仿真。
package MyFilters "自定义滤波器库"
model RCLowPass "RC 低通滤波器"
replaceable Modelica.Electrical.Analog.Basic.Resistor R(
R = 1000) constrainedby
Modelica.Electrical.Analog.Interfaces.PartialResistor;
replaceable Modelica.Electrical.Analog.Basic.Capacitor C(
C = 1e-6) constrainedby
Modelica.Electrical.Analog.Interfaces.PartialCapacitor;
Modelica.Electrical.Analog.Interfaces.Pin input, output;
Modelica.Electrical.Analog.Basic.Ground g;
equation
connect(input, R.p);
connect(R.n, output);
connect(output, C.p);
connect(C.n, g.p);
end RCLowPass;
model TestRCLowPass "测试模型"
RCLowPass f;
Modelica.Electrical.Analog.Sources.SineVoltage src(
V = 5, freq = 1000);
Modelica.Electrical.Analog.Basic.Ground g2;
equation
connect(src.p, f.input);
connect(src.n, g2.p);
connect(f.output, g2.p);
end TestRCLowPass;
end MyFilters;
仿真设置:StopTime = 0.01 s,Interval = 1e-5 s,求解器 dassl。截止频率由 f_c = 1/(2πRC) 估算,取 R=1 kΩ、C=1 μF 时 f_c ≈ 159 Hz。输入 1 kHz 正弦时,输出幅值应显著衰减(约 -16 dB),且相位滞后接近 90°。观察 f.output.v 的稳态幅值与相位,即可判断滤波器是否按预期工作。
R 用 redeclare 换成 TempResistor,只需在实例化处写 redeclare TempResistor R(T_ref=300, alpha=0.0039),其余连接和方程完全不变。这正是可替换组件带来的结构复用价值。constrainedby 约束过松导致替换后方程数不足、以及把"组合"误写成"继承"造成参数重复定义。遇到"方程数与未知数不匹配"报错时,优先检查连接是否遗漏或重复。本章的四项机制并非孤立:继承提供类型复用,可替换组件提供结构复用,连接器定义交互契约,包组织保证可检索与可维护。掌握它们,才能从"写一个模型"进阶到"建一个库"。