feat(wh120): add Dexonomy integration and scoped smoke qualification - #9
Open
horychen wants to merge 47 commits into
Open
feat(wh120): add Dexonomy integration and scoped smoke qualification#9horychen wants to merge 47 commits into
horychen wants to merge 47 commits into
Conversation
Add zh versions of the annotation, running and ADMM tutorials, plus the repository CLAUDE.md guide. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
计划文档 v1.1~v1.3: - 修正 5 处与实测不符的描述:body 数 21→26(指尖软垫是独立 body, fusestatic 经 MjSpec attach 后失效)、obj 文件名带 hand- 前缀、 凸包按 geom 取而非按 body、kp 位于 actuator 且范围为 0.180~0.686、 mesh 引入方式改为 meshdir 直连源仓库 - 补齐 10 项决策:不带 mount 的 right.xml、软垫并入 body_group、 PD 系数不改、橙色配色、安全区染色输出、走 bodyframe 方式 B 等 - 第四章重写为零基础可读,新增 wh120/shadow/real_shadow 凸包贴合度 对照(76.9% / 68.0% / 60.6%) - 新增 26→15 几何去重表,四指指尖软垫完全相同,keypoint 可别名复用 标注教程(中英双份): - task.* → op.*(Hydra 配置组名) - anno_keypoint.yaml → keypoint.yaml,collision_skeleton.yaml → skeleton.yaml - Copy pose → Copy state(MuJoCo 3.3.3 已改名) - 中文版补充 managed viewer 与 passive viewer 的区别说明 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Step 0 —— 让 hand=wh120 可用: - assets/hand/wh120/right.xml 从 wuji-description-private 的 hand2_beta2 拷贝,只改两处:meshdir 改为相对路径直连源仓库工作树(仓库里不放任何 STL)、删掉 <option>(Dexonomy 用 MjSpec 覆盖)。kp/kv/forcerange 与 inertial/contact/site 全部保持上游原样 - body_group.yaml 为 6 组 26 个 body,指尖软垫 r_*_tip_sensor_frame 并入 对应手指分组(它在 MuJoCo 里是独立 body,fusestatic 经 attach 后失效) - skeleton.yaml / keypoint.yaml 空白占位,二者 Step 0/1 均不读取 - config/hand/wh120.yaml 用橙色配色,避开 allegro 的蓝 Step 1 —— 导出可标注的 3D 文件: - dexonomy/export_anno_mesh.sh 把导凸包 obj 与导染色 ply 串成一条命令 - dexonomy/vis_hull_gap.py 把凸包按「离真实表面多远」染色导成 PLY。 凸包在凹陷处架空,标在那里 2mm 校验会静默通过但真机摸不到;染色把 这个距离画出来(绿 ≤1mm / 黄 1~2mm / 红 >2mm)。凸包顶点是原始顶点的 子集,必须先 subdivide_to_size 才量得出架空量 验收: nq nv nu nbody ngeom = 20 20 20 27 52 MuJoCo_VisEnv collision 模式 body 数 = 26 dexrun op=tmpl hand=wh120 → "Find 0 annotations" export_anno_mesh.sh wh120 → obj 26 个、ply 26 个 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
将主计划第五章「Step 2~5」拆出为独立文档,主计划正文压缩 57% (855 → 368 行),只保留已完成的 Step 0/1、决策表与实测附录; 原第七章附录降为第六章,8 处交叉引用同步更新。 新增 docs/plans/2026-08-12-wh120-contact-annotation.md: - 范围:对齐 Shadow 的 33 个 taxonomy 类型(含上游拼写 18_Extensior_Type) - 实测 Shadow 这 33 份共触及 16 个 body,但只独立定义 7 个 keypoint 集合,其余走别名;映射到 WH120 需要点选 9 个 body,约 18 个点 - Step 2~5 逐条给出命令、坑与验收标准 - 并入原 6.1 的 kp 风险(Step 4 跑 op=grasp 才触发) 一并补入两份此前未提交的本地文档:readme.local.md(26 个 obj 生成 命令的考据与逐参数消融)、2026-08-03-vis-raw-anno-mujoco.md。 记录一个新发现的行为:op=tmpl 的 2mm 校验失败走 logging.error 而非 异常,.npy 照常写出且退出码为 0,必须 grep 日志才能发现。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
把 MeshLab Picked 出来的 Tri# / Normal 两行日志转成可直接粘进 raw_anno YAML 的 6D 接触向量(cp + cn)。 - 纯 tkinter 实现,无第三方依赖 - 优先解析括号三元组,无括号时按浮点数兜底(跳过时间戳与 Tri# 整数) - 保留原始数字字符串,不重新格式化,避免丢掉末尾 0 - 窗口默认置顶(F1 切换),方便压在 MeshLab 上边标边转
用 20 条线段替换原占位文件,op=init 的 check_mesh / check_plane 现在可用。 - distal 与 tip_sensor_frame 一律不画:中轴线离表面仅 0.06~0.20mm, 缓冲厚度不足,与 shadow 注释掉 rh_*distal 的做法一致 - 指骨线取自 URDF 的 <joint><origin xyz>;WH120 连杆沿自身 −z 延伸, 与 Shadow 的 +z 相反,抄正号会让线段翘出体外且不报错 - 掌部改用"腕心扇出到 5 个指根"而非 Shadow 的平行柱:WH120 掌部向腕端 收窄,平行柱体内占比仅 55~100%,扇形全部 100% 在体内,覆盖度也更优 (最远点 21.1mm vs shadow 25.8mm) 全部 20 条线段实测均 100% 落在自身 body 的碰撞凸包内。
- r_wrist 新增 [4] 手掌靠下接触点 - r_thumb_tip_sensor_frame 新增指腹偏外侧 / 偏内侧两个点 - 食指与小指 proximal_abd 的侧面点改为倾斜角更大的版本, 更贴近 Shadow 标注的数据风格;原 x 轴正方向点保留为注释
基于新的关键点集合与接触点转换器重新标注,主要改动: - 9 个模板(1/15/16/25/30/31/32/33 等)整体重摆 qpos - 大量手写 6D 向量改回 keypoint 索引复用,标注更稳定也更易读 - 接触 body 调整:拇指从 distal / proximal_abd 改到 tip_sensor_frame 与 middle;无名指 32_Ventral 从 distal 改到 tip_sensor_frame - 22_Parallel_Extension 修正 root 四元数分量符号并补上拇指指尖接触
JupyterLab 编辑器会在每个被编辑过的目录下自动生成 .ipynb_checkpoints/ 快照,之前已在仓库里堆了 4 个目录 9 个文件(内容与原文件逐字节相同)。 删除后加 ignore 规则,避免下次编辑再次污染 git status。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- requires-python 收紧到 >=3.10,<3.11,与 .python-version 一致 - torch 由 >=2.5.0 改为 ==2.5.1,并通过 [tool.uv.sources] 指向 阿里云 cu124 wheel,避免走默认源拉到 CPU 版或 cu121 - 补充此前隐式依赖的 pyyaml / scipy - 新增 dev 依赖组(pytest) - [[tool.uv.index]] 将阿里云 PyPI 设为 default,environments 限定 linux/x86_64 以缩小解析空间 - uv.lock 为上述声明的解析结果,必须与 pyproject.toml 同步提交 - requirements-dsw.txt 仅作为 DSW 主机的安装输入,pyproject.toml 仍是唯一权威依赖声明 注:本 commit 把阿里云镜像地址写进了 pyproject.toml。本仓库只在本地 提交、不推送上游,故可接受;若日后需要导出可分享的补丁,需摘除该段。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
工作区所在文件系统的 inode 有限,而 DGN_5k 物体资产与 op 产出的
npy 数量极大,放在仓库内会打满 inode。改为:
- base.yaml 新增 object_root,指向 /root/tmp/Dexonomy/assets/object
- base.yaml 的 save_root 同样迁到 /root/tmp/Dexonomy/output
- init.yaml 的 cfg_path 由 ${asset_root}/object/... 改为引用 ${object_root}
两个文件必须同一个 commit:init.yaml 引用的 ${object_root} 由
base.yaml 本次引入,拆开会产生 Hydra 插值失败、op=init 无法启动
的中间状态。
顺带补上 base.yaml 缺失的行尾换行。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
自行编排 dexrun(init / grasp / eval)而不使用 dexsyn,原因见脚本内 「为什么不用 dexsyn」一节:dexsyn 断言 模板数 <= len(init_gpu), 33 个模板放不下 2 张卡。 支持 EXP_NAME / N_CFG / TMPL_LIST 等环境变量覆盖,中断后重跑同一条 命令即可续跑(依赖 skip_done=True)。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
用软链接构造固定的 scene_cfg 子集目录,供 A/B 实验与全量分片使用: - --split train --n-cfg N --seed S 固定抽样,seed 可控,结果可复现 - --n-cfg 0 全量,不抽样 - 支持切分成 N 片,供多进程/多卡分片跑全量作业 之所以需要它:op=init 的 n_cfg 是按 glob 顺序取前 N 个,不同轮次之间 无法保证取到同一批物体,A/B 对比会被物体分布差异污染。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
用约 6 分钟的小规模对照实验决定 kp/kv,而不是直接把 2 天的全量作业
跑出去。
- make_kp_variants.py 从 right.xml 派生 5 个变体 XML,原文件不动
- run_kp_ab.sh 跑共享 init + 逐变体 grasp/eval,输出成功率对照表,
变体通过 dexrun 的 hand.xml_path= 覆盖注入,无需 per-variant 的
hand yaml
五个变体:A_base(基线)、B_kp3_sqrt(保持阻尼比)、C_kp3_kvold
(对照组,验证「kv 必须跟着 kp 改」这一假设)、D_kp3_kv010(真机
口径)、E_kp5_force3(CLAUDE.md 作者口径,力矩上限放宽到 ±3,仅作
仿真上限参考)。
注意:make_kp_variants.py 是从 right.xml 的当前 kp/kv 现场派生变体
的。在后续「统一 kp=3 / kv=0.1」那个 commit 之后重跑,A/B/C/D 四组
会塌成完全相同的配置(脚本不报错,但对照表看不出差别)。需要复现
原始实验时,先从本 commit 取回 right.xml:
git show <本commit>:assets/hand/wh120/right.xml
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
20 个 position actuator 的 kp 由「按各关节惯量算出的一组长浮点数」 (0.18 ~ 0.69)统一改为 3,kv 统一改为 0.1。ctrlrange 与 forcerange 不动。 依据是上一个 commit 的 A/B 实验:原增益太软,squeeze 阶段跟不上目标 位;kp 提到 3 后必须同时把 kv 提上来,因为 right.xml 的 joint 级 damping 是 0,全部阻尼只来自 actuator 的 kv,否则没有任何东西抑制 震荡。最终取 kp=3 / kv=0.1,与真机控制器口径一致。 未采用 CLAUDE.md 里建议的 kp=5 + forcerange±3:该组把力矩上限放宽到 真机给不出的量级,只适合当仿真上限参考,不作为部署配置。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
run_smoke_all.sh 的全量版,产物结构一致,规模约 440 倍: 27342 个 scene_cfg(4557 物体 x 6 尺度)x 32 模板。 与 smoke 版的主要差别:先用 make_object_subset.py 把 train 集切成 多个不相交分片,再按分片依次跑 init/grasp/eval,避免一次性产出的 npy 把 inode 打满;每个分片有独立的 done flag 控制 driver 退出。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- inspect_grasp.py 读出 succ_grasp 里一个 npy 的三段 qpos (pregrasp / grasp / squeeze)与两套接触信息,并说明这些字段分别 由 op=init 和 op=grasp 写入的哪一步 - replay_grasp.py 在 MuJoCo 里逐帧复现 op=eval 的力封闭测试流程 (含 6 个方向的外力施加与「抓牢 2 秒」),支持 viewer 交互与 无 GUI 的 mp4 离线渲染 - egl_vendor/10_nvidia.json 是 replay_grasp.py 走 EGL 无头渲染时需要 的 ICD 描述文件,容器里默认没有 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 WH120 全量跑把 rootfs 的 983 万 inode 打满。复盘结论:与 分片策略无关,根因是 PURGE_INIT 默认为 0 且监控只看了空间没看 inode。 文档同时记录了两个 driver 子壳因 flag 文件缺失而无限重启的现象——即 CLAUDE.md 中「while not check_finish 无限重启子进程」那个坑的一个变体 (这次对不上的是 flag 文件而非日志措辞)。 hardlinkify.py 是该文档开出的处置工具:把 succ_grasp / new_tmpl 下的 软链原地换成硬链以回收 inode,不删除、不移动、不拷贝任何数据, grasp_data 完全不动;默认干跑只统计,加 --apply 才真正执行。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
产物离开本机就读不了 mesh 的根因是 scene_path 直指分片脚手架。三处一起修:
写入端(根因)
- obj_loader.py: glob 结果过 os.path.realpath 再写进 scene_path,
解引用到原始数据集。分片目录从此只在运行期需要,跑完可删。
读取端(兼容第一轮已产出的 DGN 数据)
- replay_grasp.py: remap_scene_path 改成四级回退 —— 原路径存在则直接用;
按数据集前缀匹配(能命中 DGN_5k_train_sh18 这类分片名)重建路径;
glob 兜底;新增 --dataset 显式指定。7 种路径形态单测通过。
inode
- eval_data.py: succ_grasp / new_tmpl 由 os.system("ln -s") 改为 os.link,
新增 _link_sample(跨设备时回退相对软链)。硬链净增 0 inode,
全量一轮的 200 多万条软链是 8/16 那次 inode 打满的主因之一;
顺带省掉每样本一次 fork。
数据集参数化
- run_full_train.sh: 新增 DATASET(默认 DGN_5k,保住第一轮口径),
SUBSET_DIR / --src / 前置检查 / 启动横幅一并跟上;
文件头标注速率表是 DGN 实测值,objaverse 凸分解件数 2.2×,需重测。
- config/op/init.yaml: 默认 cfg_path 切到 objaverse_5k(只影响单 op 调试)。
base.yaml 未动 —— object_root 是父目录,改了会 glob 到 0 个物体且不报错。
打包 / 解包
- pack_output_bundle.sh: 打 grasp_data + succ_grasp + new_tmpl + hand xml
+ BUNDLE_INFO.json,--succ-only 瘦身。tar 成员顺序显式钉死为
succ_grasp -> new_tmpl -> grasp_data:一组硬链接里只有先遍历到的那个存实体,
readdir 顺序不可控,第一轮就是这么丢了 99.94% 的 succ_grasp。
打包前抽样体检,scene_path 含 shard_ 直接拒绝出包。
- unpack_output_bundle.sh: 不需要 mesh。动磁盘前查完 sha256 / 空间 / inode /
硬链接支持 / tar / Python 环境,阈值全部从 BUNDLE_INFO.json 读,不写死。
一律整棵解,不指定成员,不加 -h。
- verify_output_bundle.py: 条目数、断链、nlink、scene_path 规范性、npy 字段,
可选 --object-root 验 mesh 可达。可单独跑。
文档口径
- make_object_subset.py / hardlinkify.py / run_full_train.sh 第十节同步
「分片目录跑完可删」「新数据直接是硬链」,逻辑均未改动。
合成数据跑通 pack -> unpack -> verify 全链路,含旧数据被拦、落点非空、
sha256 损坏等失败路径。未对真实数据执行打包。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
系统 python3 通常没装 mujoco,pack 的 kp/kv 探测会静默失败, BUNDLE_INFO 的 hand_gain 变成空的。两个脚本都改成先找 <repo>/.venv/bin/python,找不到再退回 command -v python3, PYBIN= 仍可覆盖。 pack 侧顺带把探测失败的提示写清楚:不阻断打包(包里带着 hand/right.xml,控制器参数仍能自证),并给出换环境的命令。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
把仓库打成 tar.gz 带走,跳过 .venv / __pycache__ 等大件与缓存。 要打什么改脚本里的 ITEMS 数组,排除项统一走 EXCLUDES。 产物强制落在仓库外(默认 /mnt/data/Dexonomy),否则 tar 会把 正在写的包自己读进去。--dry-run 只统计各条目大小。 和 pack_output_bundle.sh 分工:那个打数据,这个打代码。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Author
|
@FeilaoWu 麻烦 review 这个 WH120/Dexonomy 研究集成 checkpoint,当前提交为 建议重点看:
验证:Ubuntu isolated full suite 当前账号没有 base repo 的 RequestReviews 权限,因此无法把你设成 GitHub formal reviewer;先在这里直接邀请,上游 maintainer 可补点 reviewer。 |
horychen
marked this pull request as ready for review
August 29, 2026 18:10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this PR establishes
This PR turns the WH120/Dexonomy work into an auditable non-commercial research pipeline:
(dataset, template, scene, candidate)unit to one canonicalgrasp_datapayload and its complete scene/mesh dependency closure;The accepted provisional project charter and definition of done are now versioned in
docs/PROJECT_GOAL.md. Corpus-wide GIF/NPZ generation is explicitly out of scope: previews and dense telemetry are opt-in diagnostics, not a replacement dataset.Upstream source and license
Attribution and the non-commercial restriction are recorded in
NOTICE-WH120.md, the README, generated provenance, and the project charter. This PR does not grant commercial rights to Dexonomy-derived code/data or to separately owned WH120/object assets. Commercial use remains blocked pending written permission from all relevant rights holders.Data facts and scale
60+ GB.grasp_datais the canonical saved pre-grasp, grasp, and squeeze joint-angle payload.succ_graspandnew_tmplare link/index views ofgrasp_data; they are not additional independent angle datasets and must not be summed as unique units.(dataset identity, template, scene identity, candidate), with one canonical NPY plus references to a frozen scene and shared mesh closure.60+ GBsource corpus itself is not claimed to fit inside 20 GiB.Audited result packages remain separate identities:
0821-2026-second-round-obj5k/obj_train_wh120_full_20260821_1914.tar.gz, 4,682,479,789 bytes, SHA-2565bc3a9de864c296797a7a74afe6d73a4e2c6061b91b117b3ccc61c5cf21c33cf, producer commitb606bfd.0818-2026-first-round/full_train_wh120_bundle.tar.gz, 6,715,294,762 bytes, SHA-256c082583e0afd5f6e37c591a6215eb1e0ad14f2e1a1d959520a4cd2b1347f9bf7. It remains labelled first round; this PR does not invent or relabel a DGN second-round package.Implemented here
generic-dexonomy31-v1,distal-scissors-v1,tripod-variation-chopsticks-v1, and an explicitly unqualified diagnostic subset;The downstream Magichand bridge is reviewed separately in wuji-technology/wuji-hontai-magichand#5. It validates a real DGN moving-root command through MuJoCo/motor plumbing; it must not be interpreted as generic31/all-33 or physical-hand qualification.
Validation at commit
7a67cf709d5290fd49ecf73008fbb0cf943da471caabbe4aa78d8697b39759b3da9250ad66d2eca1git diff --check, shell syntax checks, anduv lock --check: pass18eea3f9fb8b347f46875e1a4a6ad32841d3183a4a9d871b50a3109efe75de0f--resumepreserved byte-for-byte SHA-256d9f2fa1aa9331b8f79e1290358e2d9beefa959845e9ee653509c89fae08fb57fWhat is deliberately not claimed
32_Ventralstill needs WH120-native human re-annotation and approval. Numerical clamping is not a substitute.19_Distaland21_Tripod_Variationneed dataset-owner-approved, hash-pinned scissors and chopsticks manifests before specialty qualification.External approvals still required
32_Ventral.This is a runnable and auditable research checkpoint, not a claim of all-33, physical, or commercial readiness.