
一个支架在 CAD 评审里通过了几何检查和有限元检查,接下来要问的是:把它装到机械臂末端,工作单元还能不能按节拍、不碰撞地完成取放?pai-design-workbench 的物理通道用 MuJoCo 回答这个问题,但它先要确认装上去的就是那个被验收的零件,质量也算对了,再把仿真场景导出成别处能重新打开的 MJCF 和 OpenUSD。本文依据固定在 commit 70330e2 的 docs/PHYSICS.md、native/robot_sim.py 和一份保存的演示记录 docs/evidence/physics-demo.json,讲这条从 CAD 到机器人仿真的链路每一步核对什么、核对不了什么。
文中的数字都来自项目自己的文档、代码和记录([O],项目自报),本站没有运行这些测试,也没有复现演示。同一个仓库的气动通道见 把固定版本的 OpenFOAM 算例搬上 AWS Batch:哈希回传、两套网格与本地阻力对照;本文不讨论支架的寻优过程,只讲零件进入工作单元之后的事。
工作单元仿真测什么
robot_sim.py 不接受用户提供的 XML,只接受八个有界参数,再自己生成 MJCF 模型。超出范围就退出:
| 参数 | 允许范围 |
|---|---|
pickDistance、placeDistance | 0.25–1.1 |
pickHeight、placeHeight | 0.6–1.2 |
pedestalHeight | 0.3–1.0 |
guardClearance | 0.1–1.5 |
speedFraction | 0.1–1.0 |
jitter | 0.0–0.08 |
上述参数范围按源码原样列出,范围定义没有单位注释。文档明确使用米表示护栏间距;speedFraction 表示额定关节速度的比例。
场景里是一台装在底座上的通用六轴臂,连杆长度按 UR5e 公开规格书的尺寸取(UR5e 级),加一个输送线取料位、一个工装放料位和四块围栏。文档写的标准做法是跑 10 个固定种子,每个种子给取料位姿加一个按种子生成的偏移。每个种子依次做三件事:在 MuJoCo 雅可比上用阻尼最小二乘求逆运动学,并限制在关节限位内;按额定关节速度的给定比例生成关节空间五次多项式轨迹,经过接近、抓取、抬起、转移、放置、退回;用位置执行器驱动,以 500 Hz 做完整刚体动力学仿真,同时检测机械臂连杆与静态场景的接触(夹爪与被抓取工件的预期接触不计)。四项检查是:
reach:每个种子在取料和放料处 TCP 误差 ≤ 2 mm。文档以取料和放料处的误差表述这项检查。代码在关节限位内用阻尼最小二乘为每个目标最多迭代 400 次,取六个逆运动学目标(含取放点上方 0.12 m 的接近/抬起点)中的最大 TCP 误差,超过 2 mm 即判失败。失败可能是因为目标在工作空间之外,这对应课程 物理AI实验室 1.1:坐标系与运动学,手算一条两连杆手臂怎样够到杯子 里的“可能无解”;也可能是解存在但迭代没有收敛。这项检查不区分这两种原因。collision-free:运动中机械臂连杆不碰围栏、输送线支架或工装。cycle-time:最慢种子的节拍(含停稳时间)≤maxCycleSeconds。success-rate:同时到位、无碰撞、满足节拍的种子占比 ≥minSuccessRate。
参照单元和候选单元跑同样的种子,界面按种子成对列出结果,参照成功而候选失败的种子会被标出。这和 Robot Reel 的配对比较是同一种做法。PHYSICS.md 记录了把节拍目标提高到 5 秒后 npm run test:robot 的结果:
| 单元 | 结果 |
|---|---|
| 参照,50 % 速度 | 5.90 s,不满足新目标 |
| 75 % 速度,围栏收近到 0.12 m | 4.60 s,但每个种子肘部都撞围栏:拒绝 |
| 同样速度,围栏 0.30 m | 4.60 s,10/10 个种子成功:接受(比参照快 22 %) |
保存的演示记录 physics-demo.json 是另一次运行,录制于项目的托管站点,不是上表的 npm run test:robot 结果;它的数值四舍五入后与上表一致:拒绝的单元节拍 4.598 s,10 个种子中无碰撞的为 0(0/10);参照 5.897 s;修正只改了一项 guardClearance 0.12 → 0.3,requirementsRelaxed 为 false,即没有放宽要求。(5.897 − 4.598) / 5.897 ≈ 0.22,与记录的 22 % 一致。记录里给出这次修正的引擎和模型名,但 modelEvidence 字段是 acp_advertised,即引擎自己声明的身份,不是独立核实的模型来源。
把零件装上夹爪:只接受验收过的那一份
工作单元请求可以带上 tool: { cadReviewId, payloadKg }。按 PHYSICS.md,服务器只接受同一项目中已完成且结论为接受的 CAD 评审,并用该评审记录的摘要核对 STL;未被接受的零件返回 TOOL_NOT_ACCEPTED。robot_sim.py 自己也再算一次 STL 的 SHA-256,与输入规格中的摘要(代码报错称之为 CAD 评审记录)不同就退出,并检查载荷 0–3 kg、电机轴位置 5–120 mm、B-Rep 质量 1–2000 g。
通过之后,精确的 STL 作为网格体装到夹爪上,另在电机轴位置加上声明的载荷(默认是 0.28 kg 的 NEMA 17 电机,代码里是一个指定质量的方块)。参照单元和候选单元装同一个工具,所以比较的是单元,而不是工具。零件的碰撞形状计入 collision-free:代码把 cad-tool 归入机械臂一侧,它碰到围栏也算碰撞。
同一个零件的三种表示
这一节是这条链路最值得借鉴的地方:同一个零件在仿真里有三种几何表示,用途不同,不能混用。
| 表示 | 用于 | 记录的数字 |
|---|---|---|
| B-Rep 实体(CAD 评审) | 质量基准 | 参照支架 48.37 g |
精确网格体积(inertia="exact",2.70 g/cm³,非凸) | MuJoCo 中的质量和惯量 | 参照支架 48.37 g;与 B-Rep 相差超过 3 % 即失败 |
| 凸包 | 只作碰撞形状 | 默认的凸包惯量曾把一个 31.85 g 的零件算成 110 g |
文档说,最后一行就是 3 % 检查存在的原因:110 g 约为 31.85 g 的 3.5 倍。文档把这次偏差归于默认的凸包惯量设置,这正是 3 % 检查要拦下的情况,但没有进一步说明凸包为何会把质量放大到约 3.5 倍;本文因此强调质量回读与阈值核对。代码里的核对方式是:取 cad-tool 刚体的总质量减去载荷,得到网格质量,再与输入规格中的 B-Rep 质量比较,超过 3 % 就以“tool mass mismatch”退出。
导出:MJCF 与 OpenUSD 各自核对了什么
MJCF。 scene.xml 引用放在同一目录下的 tool.stl,不含本机路径,所以整个目录拷到别处可以重新加载。test:robot 会检查这一点。
OpenUSD。 scene.usda 为 Z 轴向上、单位米;每个 MuJoCo 刚体对应一个带 MassAPI(质量、质心、对角惯量、主轴)的刚体,网格碰撞体按凸包;六个带限位的转动关节,焊接的刚体(例如夹爪上的零件)用固定关节,外加一个 articulation root。重新打开后运行 OpenUSD 26.8 注册的全部 UsdValidation 校验器,包括 UsdPhysics 的刚体、关节、articulation 和碰撞体检查,任何错误或警告都使运行失败;非固定关节不是六个也失败。
校验通过不等于是同一台机器人。 可选的 Newton 交叉核对(Newton 1.6,独立 venv,CPU Warp,不需要 GPU)把 scene.usda 导入 Newton,把 scene.xml 载入 MuJoCo,要求只有一个 articulation、6 个转动关节、没有孤立关节、刚体质量相同,并在导出位姿和 32 个限位内的种子构型上比较正运动学:每个刚体位置差 ≤ 0.1 mm、姿态差 ≤ 0.01°。文档记录,第一次运行就发现了两个所有 UsdValidation 校验器都放过的导出错误:
- articulation root 放在
base上,而base不是平铺刚体的祖先,Newton 因此把所有关节当成孤立关节拒绝。现在 root 放在/Workcell。 - 关节坐标系取自取料位姿,导致 USD 中关节角 0 和限位的含义与 MuJoCo 不同。现在取自模型常量(关节角为 0 时相对父刚体的偏移)。
修正后:33 个构型,最大位置误差 0.45 µm,姿态误差 3.1·10⁻⁵ °,质量差在 0.5 mg 以内,耗时 4.8 s。这个结果单独记录为 newton.json,属于导出一致性,不改变评审结论;它只覆盖运动学和质量,不说明两个引擎的动力学是否一致。
在本地复现
按 PHYSICS.md,在 commit 70330e2 的仓库根目录执行:
npm run setup:physics # Gmsh、Optuna、scikit-learn、MuJoCo 按哈希锁定安装;CalculiX 来自签名的 Ubuntu 软件源
npm run setup:physics -- --with-newton # 可选:Newton 1.6,独立 venv
npm run test:robot
按文档,setup:physics 安装的是哈希锁定的固定版本(MuJoCo 3.14.0 等),安装回执 .state/tools/physics-install-receipt.json 记录每个 .deb 的摘要和锁文件哈希。文档说 test:robot 会装上参照支架(B-Rep 48.37 g,MuJoCo 网格 48.37 g),检查 MJCF 可移植、USD 带物理关节和 articulation root,以及未接受的零件被拒绝。所给材料没有写克隆仓库和安装 npm 依赖的步骤;工具链表也没有列出 OpenUSD 本身如何安装;文档没有说明 test:robot 是否包含 Newton 核对。
每次运行写出 robot.json(逐种子结果、四项检查、工具质量对照)、scene.xml、scene.usda 和 robot.glb;robot.json 中固定写着 "scope": "rigid-body-simulation" 和 "physicalValidation": false。
演示记录与海报
本文封面是项目的物理演示海报。演示记录写明:录屏原长 659 秒,没有剪切或重排,画面静止的等待在 2.5 秒后以 6 倍速播放;章节依次为需求、MuJoCo、AI 修正单元、单元反馈、有限元、AI 寻优和发布。记录中的反馈两项都已关闭(场景的 collision-free 和零件的 max-deflection),零件正式评审结论为接受(46.87 g,9/9 项检查),发布 R1 准入 5/5。记录没有显示演示的工作单元中是否装有任何 CAD 零件,本文不作推断;被接受的 46.87 g 零件来自 MuJoCo 和 AI 修正单元之后的有限元与 AI 寻优两章。这是在项目托管站点上录制的一次演示,不是独立实验,也不是真机测试。
局限 / 注意
- 范围是通用机械臂与通用控制器的刚体仿真。被抓取的工件在夹爪闭合时按运动学方式附着,没有抓取物理模型;CAD 支架则在整个运行中作为网格刚体固定于夹爪。它不是厂商控制器,不是安全评估,也不是抓取物理模型;连杆长度是 UR5e 级,不代表任何具体产品的性能。
- 零件质量按 2.70 g/cm³ 的名义密度计算;有限元一侧是线性静力、小变形、名义材料,不含接触、螺栓预紧、疲劳、热效应、公差或试验数据。结果是仿真证据,不是认证。
- 关于凸包近似的一般几何性质(本站解释):凸包会包住凹槽,因而可能把实际不会碰的情况判为碰撞;这不是项目保存记录对具体碰撞误报的验证。
- Newton 核对只证明导出的运动学和质量与 MuJoCo 一致,不证明 Isaac Lab 或 Newton 中的动力学行为相同。
- 22 %、0.45 µm 等数字是项目在自己环境中的一次运行记录,本站没有重新执行。
链接
- docs/PHYSICS.md:物理通道说明(commit 70330e2)
- native/robot_sim.py:工作单元仿真、工具质量核对与 MJCF/OpenUSD 导出
- docs/evidence/physics-demo.json:保存的演示记录
- 封面原图:docs/media/physics-poster.png
- LICENSE(MIT)
- 把固定版本的 OpenFOAM 算例搬上 AWS Batch:哈希回传、两套网格与本地阻力对照
- 物理AI实验室 1.1:坐标系与运动学,手算一条两连杆手臂怎样够到杯子
- Robot Reel:把机器人策略仿真变成可逐帧核对的回放与三维场景
- 物理AI实验室:一套每个数字都能核对的物理 AI 课程(课程大纲)



