Files
liyang aed6e20a90 双臂操作沙盒(MuJoCo WASM 浏览器版)首次入库
从 ~/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
2026-09-01 17:16:41 +08:00

78 lines
4.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 双臂操作沙盒(浏览器 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/.venvmujoco、numpy、tyro+
linker-sim 资产库 + SO-101 MJCF,跑页面不需要它
```
完整项目(其它页面 arm/sort/policy/urdf 等)仍在 `~/mujoco-web-spike`,本包只是双臂抓取这一条线。