MuJoCo MJX Shadow Hand 模型详解:E3M5 灵巧手如何适配 GPU 批量仿真
【免费下载链接】mujocoMulti-Joint dynamics with Contact. A general purpose physics simulator.项目地址: https://gitcode.com/GitHub_Trending/mu/mujoco
本文围绕 MuJoCo 仓库中 MJX Shadow Hand 模型包 展开:这个模型包提供了 Shadow Robot 公司 "E3M5" 版 Shadow Hand 灵巧手的 MJCF 定义与网格资产,专门针对 MJX(JAX 后端的 GPU 仿真)做了求解器、接触维度、几何类型和接触分组上的针对性改造。读完后你会掌握该模型的完整文件结构与参数含义,理解 MJX 中max_contact_points/max_geom_pairs这类"静态定容接触"机制的源码实现,并能用几行 Python 把整个场景在 JAX 上跑起来。
一、模型包概览:是什么、包含哪些文件
README 对该包的定位是:
- 内容为 "E3M5" 版本 Shadow Hand 机器人手的资产,包含右利手与左利手两个版本;
- 原始 URDF 与资产由 Shadow Robot 公司直接提供,采用 Apache 2.0 许可证发布;
- 要求 MuJoCo 3.0.0 或更高版本。
当前仓库中该目录的实际结构如下:
| 文件 | 作用 |
|---|---|
| right_hand.xml | 右手本体模型:16 个 body、24 个关节、24 个位置控制器执行器 |
| scene_right.xml | 场景文件:<include>手模型,加入地面与一个自由关节球体 |
| assets/ | 16 个 OBJ 网格(前臂、腕部、手掌、指骨、拇指各节、碰撞网格等) |
| shadow_hand.png | 场景渲染示意图 |
| README.md | 本包说明文档 |
需要说明的一点是:README 声称该包同时包含左利手版本,但从当前仓库的目录内容看,test_data/shadow_hand下只提交了右手模型(right_hand.xml/scene_right.xml),左手文件不在本仓库快照中。
二、相对原版 Menagerie 模型的五个 MJX 适配改造
README 明确列出,该模型相对原版 Menagerie Shadow Hand 为 MJX 仿真做了如下修改。下面逐条结合模型文件与 MJX 源码给出佐证。
1. 求解器切换为 CG,8 次迭代
README 原文:"Solver switched to CG with 8 iterations"。共轭梯度(CG)是迭代型求解器,在 JAX 的静态计算图中迭代次数必须编译期确定,而 Newton 求解器的约束规模在运行时可能变化,因此 MJX 类模型普遍倾向 CG。提交到仓库的 right_hand.xml 的<option>块进一步给出了配套数值:impratio="10"、iterations="1"、ls_iterations="4"、timestep="0.001",并通过<flag eulerdamp="disable"/>关闭欧拉阻尼。其中 1 ms 时间步对 24 个自由度、接触密集的灵巧手来说是保证接触稳定的关键参数。
2. 接触维度统一为 condim=3
README 原文:"Condim switched to 3 (MJX does not yet support condim != 3)"。在 scene_right.xml 中可以看到被抓握球体显式声明condim="3"(即 3 维法向接触,不含切向摩擦/扭转约束的扩展维度)。
源码层面,MJX 的接触定容函数make_condim按condim对接触组分桶、累加每桶固定尺寸;接触求解也是按 condim 分组分别进行的(见 collision 函数)。全部 geom 保持同一 condim,使得接触数组布局规则、可静态推断,这正是 MJX 只支持 condim=3 的原因。
3. 抓握物由椭球改为球体
README 原文:"Object converted from ellipsoid to sphere (MJX does not yet support ellipsoids)"。scene_right.xml中的物体 geom 印证了这一点:
<body name="object" pos="0.3 0 0.1"> <freejoint/> <geom type="sphere" size="0.03" rgba="0.5 0.7 0.5 1" condim="3" priority="1" friction="0.5 0.01 0.003" contype="7"/> </body>半径 3 cm、freejoint自由运动的球,摩擦系数(0.5, 0.01, 0.003)(滑动摩擦 + 滚动 + 扭转)。从源码结构看,MJX 的碰撞内核按几何类型对查表分发碰撞函数(collision_driver.py 中的_COLLISION_FUNC),不支持的类型对会直接抛错,因此椭球物体无法进入现有函数表,改球体是最低成本的适配。
4. 手指与物体之间建立显式接触
从模型文件的 contact 分组设置看,这里通过contype/conaffinity位掩码实现了"显式配对",而不是依赖全默认可接触:
| 部件 | 关键设置 | 语义 |
|---|---|---|
| 手塑料件(碰撞 geom) | contype="1" conaffinity="1"(plastic_collisionclass) | 手内自接触 |
| 指尖/拇指远端碰撞 mesh | contype="0" conaffinity="2" | 只主动接触 bit 2 组 |
| 球体 | contype="7"(= 1+2+4) | 可与 bit 1(手)、bit 2(指尖)、bit 4(地面)三组接触 |
| 地面平面 | conaffinity="4" contype="0" | 只被动接受 bit 4 组的接触 |
这样球与指尖(bit 2)、球与手掌(bit 1)、球与地面(bit 4)三对接触被精确圈定,避免了全组合接触搜索,也避免了手指与球之间因 mesh 细节产生的冗余接触。配合下文第五点的max_contact_points=15,整个场景的接触数组规模被钉死在 15 条。
5. 移除手臂基座中未使用的 geom
README 原文:"Removed some unused geoms in the arm base"。对比 right_hand.xml 的前臂 bodyrh_forearm:它只保留forearm_0/forearm_1两个视觉 mesh、一个forearm_collision碰撞 mesh 和一个补位的小 box,基座(mounting_plate.obj虽有资产文件但未在模型中作为可运动部件使用)相关的冗余几何已删除。从源码结构看,减少 geom 数量会直接减少碰撞分组与定容阶段的计算量,对 MJX 的编译时间与运行时开销都有利。
三、模型定义逐段解析
right_hand.xml:编译选项、执行器分层与 body 树
文件头 有三个值得注意的设置:
<mujoco model="right_shadow_hand"> <compiler angle="radian" meshdir="assets" autolimits="true"/> <option impratio="10" iterations="1" ls_iterations="4" timestep="0.001"> <flag eulerdamp="disable"/> </option> <custom> <numeric data="15" name="max_contact_points"/> <numeric data="15" name="max_geom_pairs"/> </custom>angle="radian":所有关节限位以弧度书写(如-0.523599 0.174533即 -30°~10°);autolimits="true":为未显式给出限位的关节自动推导限位;- 两个
custom numeric是MJX 专用的接触预算,含义见下一节。
defaults 层级 用 class 体系把 24 个关节的阻尼、臂化惯量、位置控制参数集中管理。基准<joint axis="1 0 0" damping="0.05" armature="0.0002"/>加<position forcerange="-1 1"/>,再按关节类别覆写。各执行器 class 的关键参数如下(全部摘自 XML):
| class | 关节范围 (rad) | kp | forcerange |
|---|---|---|---|
wrist_y | -0.523599 ~ 0.174533 | 10 | -10 ~ 10 |
wrist_x | -0.698132 ~ 0.488692 | 8 | -5 ~ 5 |
thbase | -1.0472 ~ 1.0472(绕0 0 -1) | 0.4 | -3 ~ 3 |
thproximal | 0 ~ 1.22173 | 默认 | -2 ~ 2 |
thhub | -0.20944 ~ 0.20944 | 0.5 | 默认 |
thmiddle | -0.698132 ~ 0.698132(绕0 -1 0) | 1.5 | 默认 |
thdistal | -0.261799 ~ 1.5708 | 默认 | 默认 |
metacarpal | 0 ~ 0.785398(绕0.573576 0 0.819152) | 默认 | 默认 |
knuckle | -0.349066 ~ 0.349066 | 默认 | 默认 |
proximal | -0.261799 ~ 1.5708 | 默认 | 默认 |
middle_distal | 0 ~ 1.5708 | 0.5 | 默认(ctrlrange 放宽到 0 ~ 3.1415) |
腕部 class 额外覆写了damping="0.5",远高于指关节的 0.05,因为腕关节直接连接质量 3 kg 的前臂,需要更强的速度阻尼抑制摆动。
body 树 的链式结构为:
rh_forearm (3 kg) └── rh_wrist (0.1 kg) [rh_WRJ2, rh_WRJ1] └── rh_palm (0.3 kg) ├── rh_ffknuckle → rh_ffproximal → rh_ffmiddle → rh_ffdistal (示指 J4~J1) ├── rh_mfknuckle → ... (中指 J4~J1) ├── rh_rfknuckle → ... (无名指 J4~J1) ├── rh_lfmetacarpal → rh_lfknuckle → ... (小指 J5~J1) └── rh_thbase → rh_thproximal → rh_thhub → rh_thmiddle → rh_thdistal (拇指 J5~J1)每个 body 都带显式<inertial>(质量、质心偏移、对角惯量),指尖的视觉 mesh(如f_distal_pst.obj)与碰撞 mesh(f_distal_pst_214.obj,214 面片的简化版)分离,这是"视觉高保真、碰撞低开销"的典型做法。接触排除 只有两条:rh_wrist/rh_forearm与rh_thproximal/rh_thmiddle,防止相邻链体自接触。
actuator 段 是 24 个position执行器,与关节一一对应,ctrl向量即 24 维关节目标角。文件末尾还有一段被注释掉的<tendon>(L285-L302),原本用于把四指的 PIP+DIP(J2+J1)合并为单自由度肌腱驱动,MJX 版本保持注释状态未启用。
scene_right.xml:地面、物体与相机取景
完整场景文件 只有 30 行:
<mujoco model="right_shadow_hand scene"> <include file="right_hand.xml"/> <statistic extent="0.3" center="0.3 0 0"/> <worldbody> <light pos="0 0 1"/> <light pos="0.3 0 1.5" dir="0 0 -1" directional="true"/> <geom name="floor" pos="0 0 -0.1" size="0 0 0.05" type="plane" material="groundplane" contype="0" conaffinity="4"/> <body name="object" pos="0.3 0 0.1"> <freejoint/> <geom type="sphere" size="0.03" rgba="0.5 0.7 0.5 1" condim="3" priority="1" friction="0.5 0.01 0.003" contype="7"/> </body> </worldbody> </mujoco>- 球初始位于手正前方
(0.3, 0, 0.1),恰好落在<statistic>的取景中心; - 地面平面
contype="0"表示它不主动向任何组发起接触,只通过conaffinity="4"被动响应球体(contype=7中含 bit 4); - 球的
priority="1"使球-手接触的求解优先级高于手内部接触,防止指尖被手掌压穿。
四、MJX 静态接触机制:两个 custom numeric 的底层实现
MJX 基于 JAX/XLA,计算图在jax.jit编译时要求所有数组形状静态已知,而 MuJoCo 中接触点数量是运行时量。该模型包通过两个custom numeric给 MJX 提供了"接触预算",MJX 据此在建模期就把接触数组大小钉死。实现链路在 collision_driver.py 中:
- 建模期定容:
make_condim遍历 geom 分组,按每个碰撞函数声明的ncon与组内 pair 数估算接触上限;若模型声明了max_contact_points,则对每个 condim 桶取min(上限, max_contact_points)。本模型两个 numeric 均为 15,于是整条接触数组恰好 15 条 condim-3 接触(45 个标量约束)。 - 运行时裁剪:
collision在真正求交时执行两级 top-k:max_geom_pairs:先用粗阶段距离‖p2-p1‖-(r1+r2)做jax.lax.top_k,只保留最可能穿透的 geom 对进入昂贵的精确碰撞函数(L421-L430);max_contact_points:碰撞函数产出接触后,按穿透深度dist再取 top-k,截断到预算内(L449-L455)。
测试用例给出了直接证据:
test_ncon_meshes直接加载shadow_hand/scene_right.xml,断言开启max_contact_points时接触定容为15;把它置 -1 后定容为98——这正是该场景在预算外的真实接触规模,15/98 的比例直观展示了预算对计算量的压缩;support_test.py的test_custom以right_shadow_hand模型为载体,验证name2id能从 MjModel 和 mjx.Model 两边读回这两个 numeric,即 custom 数据在 CPU/GPU 两条模型路径上一致透传。
仓库中其他 MJX 测试模型也用同一机制做参数扫描,例如 humanoid 01~10 系列把max_contact_points从 4 逐步放到 40,用于批量仿真时的性能-精度权衡实验。
五、动手运行:用 MJX 加载该场景
以下示例的 API 调用方式与仓库内测试(如 support_test.py 中put_model→put_data→jax.jit(mjx.step)的模式)一致。在仓库根目录执行,meshdir="assets"的相对路径会以模型文件所在位置为基准解析,因此直接按路径加载即可:
import jax import jax.numpy as jnp import mujoco import mujoco.mjx as mjx SCENE = 'mjx/mujoco/mjx/test_data/shadow_hand/scene_right.xml' m = mujoco.MjModel.from_xml_path(SCENE) # 需要 MuJoCo 3.0.0+ d = mujoco.MjData(m) mx = mjx.put_model(m) # 此处按 custom numeric 定容接触数组(15条) dx = mjx.put_data(m, d) step = jax.jit(mjx.step) targets = jnp.zeros(m.nu) # 24 维位置控制: 全关节目标 0 for _ in range(500): dx = dx.replace(ctrl=targets) dx = step(mx, dx) print('t =', dx.time, ' palm xpos =', dx.xpos[1])要点:
mjx.put_model阶段接触数组大小即由模型里的max_contact_points=15决定,之后不可变;timestep=0.001来自<option>,mjx.step每步推进 1 ms,500 步即 0.5 s 仿真;- 若想让手去抓球,可把
targets改为一组屈曲角度(如各指 J1~J4 给 0.3~1.0 rad 的目标),腕部两维保持 0; - 由于 MJX 只支持 condim=3,如需扩展维度(如 condim=6 的摩擦锥切向力),该模型文件本身已按 README 说明做了统一,直接沿用即可。
六、使用限制与注意事项
- 版本前提:模型要求 MuJoCo 3.0.0 及以上(README 明确要求);MJX 的 JAX 后端需与当前仓库
mjx/包配套安装。 - MJX 能力边界:本模型的两项几何/维度改动(球体替代椭球、统一 condim=3)反映的是 MJX 当前能力边界,来自 README 的明确说明;如果未来 MJX 支持椭球或更多 condim,可以按原版 Menagerie 模型回退这些妥协。
- 接触预算是硬上限:15 条接触点在快速握持、多指同时压球等场景可能不够,会出现接触被 top-k 截断(io.py 中对预算过低的处理 也提示了这一点);需要更精细接触时可上调两个 numeric,但接触数组变大意味着 JIT 编译产物更大、每步开销更高。
- 左右手:README 提到包内含左右手两版,但当前仓库仅提交了右手模型文件,左手版本请以模型包上游为准。
- 许可:模型资产为 Apache 2.0(README 声明,资产来自 Shadow Robot 公司),可用于研究与再分发。
小结:MJX Shadow Hand 包的价值不仅在于提供了一只高保真灵巧手模型,更在于它示范了"如何让一个接触密集模型跑进 JAX 静态计算图"的完整套路——统一 condim、几何类型降级到受支持集合、用 contype/conaffinity 圈定接触对、再用max_contact_points/max_geom_pairs两个 custom numeric 把运行时接触量钉成编译期常量。这套模式同样适用于仓库内其他 MJX 测试模型,也是把自有机器人模型迁移到 GPU 批量仿真时的参考模板。
【免费下载链接】mujocoMulti-Joint dynamics with Contact. A general purpose physics simulator.项目地址: https://gitcode.com/GitHub_Trending/mu/mujoco
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考