1eb05132f1
build / setup (compute matrix) (pull_request) Has been cancelled
build / ${{ matrix.label }} (pull_request) Has been cancelled
build / macos-15-arm64-studio (pull_request) Has been cancelled
build / ubuntu-24.04-clang-18-studio (pull_request) Has been cancelled
build / ubuntu-24.04-gcc-14-studio (pull_request) Has been cancelled
build / windows-2025-ninja-studio (pull_request) Has been cancelled
build / ubuntu-24.04-clang-18-wasm (pull_request) Has been cancelled
build / ubuntu-24.04-clang-18-mjx (pull_request) Has been cancelled
lint / pre-commit (pull_request) Has been cancelled
181 lines
15 KiB
Markdown
181 lines
15 KiB
Markdown
# MuJoCo Web 仿真平台实施计划
|
||
|
||
> **计划状态**:✅ MVP 已实施并通过自动化验证
|
||
> **进度维护规则**:实施时将阶段/任务复选框由 `[ ]` 更新为 `[x]`,并同步“进度看板”中的状态、完成日期与备注。
|
||
|
||
## Context
|
||
|
||
目标是在本仓库官方 `wasm/` JavaScript/TypeScript 绑定和 Three.js 示例之上,建设一个纯浏览器 MuJoCo 仿真平台,支持模型工程上传、浏览器内文件组织、模型加载、三维展示、仿真控制和状态查看,交互布局参考 `urdf.enkeebot.com`。
|
||
|
||
已确认的仓库基础:
|
||
|
||
- `wasm/dist/` 有本地构建的绑定产物,但当前 `build/CMakeCache.txt` 显示其启用了线程;MVP 因此使用同版本官方 `@mujoco/mujoco` 默认单线程入口。
|
||
- `wasm/demo_app/` 已有 Vite + TypeScript + Three.js 的最小示例,可作为加载、场景构建与逐帧同步的起点。
|
||
- 官方绑定要求显式调用 `.delete()` 管理 Embind/C++ 对象生命周期。
|
||
- 官方同时支持默认单线程入口与需要 COOP/COEP 的多线程入口;MVP 已确定使用默认单线程入口。
|
||
|
||
## Scope / MVP 边界
|
||
|
||
- **技术栈**:React + TypeScript + Zustand + Vite + TailwindCSS + Three.js。
|
||
- **输入**:单个 MJCF/XML、单个 URDF、保留相对路径的文件夹、ZIP 工程;关联 mesh/贴图随工程导入。
|
||
- **编辑边界**:首版只负责导入、查看和仿真,不提供 XML 编辑、热重载、工程保存或导出。
|
||
- **物理执行**:首版使用主线程、单线程 `mujoco.wasm`;模块边界保留未来迁移 Worker 的能力,但不在 MVP 实现 Worker。
|
||
- **控制优先级**:actuator 滑杆、关节拖动、外力施加,另含播放、暂停、单步、重置和速度控制。
|
||
- **部署边界**:桌面浏览器中的中文界面,通过本地静态 HTTP 服务器运行;无 PWA、账号、后端、云存储或分享服务,导入工程仅驻留当前浏览器会话内存。
|
||
- **非目标**:URDF/MJCF 互转、Xacro 展开、控制脚本、轨迹编辑与多线程 WASM。
|
||
|
||
## Approach
|
||
|
||
已采用 **React + TypeScript + Vite + TailwindCSS + Three.js**,在 `wasm/web_platform/` 新增独立平台应用,复用官方 WASM 包和示例逻辑,未改动 MuJoCo 核心绑定或生成产物。
|
||
|
||
初步分层:
|
||
|
||
1. **UI / 工程层**:工程导入、资源树、属性/控制面板、全局状态和错误展示。
|
||
2. **应用服务层**:工程文件规范化、入口模型识别、仿真会话生命周期、命令调度。
|
||
3. **物理层**:封装 `MjModel`、`MjData`、step/reset/ctrl,并集中管理 `.delete()`。
|
||
4. **文件系统层**:将上传文件按相对路径写入 Emscripten MEMFS,校验引用与加载错误。
|
||
5. **渲染层**:Three.js 场景、相机、灯光、MuJoCo primitive/mesh 映射和逐帧位姿同步。
|
||
6. **交互控制层**:Three.js Raycaster 完成选择;关节拖动映射到 `qpos` 并调用 `mj_forward`,外力拖拽通过 `MjvPerturb`/`mjv_applyPerturbForce` 在 step 前施加。
|
||
7. **线程边界**:MVP 直接调用主线程单线程绑定,但通过 `PhysicsAdapter` 隔离 UI,未来可在不改 UI 的情况下迁移 Worker。
|
||
|
||
### 数据流、错误与生命周期
|
||
|
||
- **数据流**:File/Directory/ZIP → 路径校验与入口识别 → MEMFS workspace → `mj_loadXML` → `SimulationSession` → `mjv_updateScene`/状态快照 → Three.js 与 Zustand UI。
|
||
- **状态边界**:Zustand 只保存可序列化的工程元数据、控制参数、选择和 UI 状态;`MjModel`/`MjData`/Three.js 实例由服务对象持有,避免 React 重渲染复制 WASM 视图。
|
||
- **错误模型**:按导入、ZIP、文件系统、模型编译、仿真、渲染分类,统一包含中文摘要、阶段、相关路径和原始错误;致命错误停止当前会话但保留工程树供排查。
|
||
- **生命周期**:新模型采用“先释放旧渲染资源和 Embind 对象 → 清理旧 workspace → 写入新工程 → 创建新会话”的串行切换;所有失败分支使用 `finally` 回收已创建对象。
|
||
|
||
## Files to modify
|
||
|
||
- `Plan.md`:计划与持续进度记录。
|
||
- `wasm/demo_app/app.ts`:只读复用来源,原则上不直接扩建为平台。
|
||
- `wasm/package.json`、`wasm/package-lock.json`:加入 React、Zustand、TailwindCSS、`fflate`、Vitest、Playwright 及平台脚本。
|
||
- `wasm/web_platform/index.html`、`wasm/web_platform/vite.config.ts`:平台入口、WASM 静态资源定位和相对路径静态部署配置。
|
||
- `wasm/web_platform/src/app/`:React 壳层、布局、全局错误边界与启动流程。
|
||
- `wasm/web_platform/src/stores/`:Zustand 工程、仿真、选择和 UI 状态切片。
|
||
- `wasm/web_platform/src/project/`:文件/目录/ZIP 导入、路径规范化、入口识别和 MEMFS 工作区。
|
||
- `wasm/web_platform/src/simulation/`:WASM 初始化、`PhysicsAdapter`、`SimulationSession`、步进和控制命令。
|
||
- `wasm/web_platform/src/viewer/`:Three.js renderer、MuJoCo 场景适配、拾取、关节拖动和外力交互。
|
||
- `wasm/web_platform/src/**/*.test.ts(x)`、`wasm/web_platform/e2e/`、`wasm/web_platform/fixtures/`:单测、浏览器测试与示例工程。
|
||
- `@mujoco/mujoco`:作为默认单线程构建输入;`wasm/dist/*` 未手工修改。
|
||
|
||
## Reuse
|
||
|
||
- `wasm/demo_app/app.ts`:复用 `loadMujoco()` 初始化、`mjv_updateScene` 场景提取、Z-up 相机、固定仿真时长步进、矩阵同步和确定性释放思路;现有 `getBufferGeometry` 仅覆盖 plane/sphere/capsule/box/cylinder/ellipsoid,mesh 与贴图需补齐,且不可直接复制其全局 `app`/DOM 写法。
|
||
- `wasm/demo_app/vite.demo.config.ts`:复用 `root`、相对 `base`、独立输出目录和开发响应头模式;MVP 单线程不依赖 COOP/COEP。
|
||
- `wasm/tests/bindings_test.ts`:复用 `FS.writeFile/unlink`、`MjModel.mj_loadXML(path)`、`MjVFS.addBuffer`、带外部 OBJ 的加载测试以及 `finally + .delete()` 生命周期范式。
|
||
- `@mujoco/mujoco` 类型:使用 `MjModel`/`MjData` typed-array 视图、named accessor、`MjvPerturb` 与 `mjv_applyPerturbForce` 等 API;避开 bool typed-memory-view 的绑定限制,并显式删除 accessor。
|
||
- `doc/overview.rst`、`doc/modeling.rst`、`doc/APIreference/functions_override.rst`:MuJoCo 原生 `mj_loadXML` 可解析 MJCF 与 URDF,VFS 可容纳 XML/include、STL、PNG 等资源,因此 URDF 不另做平台侧转换。
|
||
- 参考站点仅复用信息架构理念:本地导入、3D 视口、关节树/属性与控制面板;MVP 不复刻其编辑、格式转换或云端工具能力。
|
||
|
||
## Steps
|
||
|
||
### 进度看板
|
||
|
||
| 阶段 | 状态 | 完成度 | 完成日期 | 备注 |
|
||
|---|---|---:|---|---|
|
||
| 0. 需求与架构定稿 | 🟢 已完成 | 100% | 当前 | 产品边界、交互语义和架构已确认 |
|
||
| 1. 应用骨架与质量基线 | 🟢 已完成 | 100% | 当前 | typecheck、lint、Vitest、Playwright、build 通过 |
|
||
| 2. 工程导入与 MEMFS | 🟢 已完成 | 100% | 当前 | 单/多文件、目录、ZIP、安全限制与入口选择 |
|
||
| 3. MuJoCo 会话与仿真控制 | 🟢 已完成 | 100% | 当前 | 主线程单线程、固定步进、控制与确定性释放 |
|
||
| 4. Three.js 可视化与交互 | 🟢 已完成 | 100% | 当前 | primitive/mesh/texture、拾取、关节与外力交互 |
|
||
| 5. 平台 UI 与状态管理 | 🟢 已完成 | 100% | 当前 | 中文桌面布局、面板、属性和状态栏 |
|
||
| 6. 性能、健壮性与交付 | 🟢 已完成 | 100% | 当前 | 预算保护、夹具/E2E、静态部署文档 |
|
||
|
||
### 0. 需求与架构定稿
|
||
|
||
- [x] 确认 React + Zustand,采用 Vitest + Playwright;仅支持桌面版 Chrome/Edge/Firefox 最新两个大版本。
|
||
- [x] 确认 MVP 输入格式为 MJCF/XML、URDF、文件夹和 ZIP;工程入口规则见后续步骤。
|
||
- [x] 确认 MVP 使用主线程、单线程 WASM,不实现 Worker/MT。
|
||
- [x] 明确参考站点只借鉴本地导入、工程树、中心视口、属性/控制面板的信息架构。
|
||
- [x] 输出模块边界、数据流、错误模型和资源生命周期设计。
|
||
|
||
### 1. 应用骨架与质量基线
|
||
|
||
- [x] 创建独立平台应用入口、Vite 配置、TailwindCSS 和基础布局。
|
||
- [x] 配置 TypeScript 严格检查、lint、单元测试与最小 E2E 测试。
|
||
- [x] 配置 WASM 静态资源定位及开发/生产构建路径,保持 `base: './'` 以支持任意静态子路径。
|
||
- [x] 验证本地静态 HTTP 服务器可正确提供 JS/CSS/WASM;不支持直接以 `file://` 打开,也不注册 service worker。
|
||
- [x] 建立错误边界、加载状态、日志和中文可诊断错误展示。
|
||
|
||
### 2. 工程导入与 MEMFS
|
||
|
||
- [x] 定义 `ProjectFile`、虚拟路径、入口模型和资源清单模型。
|
||
- [x] 支持单文件、多文件和目录拖放/选择,保留 `webkitRelativePath`;使用 ZIP 库在浏览器内解包 `.zip`,拒绝加密包、目录穿越和超限压缩内容。
|
||
- [x] 统一 `/workspace/<project-id>/...` 虚拟根目录,规范化路径后通过 `FS.mkdirTree/writeFile` 写入 MEMFS;二进制文件保持 `Uint8Array`,文本 XML 使用 UTF-8。
|
||
- [x] 入口规则:单个 XML/URDF 自动选中;多候选优先根目录 `model.xml`/`scene.xml`/唯一 `.urdf`,否则弹窗选择且按根元素 `<mujoco>`/`<robot>`标识类型。
|
||
- [x] 使用 `MjModel.mj_loadXML(entryPath)` 让 MuJoCo 原生解析 MJCF/URDF 及相对资源;加载失败保留工程树并展示入口、资源路径和 MuJoCo 错误。
|
||
- [x] 切换工程前按逆序释放会话对象,再递归删除 MEMFS 工作区;对文件数、单文件、解压总量设置可配置上限。
|
||
- [x] 为 MJCF include + OBJ/STL/PNG、URDF + mesh、目录、ZIP、路径冲突和缺失资源添加测试夹具。
|
||
|
||
### 3. MuJoCo 会话与仿真控制
|
||
|
||
- [x] 封装 WASM 单例初始化和 `SimulationSession` 生命周期。
|
||
- [x] 创建/销毁 `MjModel`、`MjData`,实现异常路径下的确定性释放。
|
||
- [x] 实现播放、暂停、单步、重置、时间倍率、固定步长累积器。
|
||
- [x] 实现 actuator 控件和 qpos/qvel/ctrl 等状态快照。
|
||
- [x] 实现同步 `PhysicsAdapter` 接口并保持 UI 不直接持有 Embind 对象;Worker/message protocol 仅记录为后续扩展点。
|
||
|
||
### 4. Three.js 可视化与交互
|
||
|
||
- [x] 复用官方 demo 的 primitive 构建与 `mjv_updateScene` 方法,抽离为可测试的场景适配器。
|
||
- [x] 支持 MuJoCo primitive;对 mesh 根据 `mjvGeom.dataid` 读取 `mesh_vert/face/normal/texcoord` 及地址/数量数组构建缓存的 `BufferGeometry`,并从 `tex_data/width/height` 创建纹理,覆盖材质和坐标/矩阵转换。
|
||
- [x] 每帧同步动态 body/geom 位姿,避免重复分配临时对象。
|
||
- [x] 实现 OrbitControls、相机复位、网格/坐标轴、灯光和 resize。
|
||
- [x] 用 Raycaster 实现对象拾取、选中高亮及与属性面板联动,并维护 Three.js object → MuJoCo body/geom id 映射。
|
||
- [x] 关节拖动仅对可直接编辑的 hinge/slide joint 开放:暂停仿真、按 joint axis 将拖动量写入对应 `qpos`、按 `jnt_range` 限位并执行 `mj_forward`;free/ball joint MVP 只读。
|
||
- [x] 外力施加:选中动态 body 后以拖拽箭头显示方向/大小,在每个 `mj_step` 前通过 `MjvPerturb`/`mjv_applyPerturbForce` 施力,松开即清零,并提供强度刻度。
|
||
- [x] 在模型切换/卸载时释放 geometry、material、texture、Embind 临时 accessor/vector 和渲染循环资源。
|
||
|
||
### 5. 平台 UI 与状态管理
|
||
|
||
- [x] 搭建参考站点式布局:顶部工具栏、左侧工程树、中心视口、右侧属性/控制面板、底部状态区。
|
||
- [x] 实现 MJCF/XML、URDF、文件夹、ZIP 导入流程、入口选择、最近错误、加载进度与空状态。
|
||
- [x] 实现播放/暂停/单步/重置/速度控件、基于 `actuator_ctrlrange` 的 actuator 滑杆,以及 joint 拖动/外力模式开关。
|
||
- [x] 实现模型信息、body/joint/geom 属性检查器。
|
||
- [x] 针对桌面宽屏提供可调整/折叠面板、中文文案、键盘操作和基本无障碍语义;手机和平板不在 MVP 验收范围。
|
||
|
||
### 6. 性能、健壮性与交付
|
||
|
||
- [x] 建立 FPS、step 耗时、模型规模和内存趋势监控。
|
||
- [x] 验证大模型、资源缺失、无效 XML、重复加载和长时间运行。
|
||
- [x] 对主线程 step 设置每帧预算与最大追赶步数,超预算时显示性能警告而非无限追帧;Worker/SharedArrayBuffer 留作后续版本。
|
||
- [x] 完成生产构建、纯静态部署/离线使用说明、示例工程和用户文档。
|
||
- [x] 更新本计划进度看板并记录遗留项。
|
||
|
||
## Implementation Result
|
||
|
||
- 平台代码:`wasm/web_platform/`;使用 `@mujoco/mujoco@3.11.0` 默认单线程入口。
|
||
- 自动化结果:TypeScript、ESLint、10 个 Vitest 测试、5 个 Chrome Playwright E2E、生产构建均通过。
|
||
- E2E 覆盖:单文件 MJCF、MJCF include + OBJ/STL/PNG、URDF + OBJ、中等规模持续步进/重复加载、无效模型错误保留;另用完整 Go2W ZIP 实测 ROS `package://`、重复 material 和 DAE 降级后可编译。
|
||
- 静态服务验证:Python HTTP server 对 HTML 与 WASM 均返回 200,WASM MIME 为 `application/wasm`。
|
||
- 依赖审计:`npm audit` 0 vulnerabilities。
|
||
|
||
### 遗留验收项
|
||
|
||
- Edge/Firefox 最新版本尚未在当前环境做人工交互验收。
|
||
- 关节拖动、外力箭头与超大模型的视觉手感仍需结合真实机器人工程人工调参。
|
||
- 当前构建有约 865 KiB JS chunk 的 Vite 体积警告;不影响 MVP,后续可按面板/Three.js 做代码分割。
|
||
- E2E 的持续步进是秒级冒烟;发布前建议增加 30–60 分钟内存与稳定性 soak test。
|
||
|
||
## Verification
|
||
|
||
- 自动化:TypeScript 类型检查、单元测试、构建测试、浏览器 E2E 冒烟测试。
|
||
- 导入:MJCF/URDF 与其 mesh/texture 相对引用可加载;缺失/冲突/非法路径给出可定位错误。
|
||
- 物理:播放、暂停、单步、重置和 actuator 控制结果符合 MuJoCo 时间步;重复加载无悬挂循环。
|
||
- 渲染:primitive/mesh 位姿与 `xpos/xquat` 一致,相机和选中交互稳定,窗口缩放正常。
|
||
- 生命周期:连续切换模型和长时间仿真时,WASM/Three.js 资源无持续异常增长。
|
||
- 浏览器:桌面版 Chrome、Edge、Firefox 最新两个大版本完成手工验收;不测试手机、平板和多线程模式。
|
||
- 交付:生产构建可由本地静态 HTTP 服务器启动,`.wasm` MIME 与相对资源路径正确;明确 `file://` 不受支持。
|
||
|
||
## Confirmed Decisions
|
||
|
||
1. React + Zustand。
|
||
2. MVP 支持 MJCF/XML、URDF、文件夹、ZIP;URDF 交由 MuJoCo 原生 loader 解析。
|
||
3. 首版不做 XML 编辑、热重载和工程导出。
|
||
4. 首版主线程单线程 WASM。
|
||
5. 纯静态、离线应用,无后端和账号体系。
|
||
6. 首版交互优先 actuator 滑杆、关节拖动、外力施加;关节拖动时暂停且仅支持 hinge/slide,外力拖拽期间持续施加、松开清零。
|
||
7. 仅支持桌面浏览器,中文界面;多个入口由用户弹窗选择。
|
||
8. 通过本地静态 HTTP 服务器运行,不实现 PWA。
|