本节摘要:继电器翻转、二极管导通、饱和限幅、碰撞接触——这些不连续环节是仿真世界里的"悬崖":跨越悬崖的步长会把跳变抹成斜坡,时间定位也会漂移。过零检测是引擎处理悬崖的机制:探测到信号逼近跳变面时收缩步长、精确定位事件点,再从事件点重新出发。本节讲清这套机制的工作原理、性能代价与关闭时机,并演示一个继电器模型的完整诊断过程。
考虑一个继电器:输入过阈值输出翻到正满幅,低于另一阈值翻到负满幅。数值求解器的眼光里,这是两难。步长小,跳变被精确捕捉,但切换频繁的电路里仿真慢到无法接受;步长大,跳变发生在一个步长内部,输出被平均成中间值——波形相位漂移、能量计算失真,更隐蔽的是求解器完全不知道发生了事件,误差无从估计。
过零检测把"跳变面"显式注册给引擎:每个带不连续性的模块(继电器、饱和、死区、符号函数等)声明自己的过零函数——一个在跳变处恰好为零的辅助量。求解器每个步长检查这些辅助量的符号;符号翻转意味着步长内部藏着一次跳变,求解器随即收缩步长、二分逼近跳变时刻,把该时刻注册为时间轴上的一个精确事件点,然后从事件点重新积分。效果是:跳变时刻的定位精度不依赖步长,跳变两侧各自用合适的步长正常推进。

机制的代价与过零数量成正比。开关电源模型里,功率器件每个开关周期通断各一次,一毫秒仿真可能产出成百上千个事件;每个事件都要收缩步长、定位、重启积分,仿真时间随之膨胀。判断是否陷入事件风暴,看求解器诊断输出的过零计数与最小步长统计即可。
缓解手段按激进程度排列。其一,收紧容差之外先调过零容差:过零算法的搜索容差适当放宽,换取定位略粗但事件处理更快,多数非精密定时场景可接受。其二,合并过零函数:某些模块配置里可以关闭内部冗余的检测项,只留真正影响输出的跳变面。其三,改模型结构:用带迟滞的特性替代理想开关特性,跳变次数直接减少;或者按 3.2 节的两段式,把高频开关级用平均模型替代,开发期根本不给它产生过零的机会。其四,极端情况下对特定信号关闭过零检测——这是与虎谋皮:定位漂移、抖动、能量误差都会回来,只适合"我知道这个跳变不关键"的场合,且必须在注释里写明理由。
% 继电器模型的过零诊断会话(命令行操作示意) set_param('relay_demo', 'SolverType', 'Variable-step'); set_param('relay_demo', 'Solver', 'ode45'); set_param('relay_demo', 'RelTol', '1e-4'); out = sim('relay_demo', 'StopTime', '0.1'); % 读求解器诊断:过零次数与最小步长(示意路径,随版本可能变化) diag = out.SimulationMetadata.TimingInfo; % 若过零计数达到数十万级、最小步长远小于容差需要: % 方案一 放宽过零搜索容差 set_param('relay_demo', 'ZeroCrossAlgorithm', 'Adaptive'); % 方案二 给继电器加迟滞 减少切换次数(修改模型结构) % 方案三 仅对该继电器关闭检测 并在模型注释中说明理由
容易混淆的一点:过零是连续域的事件机制,采样是离散域的节拍机制,两者在时间轴上共存但互不替代。一个常见设计错误是用高频定步长去"追"跳变(把步长设得极小希望自然捕捉到跳变)——这是把事件问题误当节拍问题,精度上不去、速度先崩掉。正确姿势:跳变时刻交给过零检测,节拍推进交给采样机制,让引擎各司其职。
第 4 章的复盘里,继电器与死区特性会真实出场一次:小林的模型在加入迟滞比较器后仿真骤慢,诊断路径正是本节的顺序——看过零计数、查最小步长、加迟滞改结构,三步收场。
⚠️ 常见坑:Stateflow 与过零的交集。状态跃迁条件里的连续信号比较会注册过零,复杂的转移图配上多个连续条件,事件数量悄悄翻倍。排查时先看状态机内部的条件数量,再决定是否把条件检测挪到离散采样域。
过零检测内部有两种算法实现,行为差异值得知道。非自适应算法用固定的小步长在跳变面附近搜索,行为保守可预测,事件定位精度与搜索步长直接挂钩;跳变面少、时刻要求苛刻的场景用它,代价是每个事件的处理时间偏长。自适应算法按信号的局部动态调整搜索力度,平缓面大步逼近、剧烈面细步搜索,事件密集场景的总开销明显更低,代价是行为依赖历史、极端场景下定位精度可能波动。拿不准时用默认的自适应,遇到"事件定位时刻有抖动"的报告再切换对照——这也算 3.2 节"换求解器复跑"思想在事件机制上的翻版。
还有一种隐蔽的过零来源值得点名:你自己写的 MATLAB Function 块。预置模块的跳变面引擎了如指掌,而自定义代码里一个 if x 大于 0 式的硬判断,引擎无法注册过零,只能靠步长硬碰——精度悄悄退回"无检测"状态。治理办法是把这类判断挪到显式的过零能力模块之外,或改用带检测能力的实现(比较器模块加数据类型转换),让引擎重新看见跳变面。自定义模块(第 9 章)与过零的这条交界,是排查"为什么我的自定义逻辑输出的跳变时刻总不准"时的第一站。
机制、地图、悬崖都讲完了,本章最后一站用一场亲手实验收束:定步长与变步长,到底差多少。