本节摘要:模型长到几十个模块之后,真正的瓶颈不再是"会不会搭",而是"还能不能读懂、改起来怕不怕"。层次化是框图世界的结构工程:子系统管封装、总线管接口、模型引用管团队边界。本节给出拆与不拆的判断标准、虚拟与原子子系统的语义差异、总线信号的组织方法,以及一套在实践中耐用的分层规范。它是 2.1 到 2.3 节的结构收尾,也是第 6 章数据管理的前置。
小林把温控风扇的完整模型搭完了:电机、驱动、控制器、保护、测量、记录,六十多个模块挤在一张画布上,信号线交叉穿插。两周后导师让她把控制周期从 1 千赫兹改到 2 千赫兹,她盯着屏幕找了二十分钟才确认哪几块属于控制器。这张图的问题不是错,是没有结构——所有信息平铺在一个视觉层级里,读者的注意力没有任何抓手。
层次化的解法是把模型组织成一棵语义树:顶层只有"控制与对象闭环"这一眼能看懂的结构;双击进去才是控制器子系统、电机子系统;再往下是每个子系统的内部实现。读者在每一层看到的模块数不超过十个,认知负荷就被结构吸收了。
什么时候拆子系统?三条标准,按优先级排列。功能内聚:一组模块共同完成一个领域职责(整流、转矩计算、故障判断),拆出去后接口可以用一句领域语言描述。复用预期:同样的结构在模型其他位置还要出现,或者下个项目大概率要用,拆成子系统并最终沉淀为库模块。速率或域边界:连续域与离散域的交界、不同采样率的分界,用子系统把边界显式化,与 2.3 节的原子化配合。
反过来,两条"别拆"的提醒。一,不要按模块类型机械分组——"所有增益放一个子系统、所有积分器放一个"是按外观分类,不是按职责分类,拆了比不拆更难读。二,不要一层套一层套到四五层:每深一层,读者的心智栈就深一格,三层通常是可读性的舒适上限,再深就该考虑升级为引用模型了。

子系统拆好后,接口成了新的可读性瓶颈:五根单独的线从控制器的输出端口伸出来,哪根是占空比、哪根是使能、哪根是故障标志,读者要逐根点开看。总线(Bus)把相关信号打包成有名字的成员集合——ctrl_out 总线里明确列着 duty_cmd、enable、fault_flag,接口从"五根线"变成"一份契约"。配合总线对象(Bus Object)限定每个成员的数据类型与维度,接口就成了带类型检查的契约:类型不匹配的信号接不进来,错误在建模期暴露而不是在仿真第 300 秒以诡异曲线的形式暴露。
跨团队的边界则升级为模型引用:子系统存储为独立模型文件,以引用方式挂进父模型。引用模型带来三样东西:独立的求解器配置(不同团队可用不同的步长策略)、独立的代码生成边界(这个子系统的代码单独生成交付)、以及工作区隔离(内部变量不会与父模型撞名)。代价是接口约束更严格、仿真设置更繁琐。判断口诀:同一个人维护、同一套求解器,用子系统;跨团队、可能各自生成代码,用引用模型。
把本节的判断标准落成团队规范,五条足够。一,顶层只允许出现子系统与端口连线,出现散装的基础模块即违规。二,每个子系统端口上的信号必须有名字,总线必须有总线对象。三,跨速率与跨域边界所在子系统必须设为原子。四,模型深度超过三层、或同一子系统被两个以上父模型使用时,升级为引用模型。五,任何一次结构调整后,跑一遍更新 diagram,用采样时间图例核对速率分布没有意外变化。
💡 关键直觉:结构重构的收益以"下次改动的速度"计价。今天花四十分钟拆好层次,三个月后改控制周期时只需要双击一个子系统——这笔投资的回报率在模型生命周期里会反复兑现。
把一个已经存在的子系统升级为引用模型,实操顺序值得记下来,因为顺序错了会白干一轮。第一步,先在原地完成结构整理:接口信号命名齐、总线对象定义齐——引用模型的接口约束比子系统严格,带着毛刺升级会在第二步反复报错。第二步,右键转换为引用模型,Simulink 自动生成独立模型文件并把原位置替换为引用块。第三步,逐项核对三件被"隔离"走的东西:求解器配置(引用模型带自己的配置,确认与父模型的节拍兼容)、工作区变量(内部变量不再共享父模型工作区,改挂字典或传入参数)、回调脚本(原父模型回调不会在引用模型内触发)。第四步,跑等效性验证——升级前后同一激励下输出一致,才算迁移完成,这一步与 7.2 节的等效性测试是同一件武器。
反过来降级(引用模型改回子系统)也有场景:调试期需要与父模型共享全部上下文时,临时降级可以省去跨模型跳转。但降级前要确认没有依赖隔离语义的东西(独立的代码生成设置、独立的采样速率声明),否则降级即悄悄改变行为。
总线契约怎么落地,一段最小定义胜过十句描述。总线对象在脚本里定义、随字典入库(6.1 节),模型接口引用它之后,类型不匹配的信号在连线时就报错:
% 定义 ctrl_out 总线:控制器到驱动的接口契约 ctrl_out = Simulink.Bus; elems = Simulink.BusElement; elems(1).Name = 'duty_cmd'; elems(1).DataType = 'double'; elems(1).Min=0; elems(1).Max=1; elems(2).Name = 'enable'; elems(2).DataType = 'boolean'; elems(3).Name = 'fault_flag'; elems(3).DataType = 'boolean'; ctrl_out.Elements = elems; assignin('base','ctrl_out',ctrl_out); % 正式项目挂入字典 % 子系统输出端口选择该总线对象后: % 接错的信号(如把转速接进 duty_cmd)在更新模型时即报类型错误
这段定义的价值不在三行代码,而在它把接口从"约定"升格为"机器可查的契约":评审时对着总线对象读接口,测试时按成员生成断言,代码生成时接口结构随之定型——2.4 节说"接口是契约",契约的实体形态就是这份随版本管理的定义。
模型的静态知识至此备齐:连续、离散、混合、层次,四种结构你都能搭也能读。接下来进入动态的部分——按下运行键之后,求解器如何把这棵结构树变成一条条随时间推进的曲线。