Skip to content

feat(sim2sim): 双后端 base.yaml 重构 + train/play 配置解耦#660

Closed
LeeLeno wants to merge 3 commits into
mainfrom
baseyaml
Closed

feat(sim2sim): 双后端 base.yaml 重构 + train/play 配置解耦#660
LeeLeno wants to merge 3 commits into
mainfrom
baseyaml

Conversation

@LeeLeno

@LeeLeno LeeLeno commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

issue(#650 #579 )
What changed

  • 双后端 task 回归 2 文件形态(每 task 仅 mujoco.yaml + motrix.yaml),删除 base.yaml 与所有 <backend>_play.yaml
  • 每个后端 owner 自包含完整训练配置,并内嵌可选的 play_profile 块(仅 rsl_rl/ppo 的 play 路径消费),在 eval(training.play_only=true)时叠加 render-only 覆盖
  • 跨后端 sim2sim play 由 resolve_sim2sim_config 只读校验:比对训练快照(run_config.json)与目标后端 owner 的策略契约字段,不一致显式报 CrossBackendIncompatibleError,training.sim2sim_strict=false 可强制,手动 CLI 覆盖(如 algo.empirical_normalization=<源值>)对齐

Why

  • 训练/渲染解耦(ppo):此前渲染参数(spacing/scene)与训练配置耦合,调渲染需改训练 owner。内嵌 play_profile.env 后可在 eval 单独覆盖 env 渲染字段,训练顶层配置零影响
  • 跨后端契约守卫:checkpoint 只存网络权重,策略契约字段(action_scale/obs_groups/empirical_normalization)由 yaml 控制;A 训练→B 渲染时守卫显式暴露契约不一致,而非静默产生错误行为
  • 文件精简:相比中间的 5 文件方案(base + 2 训练 + 2 play),回归 2 文件,文件数与 main 一致,消除 base 继承与 _play 冗余

Behavior

  • 训练:与 main 完全一致。合并前后 54/54 个 task×backend 训练配置有效值逐字段相等(compose 归一化比对),train 走顶层 env/algo,play_profile 训练时不生效
  • eval 同后端:正常play_profile(ppo)叠加渲染覆盖,不改策略字段
  • eval 跨后端:新增守卫(main 无)。契约一致静默通过;不一致显式报错,可强制 + 手动对齐

能覆盖范围(准确边界)

类别 play_profile 可覆盖 说明
ppo 的 env(render/DR/eval) play_profile.env update 到 env
appo/offpolicy 的 env 其 play 路径不消费 play_profile
任何算法的 algo algo 走独立路径;跨后端对齐靠 CLI 手动覆盖

Linked Work

  • Issue:
  • Milestone:

Validation

  • ruff check(clean)
  • pytest tests/config tests/training tests/test_cli.py(260 passed)
  • 全量 compose:60/60 task×backend 通过
  • 训练配置有效值 合并前后归一化比对:54/54 逐字段一致
  • 真机 eval 验证(见下,需 torch 环境)

Commands actually run:

# 训练配置零变化(合并后 vs 合并前 HEAD,compose 归一化逐字段比对)
#   → 54/54 task×backend 一致

# 全量 compose(所有双后端 task 两后端)
#   → 60/60 OK

# 真机 eval(在 torch 环境执行):
# A. 训练无变化
uv run train --algo ppo --task go2_joystick_flat --sim mujoco
# B. 同后端 eval(play_profile 渲染覆盖生效)
uv run eval --algo ppo --task go2_joystick_flat --sim mujoco --load-run <run>
# C. 跨后端 ✅(契约一致,守卫静默通过)
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libffi.so.7 \
uv run eval --algo ppo --task go2_joystick_flat --sim motrix --load-run <mujoco_run>
# D. 跨后端 ❌(守卫报错 → 强制 + 手动对齐)
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libffi.so.7 \
uv run eval --algo ppo --task go1_joystick_flat --sim motrix --load-run <mujoco_run>
#   → CrossBackendIncompatibleError: algo.empirical_normalization source=false target=true
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libffi.so.7 \
uv run eval --algo ppo --task go1_joystick_flat --sim motrix --load-run <mujoco_run> \
    training.sim2sim_strict=false algo.empirical_normalization=false

torch/模型相关测试与 make test-all 需真机 torch 环境;本地沙箱无 torch。

Impact

  • Backend impact: both(mujoco / motrix)
  • Platform impact: both(macOS / Linux)
  • Training effect expected: no(训练配置有效值不变,54/54 归一化比对证实;train 路由不变)

Artifacts

  • W&B:
  • benchmark result:
  • video / screenshot:
  • ONNX / checkpoint:

Checklist

  • Added or updated tests where needed(sim2sim resolver 注释更新;删除 _play 后 compose 用例自然收敛)
  • Updated docs if behavior or workflow changed(README / README_zh 改为 play_profile 内嵌说明)
  • Linked the driving issue
  • Noted any follow-up work explicitly

Follow-up(已知,非本 PR):

  • appo / offpolicy 的 play 路径调 build_task_env_cfg_override,不消费 play_profile,故其 owner 无渲染层、train/play 未解耦(与 main 一致);如需解耦需改其 play 脚本调 build_play_env_cfg_override
  • 2 个 task(ppo/g1_flip_trackingappo/g1_wall_flip_tracking)motrix.yaml 缺显式 action_scale/sampling_mode,跨后端 play 命中结构不对称守卫,需显式声明才能验证契约

- 为所有双后端 task 提取共享 base.yaml,mujoco/motrix 训练配置继承之(compose 有效值不变)
- 为 29 个双后端 task 新增 <backend>_play.yaml:继承同后端训练契约,仅叠加 play_only 与渲染层,实现 train/play 解耦
- eval 入口在 _play 存在时自动路由,train 不变;显式 --profile play 优先
- 跨后端 play 由 resolve_sim2sim_config 只读校验,差异显式报错,sim2sim_strict=false 可强制
@LeeLeno LeeLeno requested a review from TATP-233 as a code owner July 1, 2026 06:35
@LeeLeno LeeLeno changed the title feat(sim2sim): 双后端 base.yaml 重构 + train/play 配置解耦(issue #650 #579) feat(sim2sim): 双后端 base.yaml 重构 + train/play 配置解耦 Jul 1, 2026
@caozx1110

Copy link
Copy Markdown
Collaborator

为何需要新增xxx_play, 在原来的文件内添加play字段的配置,加载时如果模式是train,则不加载play,如果模式是play,则将play的配置再加载覆盖原始train配置

@TATP-233

TATP-233 commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

没看懂,到底改了些什么东西。像是一些key顺序变了

@LeeLeno LeeLeno closed this Jul 2, 2026
@LeeLeno

LeeLeno commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

feat(sim2sim): 跨后端契约 guard + train/play 配置解耦

概述

为双后端 task(MuJoCo ↔ Motrix)引入 sim2sim 契约守卫机制,在跨后端 play/eval 时自动检测策略 I/O 不兼容,并通过 play_profile 实现 train/play 配置逻辑解耦。

核心变化:

  • ✅ 训练时自动快照策略契约字段(obs/action/网络结构)至 run_config.json
  • ✅ 跨后端 play 时强制校验契约一致性,不兼容即报错 CrossBackendIncompatibleError
  • ✅ 36 个 task 支持 play_profile 块,渲染/scene 配置只在 eval 生效、train 路径零污染
  • ✅ 配置从「共享 base.yaml + 继承」重构为「自包含 owner + 内联契约」(文件数 -23)

1. 守卫机制:拦截跨后端不兼容

DENYLIST(7 个):差异即报错

影响策略 I/O 的核心字段,训练与播放必须完全一致:

字段 守卫理由
algo.obs_groups 策略输入分组,影响观测维度
env.control_config.action_scale 动作缩放,改变物理执行范围
algo.policy.actor_hidden_dims actor 网络结构,checkpoint 加载会 shape mismatch
algo.policy.critic_hidden_dims critic 网络结构
algo.empirical_normalization 观测归一化,改变输入预处理
algo.obs_normalization 另一种归一化实现
env.sampling_mode privileged obs 等,影响观测维度

WARNING_LIST(6 个):差异仅告警

不影响策略加载,但可能影响指标显示:

  • reward.scales, reward.base_height_target, reward.max_tilt_deg, reward.min_base_height(eval 不训练,reward 只影响指标)
  • env.control_config.simulate_action_latency, env.ctrl_dt(仿真细节)

ALLOWLIST(6 个):自由覆盖

渲染/播放专属,或训练时就允许随机化:

  • training.sim_backend, env.scene, training.play_steps, env.domain_rand, env.noise_config, env.commands.vel_limit

生效路径:

  • 训练时:ExperimentTracker.start() 自动提取契约快照 → run_config.json(contract_snapshot 字段)
  • Eval 时:5 个 play 入口(rsl_rl / appo / offpolicy / mlx_ppo / him_ppo)调 resolve_sim2sim_config 比对目标配置 vs 源快照
  • 跨后端差异:DENYLIST 不匹配 → raise CrossBackendIncompatibleError(默认);设 training.sim2sim_strict=false 可降级为 warning

2. play_profile:train/play 逻辑解耦

机制

同一 owner 配置(mujoco.yaml / motrix.yaml)内,通过 play_profile 块 + training.play_only 标志区分:

  • train:play_only=falseplay_profile 被短路忽略,训练契约生效
  • eval:play_only=true(cli 自动注入)→ 训练契约 + 叠加 play_profile

play_profile 可覆盖的内容

A. play_profile.env.* — 任意 env 层字段

直接 dict.update 叠加到 env_cfg_override,喂给 create_env:

  • ALLOWLIST 字段:如 scenedomain_randnoise_config — 写了就生效
  • 任意其他 env 配置:如 render_spacing(当前 36 个 task 在用)、reset_base_qvel_limitcurriculum
  • ⚠️ DENYLIST env 子集:如 action_scale — 可写,本后端 play 会生效,但跨后端时 guard 拦截
  • reward/algo:不在 env_override 管辖,写了无效

B. play_profile.scene.* — scene 重建(纯视觉层)

play_profile:
  scene:
    enabled: true
    source_model_file: assets/robots/xxx.xml
    ground_texture_file: assets/textures/floor.png  # 可选
    skybox_rgb1: [0.9, 0.9, 0.91]
    ground_texrepeat: [4, 4]

TATP-233 added a commit that referenced this pull request Jul 2, 2026
# feat(sim2sim): 跨后端契约 guard + train/play 配置解耦(关于pr #660)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants