相较上一版 Jacobian 确定性复用更新,本次补齐事件边界一致性、结果两侧采样及接触事件定位;保留已有物性复用和组件力学公式。 - 统一 UD00 信号求值与下一事件查询的绝对时间边界,修复循环边界浮点舍入导致的阶段错位、重复或漏报,并覆盖零时长、多阶段及长周期场景。 - 引入原生输出语义 v2:保留规则网格真实时间,补充内部时间事件和状态事件的左邻及事件后采样,按保存时间、状态和离散模式重放结果。 - 两条代码生成路径均发出 LSTP 接触描述,默认定位间隙过零及非负力模式的力截断;仅在接受事件时更新防重复记录,增加 contactEvents 诊断计数。 - 补充 MASS/LSTP 独立事件实验、八路全曲线与驱动阶段配对评估,以及 Amesim 不连续点输出对照和力差定位报告;MASS 新增释放机制仍保留为独立实验。 - 保存局部 probe、context 访问与回退、shadow replay、R288 real skip/typed replay 及阀门数值尾部诊断工具和报告;未证明净收益的实验不启用为生产默认优化。 - 更新原生运行说明和元件建模规范,补充信号边界、输出语义、接触事件和实验依赖回归测试。 验证:五组专项回归共 34 项全部通过;37 个待提交 Python 文件语法检查通过;git diff --cached --check 通过。
8.9 KiB
循环 UD00 事件边界修复与独立验证
日期:2026-09-17。环境:Windows,项目 .venv-win,MinGW GCC 8.1.0,原生 C 运行库,CVODE 7.4.0 BDF / RK45。
1. 本次完成的第一步
修复 UD00 的信号求值与事件时间计算不一致。实现位于 signal.c,新增回归位于 test_native_signal_boundaries.py。
原来信号值用 fmod(t - tstart, period) 判断当前阶段,事件查询另用绝对时间计算。两种浮点运算可能落在边界两侧。例如阶段时长为 0.8 s 和 10 s 时,事件查询已经返回 44 s,而取模仍小于 0.8,信号在该时刻仍输出上一段的 1e17。
现在 native_signal() 与 native_signal_break() 共用 signal_interval(),共同确定当前阶段、阶段起点及下一边界。阶段内部仍按线性插值计算;周期结束统一使用下一周期的起点,避免同一事件出现两个相邻浮点时间。
本次没有修改八路工程 JSON、Amesim 文件、积分算法或结果采样协议。已有 UD00 的调用接口保持不变,两条原生生成路径均自动使用修复后的内核。
2. 修改后的事件定位及处理
时间边界定位
设信号开始时间为 s,各段累计时长为 offset[i],周期为 P:普通阶段边界按 (s + offset[i]) + k * P 计算;周期终点统一采用下一周期起点 s + (k + 1) * P。这些表达式的浮点运算顺序也是合同的一部分,求值与事件查询共同使用同一函数。
当前周期由估算值开始,再依据绝对周期起点修正。阶段采用左闭右开区间:严格早于边界时仍在前段,恰好等于边界时进入后段;零时长阶段被跳过。查询返回严格晚于当前时间的最近事件,无后续事件或事件超过仿真终点时返回仿真终点。
运行库处理
- 生成的
model_next_break(t, end)汇总各信号的下一边界,选择最早一个。 - BDF / RK45 在这个时间边界处分段积分。对仿真内部的边界,旧段积分终点是边界左侧相邻浮点数
nextafter(boundary, -INFINITY)。 - 到达边界后保留连续状态,将时间推进到该边界,并重新启动积分。此时信号求值已切换到新段,导数计算使用新信号。
- 结果输出仍按保存的实际时间和状态调用
model_eval()重放。因为信号阶段是时间的纯函数,求解试算、回退和重放不会推进或污染一个全局阶段计数器。
第 1~4 项的运行库流程原来已经存在;本次让第 3、4 项使用的信号值与第 1 项报告的边界保持一致。连续状态如位置、速度不会仅因信号切换被重置;代数量如输出信号和力可以立即改变。机械碰撞、摩擦等由状态触发的事件仍使用各自的状态事件处理。
与 Amesim 对齐的范围
本次确立平台自身一致的右连续规则。Amesim 的 UD00 还维护当前阶段及 TNEXT,通过离散时间事件调用更新阶段;本次没有完整复制这套离散调用和结果保存协议。
尤其是普通浮点数 32.4 与本工况计算出的事件时间 32.400000000000006 不相等。前者仍属于边界前,测试专门保留这一点,没有使用全局 epsilon 提前切换。后续对照 Amesim 的原始采样时,应继续区分实际时间和事件前/后侧;此次不能直接推出此前八路模型在 32.4 s 的力尖峰差异已经消失。
3. 独立信号测试结果
测试网络只有一个循环 UD00:第一段 1e17,持续 0.8 s;第二段 49000,持续 10 s;从 0 s 开始循环。两种求解器均运行至 60 s,以 0.01 s 保存结果,共 6,001 个采样点。该模型没有物理动态状态,运行库使用一个内部常量状态执行真实求解及输出重放流程。
| 验证项 | 结果 |
|---|---|
| BDF 独立仿真 | 成功完成 60 s;6,001 点逐点符合冻结边界期望值 |
| RK45 独立仿真 | 成功完成 60 s;6,001 点逐点符合冻结边界期望值 |
| 两种求解器比较 | 全部采样值相同 |
| 事件分段 | 两种求解器均启动 12 次:初始启动一次,11 个内部时间事件后分别重启 |
| 44 s 信号 | 修改前 1e17;修改后 49000,符合后一段 |
| 54 s 信号 | 修改前 49000;修改后 1e17,符合下一周期 |
| 八段信号 1,000 周期 | 连续检查 8,000 个边界,无漏报、重复;边界左邻/本身/右邻均正确 |
| 非零起点与小周期 | 3 组工况,各 900 个边界通过 |
| 含零时长阶段的循环 | 连续 2,000 个边界通过;周期尾部零时长段不泄漏瞬时值 |
| 其他 | 斜坡、非循环终值、零总时长、单段锯齿、乱序重放通过 |
新增 7 个测试方法及相关 3 个既有测试全部通过(最终版本 10 / 10,39.033 s,包含积分启动次数断言)。修改前先运行关键边界测试,已复现 44 s、54 s 及多阶段循环边界失败,详见保留日志。独立测试用于确认信号内核和求解/重放接入,不替代八路气动与机械系统的精度验收。
测试过程中曾遇到测试子进程返回空输出及 GCC 进程启动重试。边界查询已合并为单次批量调用,数值判定保持严格;最终定向测试通过。相关中间日志保留在验证目录,不把进程空输出当成边界数值错误,也不将一次成功重跑解释为环境问题已修复。
完整回归的结果与限制
另执行 python -m unittest discover -s tests -v,共运行 458 项,耗时 1,534.126 s,报告 7 个错误、25 项跳过,不能记为整库回归通过。新增 7 项信号测试、既有 UD00 XML、阶跃重启、信号驱动力以及当前八路工程保存配置的完整运行测试均通过。
| 错误来源 | 实际失败点与处理 |
|---|---|
| 管道多变过程测试 | model --init 输出不能解析为 JSON,尚未进入数值断言;单独复核又遇到 GCC 预处理超时 |
| 冻结参考网络 38 | 发布编译缓存时目录重命名报 Windows WinError 5 |
| 雅可比固定状态夹具 | 夹具保存的输入哈希与当前八路 JSON 不一致,类初始化被主动阻止 |
| 物性警告测试 | GCC 预处理超时;残留预处理进程退出后测试序列继续 |
| 工程输入合同的三项测试 | 默认 GBK 解码 UTF-8 JSON 失败;仅加 -X utf8 单独重跑,3 / 3 通过 |
夹具期望输入 SHA-256 为 670977bef67e62d9c66e8af497bada208bd72a7301be45128d185d47282cf288;实际工作树及 HEAD 中八路 JSON 的 SHA-256 均为 60ed16c71b72be4ae71ca7980f4f8cdcdf6b4c4c4c8950863c2390960b71ced4,确认这项输入不匹配不是本次修改引入。没有为使回归通过而更新参考夹具、修改其他内核或放宽断言。本次仅在 Windows 验证,未声明 Linux 验收通过。
验证文件:
复现定向测试:
.\.venv-win\Scripts\python.exe -m unittest tests.test_native_signal_boundaries tests.test_amesim_ud00_xml tests.test_amesim_semantics.NativeComponentSemanticTests.test_signal_endpoints_and_exponential_contact_law_in_c -v
4. 新注册类似信号组件时需要什么
| 情况 | 事件方面的工作 |
|---|---|
| 新增现有 UD00 实例或调整其参数 | 无需额外注册,直接使用已有求值和事件内核 |
| 新型号复用 UD00 分段语义 | 接入已有 native_signal 和 native_signal_break,并把后者汇入 model_next_break |
| 新阶跃、脉冲、分段周期信号 | 除输出公式,还要提供下一时间事件;与输出共享边界计算和相等判定 |
| 无分段的光滑信号,如正弦 | 通常无需专门时间事件 |
| 由压力、位置等状态触发的切换 | 需要相应状态事件定位与切换机制,时间事件表不足以实现 |
仅在 library.py 注册类型,或填写 PORTS / PARAMETERS / RESULT_VARIABLES,不会自动实现这些事件。默认在 native_codegen/extended.py 接入;若新型号也进入 compiler.py 紧凑路径,两条路径都要覆盖。新增 C 函数时同步公共声明与模块导出/依赖。
新组件通常只负责描述事件时刻及切换语义,由运行库统一执行积分分段与重启。相关要求已写入元件开发规范第 12.1 节。