aed6e20a90
从 ~/mujoco-web-spike 抽出 manip.html 的依赖闭包:
- manip.html + src/{manip,autograsp,scene-bridge,math}.js
- scenes/so101_pair(桌面并排两台 SO-101,默认)与 scenes/p7_bimanual(P7 双臂 + SO-101 夹爪)
—— 自包含 MJCF,网格已内联
- serve.py(COOP/COEP + no-store 的静态服务器)、start.sh、README
- tools/export_manip_scene.py 场景生成脚本(仅供参考,跑页面不需要)
快照对应 2026-08-31 的最后一次改动(自动抓取规划 autograsp.js 落地版)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tgfYxR5mWNRKQ67PKga4T
78 lines
4.5 KiB
Markdown
78 lines
4.5 KiB
Markdown
# 双臂操作沙盒(浏览器 WASM 版)
|
||
|
||
从 `~/mujoco-web-spike` 抽出来的**自包含**子集:只含 `manip.html` 跑起来需要的文件。
|
||
物理引擎是 MuJoCo 的 WASM 构建,全部在浏览器里运行,服务器只负责发静态文件。
|
||
版本快照:2026-08-31 最后一次改动(自动抓取规划 `autograsp.js` 落地那一版)。
|
||
|
||
## 启动
|
||
|
||
```bash
|
||
tar xzf wasm-bimanual-grasp-20260901.tar.gz
|
||
cd wasm-bimanual-grasp-20260901
|
||
python3 serve.py 8000 # 或 ./start.sh 8000
|
||
```
|
||
|
||
然后用 **Chrome / Edge** 打开:
|
||
|
||
| 场景 | 地址 |
|
||
|---|---|
|
||
| 默认:桌面上并排两台 SO-101(5 自由度臂 + 二指夹爪) | http://127.0.0.1:8000/manip.html |
|
||
| P7 双臂工作站,末端换成 SO-101 夹爪(7 自由度臂) | http://127.0.0.1:8000/manip.html?scene=p7_bimanual |
|
||
|
||
首屏会依次显示「载入 MuJoCo WASM → 下载场景 → 编译 MJCF」,编译要几秒(网格已内联,
|
||
so101_pair 1.8 MB / p7_bimanual 5.3 MB)。
|
||
|
||
### 环境要求
|
||
|
||
- **Python 3**(只用标准库 `http.server`,不需要 venv、不需要 pip 装任何东西)
|
||
- **能上网**:three.js 0.170.0 和 `@mujoco/mujoco` WASM 都从 jsDelivr CDN 拉,断网会白屏
|
||
- **桌面版 Chrome / Edge**:页面用到 SharedArrayBuffer(多线程 WASM),
|
||
`serve.py` 已经发好 `Cross-Origin-Opener-Policy` / `Cross-Origin-Embedder-Policy` 两个头
|
||
|
||
### 为什么不能直接 `python3 -m http.server`
|
||
|
||
可以起来,但页面会**卡在「初始化 MuJoCo…」不报错也不动**——
|
||
`@mujoco/mujoco` 是 pthread 构建,浏览器只在"跨源隔离"页面里放开 SharedArrayBuffer,
|
||
而 `http.server` 不发那两个头。`serve.py` 就是 `http.server` + 这两个头 + `Cache-Control: no-store`
|
||
(ES 模块缓存极死,改了 `src/*.js` 普通刷新照样跑旧代码,所以开发期一律不缓存)。
|
||
|
||
## 页面怎么用
|
||
|
||
- **拖动物体**(鼠标直接拖桌上的易拉罐/瓶子/罐子/玩具琴/玩偶)→ 右侧实时出**判定卡**:
|
||
自动选哪只臂、下爪方向罗盘、左右两臂 IK 残差并排;两臂都够不着会明确给出「不可抓」
|
||
- **自动抓取** 按钮:按判定结果执行 抵近 → 预收爪口 → 下探 → 合爪 → 抬升;**中止** 随时停
|
||
- **手动模式**:选 左臂/右臂 → `X± Y± Z±` 步进末端 → 朝向约束(朝下/朝前/不约束)→ 张开/夹紧
|
||
- 物体面板可改**位置和重量**,点 **应用** 生效(运行时改 `body_mass`,不重编译)
|
||
- **抓放记录**:物体离抓取点 < 9 cm 且高于原位 5 cm 并保持 0.5 s = 抓取成功;
|
||
松手后静止落在台面 = 放置成功;掉到台下 = 失败
|
||
|
||
## 已知状态(08-31 实测)
|
||
|
||
- 判定侧 6/6 准确(含正确拒绝够不着的情况)
|
||
- 执行侧 **3/5**:立着的易拉罐、罐子、玩偶稳定成功;**躺倒的瓶子和玩具琴还抓不住**。
|
||
根因是二指爪全开时爪尖比爪中点低 2.6 cm,下探先顶到桌面;已加「按物体宽度预收爪口」
|
||
但仍不够,尝试过的三种夹持点选择策略都没救回来,细节见 `src/autograsp.js` 顶部注释
|
||
- **切到别的标签页仿真会暂停**(浏览器对后台标签的 rAF 节流),抓取过程中别切走
|
||
- `@mujoco/mujoco` 走的是 CDN 上的 `+esm` 最新版(打包时 jsDelivr latest = 3.12.0),
|
||
没有钉版本;如果哪天 CDN 升级后页面报 `xxx is not a function`,先把 `manip.html` 里的
|
||
`@mujoco/mujoco/+esm` 改成 `@mujoco/mujoco@3.12.0/+esm` 试
|
||
|
||
## 文件
|
||
|
||
```
|
||
manip.html 页面 + 启动编排(渲染、UI、事件)
|
||
serve.py 静态服务器(COOP/COEP + no-store)
|
||
start.sh python3 serve.py 的包装,顺带打印地址
|
||
src/manip.js ArmController(阻尼最小二乘 IK、零空间次要目标)、GraspMonitor(抓放判定)
|
||
src/autograsp.js GraspPlanner:拖动后实时判定选臂/下爪方向,自动抓取状态机
|
||
src/scene-bridge.js MuJoCo mjModel/mjData → three.js 网格与每帧位姿
|
||
src/math.js 旋转矩阵 → 四元数
|
||
scenes/so101_pair/ 默认场景:model.xml(自包含 MJCF)+ meta.json(臂/爪/物体索引)
|
||
scenes/p7_bimanual/ P7 双臂场景,同上
|
||
tools/export_manip_scene.py ★ 仅供参考:生成上面两个场景的脚本,
|
||
依赖 ~/unitree_rl_mjlab/.venv(mujoco、numpy、tyro)+
|
||
linker-sim 资产库 + SO-101 MJCF,跑页面不需要它
|
||
```
|
||
|
||
完整项目(其它页面 arm/sort/policy/urdf 等)仍在 `~/mujoco-web-spike`,本包只是双臂抓取这一条线。
|