在浏览器中运行 MuJoCo 物理仿真,加载 mujoco_menagerie 的 xArm7 模型,并用 Three.js 渲染。逆运动学(IK)来自同级仓库 mujoco-wasm-ik:minc + DAQP 编进同一份 mujoco.wasm,支持拖拽末端目标、关节限位与速度约束。
本仓库是应用,不含 MuJoCo 源码与 WASM 编译树。
- MuJoCo WASM 物理步进(
mj_step) - Three.js 将
mjvScene同步为THREE.Mesh渲染(见 docs/rendering-pipeline.md) - xArm7 + 地面 + 可抓取物体(球 / 盒)
- 基于 minc 的微分 IK:
FrameTask+PostureTask+solveIK - TransformControls 拖拽橙色目标球控制末端位姿
| 依赖 | 用途 |
|---|---|
| Node.js ≥ 18 | 前端开发与 WASM 测试脚本 |
| pnpm | 包管理(亦可用 npm) |
同级目录 ../mujoco-wasm-ik |
带 IK 的 MuJoCo WASM npm 包 |
目录布局:
mujoco-project/
├── mujoco-wasm-ik/ # 库:WASM + IK API
└── vite-project/ # 本应用
package.json 通过 "mujoco-wasm-ik": "file:../mujoco-wasm-ik" 引用。库内已有预编译 dist/,仅运行前端无需安装 Emscripten。
pnpm install
pnpm dev浏览器打开 Vite 提示的本地地址(默认 http://localhost:5173)。
| 操作 | 按键 / 方式 |
|---|---|
| 暂停 / 继续仿真 | Space 或 HUD「Pause」 |
| 重置场景 | Backspace 或 HUD「Reset」 |
| 开关 IK | HUD「IK: On/Off」 |
| 显示 / 隐藏接触点 | C 或 HUD「Show Contacts」 |
| 平移 IK 目标 | 拖拽橙色球,或 T 切换平移模式 |
| 旋转 IK 目标 | R 切换旋转模式 |
| 旋转视角 | 鼠标拖拽(OrbitControls) |
vite-project/
├── index.html # 页面入口与 HUD
├── vite.config.js # 允许读取同级 mujoco-wasm-ik
├── package.json
├── src/
│ ├── main.js # 仿真主循环、模型加载、Three.js 渲染、IK 交互
│ └── arm-ik.js # IK 任务 / 限位封装(FrameTask、PostureTask 等)
├── scripts/
│ ├── xarm7_smoke.mjs
│ └── xarm7_collision_smoke.mjs
├── public/
│ └── models/
│ ├── ufactory_xarm7/ # 机械臂 MJCF + STL
│ └── grasp_objects/ # 可拾取物体(OBJ/PNG + 物体 MJCF)
└── docs/
├── load-model.md
├── rendering-pipeline.md
├── add-grasp-object.md
└── import-usd-grasp-object.md
| 路径 | 说明 |
|---|---|
src/main.js |
应用入口:VFS 挂载模型 → MjModel → 仿真循环 → Three.js 绘制 |
src/arm-ik.js |
xArm7 专用:7 轴 ctrl、夹爪、DofFreezingTask |
public/models/ufactory_xarm7/scene.xml |
场景入口:地面、机械臂、抓取物体 |
../mujoco-wasm-ik |
WASM + IkConfiguration / solveIK(机械臂无关) |
物理与 IK 在同一份 mujoco.wasm 里,由 mujoco-wasm-ik 提供:
import loadMujoco from "mujoco-wasm-ik";本应用只写手臂 7 轴 ctrl[0..6],夹爪走 ctrl[7],不对仿真 mjData 做 integrateInplace。
改 IK 绑定、minc、DAQP 或 MuJoCo 本体时,到 mujoco-wasm-ik 按该仓库 README 的构建 SOP 重编(默认 -DMUJOCO_WASM_THREADS=OFF)。只改 src/arm-ik.js / src/main.js 时刷新页面即可。
验证:
# 库:通用 IK smoke
cd ../mujoco-wasm-ik && npm run smoke
# 本应用:xArm7
pnpm run smoke:xarm7
pnpm run smoke:collisionscene.xml ──► MjModel + MjData
│
mj_step │ mjv_updateScene
▼
mjvScene.geoms ──► THREE.Mesh(Three.js 渲染)
│
solveIK │ 写 ctrl(不要 integrateInplace)
▼
qpos / ctrl ──► 物理仿真
- 物理与可视化抽象:MuJoCo WASM(
mujoco-wasm-ik) - 像素绘制:Three.js(灯光、相机、材质均为 Three 侧)
- IK 求解:minc + DAQP,经库的
ik_bindings.cc暴露给 JavaScript
浏览器里只有一份 WASM。它同时做物理步进和微分 IK;Three.js 只负责把 mjvGeom 画出来。JS 不另开 IK 进程,也不对仿真 mjData 调用 integrateInplace。
flowchart TB
subgraph ui [浏览器 UI]
HUD[index.html HUD]
Keys[键盘 WASD / 夹爪]
Orbit[OrbitControls]
end
subgraph js [JavaScript]
Main[src/main.js]
ArmIk[src/arm-ik.js]
end
subgraph wasm [mujoco.wasm 同一模块]
Physics[mj_step 接触 重力]
Viz[mjv_updateScene]
IK[minc + DAQP solveIK]
VFS[MEMFS /working]
end
subgraph three [Three.js]
Mesh[THREE.Mesh]
Light[灯光 相机]
GPU[WebGLRenderer]
end
HUD --> Main
Keys --> Main
Orbit --> Light
Main --> ArmIk
ArmIk -->|写 data.ctrl 0..6| Physics
Main -->|写 data.ctrl 7 夹爪| Physics
Main -->|fetch XML/OBJ/STL/PNG| VFS
VFS --> Physics
Physics --> Viz
Viz -->|mjvGeom 列表| Mesh
Mesh --> GPU
Light --> GPU
| 层 | 职责 | 不负责 |
|---|---|---|
main.js |
加载模型、键盘/HUD、每帧调度、Three 网格与贴图 | 不解 QP、不算接触 |
arm-ik.js |
组 IK 任务/限位,把 solveIK 速度积成 7 轴 ctrl |
不改夹爪、不挪桌上物体 |
| MuJoCo | mj_step 伺服、接触、摩擦;mjv_updateScene 出绘制指令 |
不画像素 |
| minc/DAQP | 在同一 WASM 内求关节速度 | 不直接改 qpos |
| Three.js | Mesh、灯光、相机、DataTexture | 不参与物理 |
机械臂与可拾取物体目录分开。MJCF 在浏览器里不能读磁盘,必须先写入 WASM 虚拟文件系统。
flowchart LR
subgraph disk [public/models]
ArmXML[ufactory_xarm7/scene.xml]
ArmSTL[assets/*.stl]
ObjXML[grasp_objects/*/物体.xml]
ObjMesh[*.obj *.png]
end
subgraph vfs [VFS /working]
WScene[scene.xml]
WArm[xarm7.xml + assets]
WObj[grasp_objects/...]
end
disk -->|XARM7_FILES| vfs
disk -->|GRASP_OBJECT_FILES| vfs
WScene -->|include| WArm
WScene -->|include| WObj
vfs -->|MjModel.from_xml_path| Model[MjModel + MjData]
- 入口:
scene.xmlinclude 臂本体 + 各物体包(物体包不要写<compiler meshdir>,以免覆盖臂的assets/)。 - Reset 时
homekeyframe 不含桌上freejoint,由GRASP_OBJECT_POSES写回qpos。 - 当前 WASM 不能加载 USD;物体网格用 OBJ/STL,贴图必须 PNG。
requestAnimationFrame → update() → render()。未 Pause 时顺序固定:先改目标与 ctrl,再 mj_step,最后同步画面。
sequenceDiagram
participant User as 用户
participant Main as main.js
participant IK as solveIK
participant Phys as mj_step
participant Viz as mjv_updateScene
participant Three as Three.js
User->>Main: 键盘改 targetMesh / 夹爪
alt IK On
Main->>IK: 目标位姿 + FrameTask + PostureTask
Note over IK: DofFreezingTask 冻住非 joint1-7
IK-->>Main: 7 轴速度
Main->>Phys: 只写 data.ctrl 0..6
end
Main->>Phys: data.ctrl 7 夹爪
Phys->>Phys: 位置伺服 + 接触 + 重力
Phys-->>Viz: 更新后的 qpos
Viz-->>Main: mjvGeom 列表
Main->>Three: 几何 / 贴图 / 世界矩阵
Three->>User: WebGL 画面
关键约束:
- IK 只写手臂 7 轴
ctrl,不写qpos,不对仿真mjData做integrateInplace。 - 夹爪走腱驱动
ctrl[7]([开 /]合),IK 不碰。 - 桌上物体带
freejoint,物理里自由;IK 里被DofFreezingTask冻住,避免 QP 把方块当关节挪走。 - 碰撞与夹取交给接触力,不再挂
CollisionAvoidanceLimit。
flowchart TB
Target[targetMesh 位姿] --> EE[FrameTask site link_tcp]
Posture[PostureTask 弱正则]
Freeze[DofFreezingTask 非臂 DOF]
Lim[ConfigurationLimit + VelocityLimit]
EE --> QP[solveIK DAQP]
Posture --> QP
Freeze --> QP
Lim --> QP
QP --> Vel[nv 维速度]
Vel --> Ctrl["ctrl[i] = qpos + vel * dt"]
Ctrl --> Act[actuator 位置伺服]
目标球是 JS 里的 targetMesh(可隐藏),不是地上的 grasp_sphere。
不使用 mjr_render。mjv_updateScene 产出要画的 geom;Three 侧建 BufferGeometry + MeshPhongMaterial,每帧只更新世界矩阵。
flowchart LR
Data[mjData] --> Update[mjv_updateScene]
Update --> Geom[mjvGeom]
Geom -->|type/size/dataid| Geo[BufferGeometry]
Geom -->|texid + tex_data| Tex[DataTexture]
Geom -->|mat + pos| Mat4[mesh.matrix]
Geo --> Mesh[THREE.Mesh]
Tex --> Mesh
Mat4 --> Mesh
Lights[Ambient Point Spot] --> GPU[WebGLRenderer]
Mesh --> GPU
- 灯光、相机、阴影在 Three 侧;XML 里的
<light>不照亮画面。 geomgroup[3]=0隐藏夹爪 collision pad。- Mesh UV 按面展开(OBJ 的
vt常多于v);贴图从mjModel.tex_data拷出,WASM 的宽高/偏移需转成Number。
flowchart TB
subgraph app [本应用]
index[index.html]
main[src/main.js]
ikjs[src/arm-ik.js]
end
subgraph assets [静态资源]
scene[scene.xml]
xarm[xarm7.xml]
grasp[grasp_objects]
end
subgraph lib [mujoco-wasm-ik]
dist[dist/mujoco.js]
bind[codegen/generated/bindings.cc]
ikcc[overlay/ik_bindings.cc]
minc[minc + daqp + eigen]
end
index --> main
main --> ikjs
main -->|import mujoco-wasm-ik| dist
main --> scene
scene --> xarm
scene --> grasp
dist --> bind
dist --> ikcc
ikcc --> minc
| 文件 | 角色 |
|---|---|
vite.config.js |
允许读取同级 mujoco-wasm-ik,把 .wasm 当静态资源 |
src/main.js |
VFS、主循环、键盘、Three 同步、Reset |
src/arm-ik.js |
xArm7 IK 封装;导出 createArmIk / applyArmIkToCtrl |
public/models/ufactory_xarm7/ |
臂 + 地面场景,不含物体 mesh |
public/models/grasp_objects/ |
每物体一目录:xml + obj + png |
../mujoco-wasm-ik/overlay/ik_bindings.cc |
库侧唯一允许改的 IK Embind;勿改官方 bindings.cc |
更多细节:
- docs/load-model.md — 浏览器 VFS 加载外部 MJCF
- docs/rendering-pipeline.md —
mjvGeom→THREE.Mesh流程 - docs/add-grasp-object.md — 可拾取物体
- docs/import-usd-grasp-object.md — USD/OBJ 接入
- mujoco-wasm-ik README — WASM 编译 SOP 与 IK API
pnpm dev # 启动 Vite 开发服务器
pnpm build # 生产构建(输出到 dist/)
pnpm preview # 预览生产构建
pnpm lint # ESLint
pnpm run smoke:xarm7 # xArm7 IK smoke| 组件 | 来源 |
|---|---|
| mujoco-wasm-ik | 同级库仓库(Apache-2.0;上游见该仓库 NOTICE) |
| xArm7 模型 | mujoco_menagerie / ufactory_xarm7 |