Skip to content

将 sharpa 任务与 hora 算法迁移至 unilabsim/sharpa_rl_unilab 独立仓库 #1547

Description

@TATP-233

目标

sharpa 任务(SharpaInhandRotation / SharpaInhandRotationGrasp,含全部 owner 配置、资产、文档、测试)和 hora 算法(hora_ppo / hora_appo / hora_sac / hora_distill)从 UniLab 和 unilab_rl 完整移除,迁移到独立仓库 unilabsim/sharpa_rl_unilab(当前为空仓库)。

迁移方法借鉴已成功落地的 unilabsim/legged-manipulation_unilab(Go2+Arm 任务 + HIM-PPO 算法的拆出范例)。

参考方法:legged-manipulation_unilab 是怎么做的

该仓库不是 fork,而是一个依赖 UniLab / unilab_rl 的插件式独立 Python 包,单包 src-layout,任务、算法、配置、资产、工具全部打进一个 wheel。

1. 包结构与依赖

  • 包名 legged_manipulation_unilab,uv_build 构建。
  • pyproject.toml 中 UniLab 和 unilab-rl 以 git commit 钉死:
dependencies = [
    "unilab @ git+https://github.com/Motphys/UniLab.git@<pinned-commit>",
    "unilab-rl @ git+https://github.com/unilabsim/unilab_rl.git@<pinned-commit>",
    "numpy", "torch>=2.8", "tensordict", "hydra-core>=1.3", "filelock", "tensorboard",
]
[project.optional-dependencies]
mujoco = ["unilab[mujoco] @ git+...UniLab.git@<pinned-commit>"]
motrix = ["unilab[motrix] @ git+...UniLab.git@<pinned-commit>"]

backend extra 通过 unilab[mujoco] / unilab[motrix] 透传;不发版、不上 PyPI,靠 git rev + uv.lock 锁定。

  • 目录划分(所有权边界):
src/legged_manipulation_unilab/
├── cli.py            # legged-train / legged-eval 入口:owner 选择 + Hydra 组合
├── conf/<algo>/      # 每个 algo 一个 config 目录(config.yaml + task/...)
├── tasks/            # 环境本体 + 任务专用 common(rewards/DR/commands)
├── algos/him_ppo/    # 专用算法完整实现(自包含,只依赖 torch/tensordict)
├── training/         # 专用算法的训练/评估装配
├── tools/            # 任务专用工具(IK viewer、诊断、标定)
└── assets/           # 资产 + manifest.json + ensure_assets() 缓存物化

2. 任务注册机制(无需改 UniLab)

UniLab 侧已有通用扩展点 src/unilab/base/registry.py:

  • entry point group unilab.tasks:ensure_registries() 遍历 entry_points(group="unilab.tasks"),import entry point 指向的包,读取其 __unilab_registry_modules__ 属性并逐个 import 完成注册。entry point 元数据在 site-packages 里,spawn 子进程(fresh interpreter)也能自动发现,无需环境变量转发。

外部包侧只需三步:

  1. pyproject.toml 声明:
    [project.entry-points."unilab.tasks"]
    legged_manipulation = "legged_manipulation_unilab.tasks"
  2. tasks/__init__.py 全文只有:
    __unilab_registry_modules__ = ("legged_manipulation_unilab.tasks.go2_arm",)
  3. 环境模块内照常使用 UniLab 现有 decorator:@registry.envcfg("Go2ArmManipLoco") + registry.register_env(...)(legacy 环境经 unilab.tasks.compatibility.adapt_legacy_factory 包装);SceneCfg 默认指向包内资产。

配套测试:用真实 spawn 子进程验证 list_registered_envs() 里能发现外部任务及其 backend 列表。

3. 训练/算法接入(算法无 entry point,纯配置驱动 + 包自有 CLI)

  • 包自有 CLI(legged-train / legged-eval)用 hydra.initialize_config_dir + compose 组合包内 conf/<algo>/ 的 owner 配置,并用 reserved-override 黑名单(tasktraining.task_nametraining.sim_backendtraining.play_only)强制保持 owner 身份。
  • 通用算法(如 PPO):不做自己的实现,直接委托 UniLab 共享 launcher(unilab.scripts.train_rsl_rl),重写 sys.argv 传入 --config-path=<包内 conf>task=...;算法类由 YAML 里的 class_name 解析。
  • 专用算法(HIM-PPO):写自己的 training/him.py 装配,import UniLab 的 create_envExperimentTracker、playback session factory、sim2sim preflight 等公共件,只注入自己的 runner(runner 接口对齐 rsl_rl OnPolicyRunner);play 路径通过 playback session factory 的 runner_cls= + guard_algo_name= 参数注入。
  • 维度契约写死在配置/装配里,启动时校验、play 时在建环境之前做 checkpoint 形状 guard。

4. 资产处理(整体进 git + wheel,运行时零网络)

  • 机器人 XML/OBJ/STL/纹理(~37 MB)以普通 git blob(非 LFS)提交在 src/<pkg>/assets/robots/...,随 wheel 分发。
  • assets/manifest.json 记录来源("source": "unilabsim/unilab-robots" + pinned revision)+ 每个文件的 sha256。
  • assets/__init__.py::ensure_assets():把包内资产复制到可写缓存(默认 $XDG_CACHE_HOME/<pkg>/<manifest-sha256 前16位>),逐文件校验 sha256、损坏自动修复、FileLock 保护并发;配套 <pkg>-assets CLI 预热缓存。
  • 明确无 Hugging Face / 运行时网络依赖;测试通过封禁 socket 证明离线可用。
  • 这是对 UniLab 主仓库 "meshes 从 asset hub 来、不进 git" 约定的刻意背离,目的是让拆出仓库自足;NOTICE.md 声明资产来源与授权边界。

5. 迁移交付物模板

文件 作用
MIGRATION_MANIFEST.json 溯源清单:源仓库 commit、每个迁移文件的源路径 + 迁移前 sha256(部分精确到 symbol)
docs/ARCHITECTURE.md 所有权边界表:哪些迁出、哪些留在主仓库
docs/VALIDATION.md 已执行的迁移验证记录(见下)
NOTICE.md + LICENSES/ 各部分来源与 license(算法来自 uni_rl 的保留原 SPDX 头)
docs/en, docs/zh_CN/ 从 UniLab sphinx 拆出的任务/算法文档页(中英)

6. 验证方法(VALIDATION.md 记录)

  • 三方协调 commit:UniLab(删除 PR)、unilab_rl(删除 PR)、新仓库(迁移 PR)各钉一个 commit,互相在 pyproject/manifest 里引用。
  • 行为等价性回放:docs/validation/compare_extraction.py 在拆出前后两个进程里以相同 seed/动作序列回放,逐数组 assert_array_equal 比较 reset obs、每步 obs/reward/flag 等(每个 algo×backend 组合)。
  • 离线安装探针:wheel 安装后在 /tmp + HF_HUB_OFFLINE=1 + socket 封禁下跑通。
  • 质量门:本地 Makefile(check = ruff + mypy + pyright,test = pytest,build = uv build),无 CI(可选)。
  • 关键测试:owner 组合身份、spawn 注册、资产离线/修复、runner 契约(维度校验、checkpoint save/load/resume、ONNX 导出数值一致)。

sharpa / hora 迁移清单

A. 迁移到 sharpa_rl_unilab(新仓库)

UniLab 侧任务代码

  • src/unilab/tasks/manipulation/sharpa_inhand/ 全部(base.py 670 行、rotation.py 1554 行、grasp_gen.py 402 行)
  • src/unilab/tasks/__init__.py:15 注册条目、src/unilab/tasks/migration_matrix.py:59-60,148 的 sharpa family
  • 配置:src/unilab/conf/{ppo,appo}/task/sharpa_inhand*/src/unilab/conf/sac/task/sharpa_inhand/src/unilab/conf/hora_distill/ 整树
  • 脚本/training:src/unilab/scripts/play_hora_appo.pyscripts/train_hora_distill.pysrc/unilab/training/hora_distill_config.pyscripts/sharpa_collect_grasps.sh
  • demo 条目:src/unilab/demo.py:32-46inhandgraspsharpa_appo_student
  • 测试:tests/envs/test_sharpa.pytests/test_sharpa.pytests/algos/test_hora_distill_config.pytests/benchmark/test_sharpa_init_dr_benchmark.py
  • 文档:2-user_guide/2-algorithms/7-hora.md8-manipulation/1-dexterous_inhand.md(en + zh_CN),及各索引页 sharpa/hora 行

unilab_rl 侧算法代码(src/uni_rl/algos/hora/,保留原 SPDX/license 头)

  • 共享 backbone:models.py(HoraActorModel/HoraCriticModel/HoraSharedActorCritic)、observations.py(obs 切分契约)、runtime.py
  • PPO:ppo.pyrsl_rl.py(resolve_hora_ppo_runtime + HoraRslRlVecEnvWrapper)、rsl_rl_compat.py
  • APPO:appo.pyappo_runner.pyappo_worker.pyappo_learner.py
  • SAC:sac.py(resolve_hora_sac_runtime)、sac_learner.pysac_models.py
  • 蒸馏:distill.py(608 行,HoraDistillationTrainer 等)
  • 测试:tests/algos/test_hora_contract.pytest_hora_imports.py

资产

  • src/unilab/assets/robots/sharpa_wave/(scene.xml + right_sharpa_wave.xml,git 内)
  • 随资产迁移:HF unilabsim/unilab-robots 中的 sharpa_wave meshes(hub.py:51 ROBOT_ASSET_SPECS["sharpa_wave"])、unilabsim/unilab-caches 中的 sharpa_grasp_linspace 抓取缓存——按 legged-manipulation 模式整体打进新仓库 git + wheel,manifest.json 记录来源 revision + sha256
  • 待确认:IsaacSim/Gym benchmark 引用的 right_sharpa_wave.usda 的托管位置

benchmark:sharpa 专属段(env/benchmark_sharpa_init_dr_construct.pyenv/benchmark_env_step.pycore/task_names.py、physics 三个脚本中的 sharpa 段)

B. 主仓库(UniLab)删除/清理

  • src/unilab/training/run.py:159-229 hora stage2 checkpoint 助手及 training/__init__.py 再导出
  • src/unilab/visualization/interactive_playback.py 全部 hora 分支(hora_sac/hora_appo/create_hora_distill_playback_session,约 30 处)
  • src/unilab/scripts/play_interactive.pyhora_distill 路由;src/unilab/scripts/train_offpolicy.py:366,389 的 hora_sac 特判
  • tests/algos/test_hora_contract.py(与 unilab_rl 重复,主仓直接删)
  • 各通用测试中的 sharpa/hora 段落(tests/envs/test_env_configs.pytests/scripts/test_train_scripts.py 等)、scripts/tools/support_matrix.pydocs support_matrix 页条目、cli.py:98,253 的 hora 提示文案
  • asset hub:hub.pysharpa_wave 行删除(HF 下载机制本身保留)

C. unilab_rl 侧需先改造为通用机制的耦合点

这三处 hora 硬编码必须先抽象成通用 hook,否则 hora 迁出后形成反向依赖:

  1. src/uni_rl/algos/common/actor_factory.py:33-38algo_type=="hora_sac" 分支 → 改造为 actor builder 注册表
  2. src/uni_rl/offpolicy/worker.py:47-76 — hora_sac 动作采样依赖 priv_info,直接 import hora.observations → 改造为 priv-info 上下文协议
  3. src/uni_rl/offpolicy/double_buffer_runner.py:610 — hora_sac actor context 切尾 → 同上

同时确认保留的通用扩展点:runtime_resolver 分发机制(appo/rsl_rl/offpolicy)、adapt_legacy_factory、migration_matrix/support_matrix 工具链(仅删 sharpa 数据行)。observation_mode: separated + info["critic_info"] 的观测契约目前由 hora 独占,可考虑上升为 env contract 的通用 capability。

D. 新仓库交付物(对照 legged-manipulation 模板)

  • 单包 sharpa_rl_unilab,uv_build,git-pin UniLab / unilab_rl 到协调后 commit,extras 透传 backend
  • pyproject.toml entry point [project.entry-points."unilab.tasks"] + tasks/__init__.py__unilab_registry_modules__
  • 包自有 CLI(sharpa-train / sharpa-eval),conf/{ppo,appo,sac,hora_distill}/ 每个 algo 一个 config 目录
  • algos/hora/ 自包含实现;hora_ppo 委托共享 launcher,hora_appo/hora_sac/hora_distill 写包内 training/ 装配(runtime_resolver 改为指向包内路径)
  • 资产整体进 git + wheel,assets/manifest.json(来源 revision + per-file sha256)+ ensure_assets() 可写缓存 + 离线测试
  • MIGRATION_MANIFEST.json + docs/ARCHITECTURE.md + docs/VALIDATION.md + NOTICE.md + LICENSES/
  • 中英任务/算法文档页迁入新仓库 docs

验收标准

  1. 新仓库 uv sync --extra mujoco 后,sharpa-train --algo {ppo,appo,sac}sharpa-eval、hora_distill 全部可用;spawn 子进程能发现 SharpaInhandRotation / SharpaInhandRotationGrasp
  2. 行为等价性回放:迁移前后相同 seed/动作序列下 obs/reward 逐数组一致(ppo/appo/sac × mujoco)。
  3. wheel 离线安装探针通过(无 HF/网络依赖)。
  4. UniLab 与 unilab_rl 主仓库删除 sharpa/hora 后 make checkmake test-all 全绿,无残留引用(rg -i 'sharpa|hora' 仅剩通用机制说明)。
  5. 三方协调 commit 互相钉死并记录于 VALIDATION.md / MIGRATION_MANIFEST.json。

参考

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions