This is the code for Any2Any: Efficient Cross-Embodiment Transfer for Humanoid Whole-Body Tracking. Project page: any2any.top
Any2Any transfers an existing whole-body tracking (WBT) specialist to a new humanoid with only a small fraction of the data and compute needed to train from scratch: first kinematic alignment of the input/output spaces, then dynamics adaptation via lightweight parameter-efficient fine-tuning (PEFT) on the dynamics-sensitive modules.
This repository is the concrete instance of that recipe: it adapts the pretrained SONIC G1 (29-DoF) tracking policy to the Oli humanoid (31-DoF).
Built on the SONIC training stack in NVlabs/GR00T-WholeBodyControl (the gear_sonic/ subproject). Upstream docs: nvlabs.github.io/GR00T-WholeBodyControl · SONIC paper: arXiv:2511.07820
Two training entry points share the same environment, reward, and PPO stack:
| Mode | Experiment config | Starts from |
|---|---|---|
| LoRA (Any2Any) | sonic_oli_lora |
Pretrained SONIC G1 checkpoint |
| Scratch training (baseline) | sonic_oli_native |
Random init |
Matching the upstream Installation (Training) requirements:
| GPU | NVIDIA GPU with CUDA 12.x (L40 recommended) |
| OS | Ubuntu 22.04+ |
| Python | 3.11 (required by Isaac Lab; this package declares >=3.10) |
| Isaac Lab | 2.3+ |
| VRAM | ≥ 24 GB; 48 GB+ for the default num_envs=2048 |
The code imports itself as an absolute package (from gear_sonic.trl.utils.common import ...), matching the upstream monorepo layout where the training stack lives in gear_sonic/. The repository directory must be named gear_sonic:
<workspace>/
└── gear_sonic/ ← this repository (name is required)
├── train_agent_trl.py
├── checkpoint/
└── config/
If you cloned it under a different name, rename it:
mv gear_sonic_v2 gear_sonicSONIC training uses Isaac Lab for physics simulation. Follow the official Isaac Lab installation guide, then verify:
python -c "import isaaclab; print(isaaclab.__version__)"Everything below runs inside the Isaac Lab Python environment. If you manage it with conda:
conda create -n sonic python=3.11 -y
conda activate sonic
# ... install Isaac Sim + Isaac Lab into this env ...From the parent of the repository directory (same form as upstream's pip install -e "gear_sonic/[training]"):
cd <workspace>
pip install -e "gear_sonic/[training]"This adds hydra-core==1.3.2, trl==0.28.0, transformers, accelerate, tensorboard, and wandb on top of Isaac Lab.
Verify:
python -c "import hydra, omegaconf; print(hydra.__version__, omegaconf.__version__)"
# expected: 1.3.2 2.3.0Git LFS. If you also clone the upstream monorepo, run
sudo apt install git-lfs && git lfs install && git lfs pull— without it you get pointer stubs instead of meshes and ONNX models, which fail silently.
LoRA mode fine-tunes on top of the released SONIC G1 policy, published on Hugging Face at nvidia/GEAR-SONIC. The repo is public — no token required.
The PyTorch training checkpoint lives under sonic_release/ on the Hub:
| Hub file | Purpose |
|---|---|
sonic_release/last.pt |
Training checkpoint (eval / fine-tuning) |
sonic_release/config.yaml |
The config it was produced with |
config.json |
Release manifest — include it when downloading selected files |
pip install -U "huggingface_hub[cli]"
hf download nvidia/GEAR-SONIC \
config.json \
sonic_release/last.pt \
sonic_release/config.yaml \
--local-dir /tmp/gear_sonic_hfOr via Python:
from huggingface_hub import hf_hub_download
REPO_ID = "nvidia/GEAR-SONIC"
hf_hub_download(repo_id=REPO_ID, filename="config.json")
ckpt = hf_hub_download(repo_id=REPO_ID, filename="sonic_release/last.pt")
cfg = hf_hub_download(repo_id=REPO_ID, filename="sonic_release/config.yaml")
print(ckpt, cfg, sep="\n")If you have the upstream monorepo checked out, its helper script does the same plus the SMPL dataset:
python download_from_hf.py --training # checkpoint + SMPL data (~30 GB)
python download_from_hf.py --training --no-smpl # checkpoint only
python download_from_hf.py --sample # sample motion data (~4 MB)This repo resolves the pretrained weights at checkpoint/last.pt:
cd <workspace>/gear_sonic
mkdir -p checkpoint
cp /tmp/gear_sonic_hf/sonic_release/last.pt checkpoint/
cp /tmp/gear_sonic_hf/sonic_release/config.yaml checkpoint/Final layout:
gear_sonic/
└── checkpoint/
├── last.pt # SONIC G1 policy weights (~450 MB)
└── config.yaml # the config the checkpoint was trained with
The path comes from config/exp/manager/universal_token/all_modes/sonic_oli_lora.yaml:
pretrained_model:
path: ${repo_root:}/checkpoint/last.pt
state_dict_key: policy_state_dict
strict: false
module_mapping:
policy.actor_module: actor_module.${repo_root:} is a custom OmegaConf resolver registered in utils/config_utils.py, so it works regardless of your current working directory.
Model weights are not tracked in Git (
checkpoint/and*.ptare ignored). A 450 MB blob also exceeds GitHub's 100 MB per-file limit.
cd <workspace>/gear_sonic
python - <<'PY'
import torch
from pathlib import Path
ckpt = Path("checkpoint/last.pt")
assert ckpt.exists(), "checkpoint/last.pt not found"
assert Path("checkpoint/config.yaml").exists(), "checkpoint/config.yaml not found"
sd = torch.load(ckpt, map_location="cpu", weights_only=False)
print("top-level keys:", list(sd.keys()))
assert "policy_state_dict" in sd, "missing 'policy_state_dict' — wrong checkpoint format"
actor = [k for k in sd["policy_state_dict"] if k.startswith("policy.actor_module")]
print(f"actor tensors: {len(actor)}")
print(f"size: {ckpt.stat().st_size / 1e6:.0f} MB")
PYpolicy_state_dict must contain policy.actor_module.* tensors — that prefix is what module_mapping rewrites to actor_module. at load time. If the assert fails you downloaded the ONNX deployment artifacts (model_encoder.onnx / model_decoder.onnx) instead of the training checkpoint.
Retargeted Oli motions live under data/oli_motion/:
data/oli_motion/
└── atom_motions_v6_selected_fix10n11_100hz/ # .npy motion clips
Point the motion library at the directory containing your clips. If you pass a parent directory holding several sub-folders, either keep the default include_subdirs whitelist or disable it with include_subdirs=null.
Note on the shipped dataset.
atom_motions_v6_selected_fix10n11_100hzis a deliberately small, hand-selected subset of the full motion corpus. It is provided so that the efficiency claim of Any2Any can be reproduced quickly on a single GPU: LoRA fine-tuning and scratch training are run on exactly the same clips, making the gap in sample efficiency and wall-clock time easy to verify without access to the full dataset or a large cluster.
For upstream G1 data (Bones-SEED, 142K motions), see Training Data and the conversion scripts in data_process/.
Single GPU is the default — config/base.yaml ships with num_gpus: 1 and multi_gpu: False. No launcher (accelerate launch / torchrun) is needed; just run the script.
Confirm the whole stack starts before committing to a long run (upstream's verification recipe):
cd <workspace>/gear_sonic
conda activate sonic
CUDA_VISIBLE_DEVICES=0 python train_agent_trl.py \
+exp=manager/universal_token/all_modes/sonic_oli_native \
num_envs=16 headless=True \
++algo.config.num_learning_iterations=5After about a minute of initialization you should see reward and tracking-error metrics printing to the console.
CUDA_VISIBLE_DEVICES=0 python train_agent_trl.py \
+exp=manager/universal_token/all_modes/sonic_oli_lora \
num_envs=2048 headless=True \
manager_env.commands.motion.motion_lib_cfg.motion_file="${PWD}/data/oli_motion/atom_motions_v6_selected_fix10n11_100hz"Outputs go to logs/rsl_rl/LoRA/<timestamp>/.
CUDA_VISIBLE_DEVICES=0 python train_agent_trl.py \
+exp=manager/universal_token/all_modes/sonic_oli_native \
num_envs=2048 headless=True \
manager_env.commands.motion.motion_lib_cfg.motion_file="${PWD}/data/oli_motion/atom_motions_v6_selected_fix10n11_100hz"Outputs go to logs/rsl_rl/Native/<timestamp>/. No pretrained checkpoint is required.
Both runs use the identical environment, reward, PPO hyperparameters, num_envs, and the motion subset from Section 4 — the only difference is whether training starts from the pretrained SONIC G1 checkpoint (LoRA) or from random init (scratch).
| LoRA (Any2Any) | Scratch training | |
|---|---|---|
| Iterations to reward 30 | ~600 | ~3,300 |
| Iterations to episode length 450 | ~800 | not reached by 4,200 |
| Reward at 4,200 iterations | ~47 | ~33 |
LoRA saturates episode length (i.e. stops early-terminating) within roughly 800 iterations, while scratch training is still climbing after 4,200. The pretrained tokenizer and motion prior carry over across the embodiment gap, so PPO only has to learn the residual dynamics correction instead of rediscovering whole-body tracking from nothing.
All Hydra overrides are key=value on the command line. See the upstream Configuration Guide.
# Smaller GPU — halve the parallel envs
num_envs=1024
# Watch the sim (disables headless; much slower)
headless=false
# Reproducibility
seed=42
# Pin the output root elsewhere
base_dir=/data/sonic_runs
# Enable Weights & Biases (the training script sets WANDB_MODE=disabled by default)
WANDB_MODE=online python train_agent_trl.py use_wandb=true ...
# Point at a different pretrained checkpoint (LoRA only)
algo.config.pretrained_model.path=/abs/path/to/last.pt
# Disable the motion sub-directory whitelist
manager_env.commands.motion.motion_lib_cfg.include_subdirs=null
# Disable observation noise (useful when debugging tracking quality)
++manager_env.observations.policy.enable_corruption=False
++manager_env.observations.tokenizer.enable_corruption=FalsePrint the fully composed config without launching the simulator:
python train_agent_trl.py +exp=manager/universal_token/all_modes/sonic_oli_lora --cfg job@article{any2any,
title={Any2Any: Efficient Cross-Embodiment Transfer for Humanoid Whole-Body Tracking},
author={Anonymous},
journal={arXiv preprint arXiv:2605.23733},
year={2026}
}Please also cite SONIC, the pretrained backbone this work transfers from:
@article{luo2025sonic,
title={SONIC: Supersizing Motion Tracking for Natural Humanoid Whole-Body Control},
author={Luo, Zhengyi and Yuan, Ye and Wang, Tingwu and Li, Chenran and Chen, Sirui
and Casta\~neda, Fernando and Cao, Zi-Ang and Li, Jiefeng and Minor, David
and Ben, Qingwei and Da, Xingye and Ding, Runyu and Hogg, Cyrus and Song, Lina
and Lim, Edy and Jeong, Eugene and He, Tairan and Xue, Haoru and Xiao, Wenli
and Wang, Zi and Yuen, Simon and Kautz, Jan and Chang, Yan and Iqbal, Umar
and Fan, Linxi and Zhu, Yuke},
journal={arXiv preprint arXiv:2511.07820},
year={2025}
}Dual licensing, following upstream:
- Source code — Apache License 2.0
- Model weights — downloaded SONIC checkpoints are covered by the NVIDIA Open Model License
