# HumDex: Humanoid Dexterous Manipulation Made Easy By [Liang Heng](https://liangheng121.github.io/), [Yihe Tang](https://tangyihe.com/), [Jiajun Xu](https://georginhsu.github.io/), [Henghui Bao](https://scholar.google.com/citations?user=88XEeKAAAAAJ&hl=en), Di Huang, [Yue Wang](https://yuewang.xyz/)

Banner for HumDex

Project Page Paper Issues Hugging Face Model Card X (Twitter)

## News - **2026-05-31**. ***Sonic*** channel: fixed teleop turning and latency (apply the one-time [controller patch](#3-clone-gr00t-wholebodycontrol-for-sonic)), and added token-level data collection, training, and inference. - **2026-03-27**. ***Xsens*** body teleoperation is available in HumDex (`body_source: xsens`). - **2026-03-16**. ***Manus*** hand tracking has been integrated into the teleop pipeline (`hand_source: manus`). - **2026-03-10**. Added ***Sonic*** teleoperation support (`policy: sonic`). ## Content Table - [Installation](#installation) - [Teleop](#teleop) - [G1 Controller](#g1-controller) - [Wuji Hand Controller](#wuji-hand-controller) - [Wuji Policy](#wuji-policy) - [Data Collection](#data-collection) - [Policy Learning](#policy-learning) - [Citation](#citation) --- ## Installation We will have two conda environments for Humdex. One is called `humdex`, which can be used for controller training, controller deployment, and teleop data collection. The other is called `gmr`, which can be used for online motion retargeting. ### 1) Create `gmr` Environment ```bash conda create -n gmr python=3.10 -y conda activate gmr git clone https://github.com/YanjieZe/GMR.git cd GMR # install GMR pip install -e . cd .. pip install python-osc zmq pyyaml conda install -c conda-forge libstdcxx-ng -y ``` ### 2) Create `humdex` Environment ```bash conda create -n humdex python=3.8 -y conda activate humdex # install wuji-retargeting cd wuji-retargeting git submodule update --init --recursive pip install -r requirements.txt pip install -e . && cd .. # install wuji_policy cd wuji_policy pip install -r requirements.txt pip install -e . && cd .. # install for act cd act pip install -r requirements.txt cd .. ``` For the rest of `humdex` environment setup, follow TWIST2 README: - [Step 2: Install Isaac Gym](https://github.com/LiangHeng121/TWIST2?tab=readme-ov-file#step-2-install-isaac-gym) - [Step 3: Install Packages](https://github.com/LiangHeng121/TWIST2?tab=readme-ov-file#step-3-install-packages) - [Step 4: Install Unitree SDK2 for Laptop Sim2Real](https://github.com/LiangHeng121/TWIST2?tab=readme-ov-file#step-4-install-unitree-sdk2-for-laptop-sim2real) ### 3) Clone `GR00T-WholeBodyControl` (for sonic) ```bash cd .. git clone https://github.com/NVlabs/GR00T-WholeBodyControl.git cd GR00T-WholeBodyControl git lfs pull ``` Then follow the official [doc](https://nvlabs.github.io/GR00T-WholeBodyControl/) to install its environment. To lower teleop latency, patch line 3392 of `gear_sonic_deploy/src/g1/g1_deploy_onnx_ref/src/g1_deploy_onnx_ref.cpp` (in `CurrentFrameAdvancement()`), as discussed in the [Sonic issue #60](https://github.com/NVlabs/GR00T-WholeBodyControl/issues/60): ```diff - if (current_frame_ >= current_motion_->timesteps - saved_frame_for_observation_window_) { + constexpr int kStreamWindowCap = 10; + const int eff_window = std::min(saved_frame_for_observation_window_, kStreamWindowCap); + if (current_frame_ >= current_motion_->timesteps - eff_window) { ``` --- ## Teleop For a concise end-to-end setup flow, see [`doc/teleop.md`](doc/teleop.md). ### 1) Unified Entry ```bash conda activate gmr bash scripts/teleop.sh [options] [-- extra_args] ``` Supported selectors: - `--policy {twist2|sonic}` (default `twist2`) - `--body {vdmocap|slimevr|xsens}` (default `vdmocap`) - `--hand {vdhand|manus}` (default `vdhand`) ### 2) Common Examples Run sonic + vdmocap + manus: ```bash bash scripts/teleop.sh --policy sonic --body vdmocap --hand manus ``` Run twist2 + slimevr + vdhand: ```bash bash scripts/teleop.sh --policy twist2 --body slimevr --hand vdhand ``` Run twist2 + xsens + manus: ```bash bash scripts/teleop.sh --policy twist2 --body xsens --hand manus ``` ### 3) Config Files Main YAML: - `deploy_real/config/teleop.yaml` Config Structure: - `runtime`: loop settings like `target_fps`, `print_every`, `max_steps` - `network`: redis + mocap transport (`network.redis`, `network.mocap.default/body/hand`) - `retarget`: retarget core settings (`actual_human_height`, `hands`, `format`, `offset_to_ground`) - `control`: runtime control settings (`safe_idle_pose_id`, `ramp_*_seconds`, `ramp_ease`) - `adapters`: source-specific settings (`vdmocap`, `vdhand`, `manus`, `slimevr`, `xsens`) - `policy`: policy-specific settings (`policy.sonic`, `policy.twist2`) ### 4) Keyboard Behavior - `k`: toggle send/default mode - `p`: toggle send/hold mode --- ## G1 Controller ### 1) Sim Controller ```bash ## for --policy twist2 conda activate humdex # Warm arp the redis server at first time bash scripts/run_motion_server.sh bash scripts/sim2sim.sh ## for --policy sonic # Terminal 1 — MuJoCo simulator cd ../GR00T-WholeBodyControl source .venv_sim/bin/activate python gear_sonic/scripts/run_sim_loop.py # Terminal 2 — C++ deployment cd ../GR00T-WholeBodyControl/gear_sonic_deploy source scripts/setup_env.sh bash deploy.sh sim --input-type zmq ``` ### 2) Real Controller ```bash ## for --policy twist2 conda activate humdex bash scripts/run_motion_server.sh # edit `net` in `scripts/sim2real.sh` to your real NIC name before running bash scripts/sim2real.sh ## for --policy sonic cd ../GR00T-WholeBodyControl/gear_sonic_deploy source scripts/setup_env.sh bash deploy.sh real --input-type zmq ``` --- ## Wuji Hand Controller For Wuji device setup details, see [`doc/wuji.md`](doc/wuji.md). ### 1) Sim Hand Controller ```bash conda activate humdex bash scripts/wuji_hand_sim.sh ``` ### 2) Real Hand Controller ```bash conda activate humdex bash scripts/wuji_hand_real.sh ``` --- ## Wuji Policy ### 1) Build training `.npz` from collected data ```bash conda activate humdex bash scripts/wuji_data_collect.sh ``` This generates: - `wuji_policy/data/wuji_right.npz` ### 2) Training ```bash conda activate humdex # default: wuji_policy/data/wuji_right_000.npz -> checkpoint tag wuji_right_000 bash scripts/wuji_hand_training.sh ``` Edit top variables in `wuji_hand_training.sh` to switch dataset/tag/hyperparameters: - `human_data_name` - `hand_config` (`wuji_right` / `wuji_left`) - `ckpt_tag` - `epoch`, `n_samples`, `batch_size`, `lr`, `save_every` ### 3) Inference The scripts in [Wuji Hand Controller](#wuji-hand-controller) use `wuji-retargeting` by default. To use a trained policy instead: 1. In `wuji_hand_real.sh` and `wuji_hand_sim.sh`, set: - `policy_tag` to your checkpoint tag - `policy_epoch` to your checkpoint epoch (use `-1` for latest) 2. In both scripts, uncomment: - `--use_model` - `--policy_tag ${policy_tag}` - `--policy_epoch ${policy_epoch}` --- ## Data Collection For robot/human recording workflow and saved data layout, see [`doc/data_collection.md`](doc/data_collection.md). ### 1) Start Camera Stream on g1 ```bash bash scripts/realsense_zmq_pub_g1.sh ``` ### 2) Teleop Data Recording ```bash bash scripts/data_record.sh # sonic channel bash scripts/data_record.sh --channel sonic ``` ### 3) Human Data Recording ```bash bash scripts/data_record_human.sh # sonic channel bash scripts/data_record_human.sh --channel sonic ``` --- ## Policy Learning ### 1) Data Processing **a) Human data preprocessing:** For human tracking data, approximate proprioceptive state with previous-frame action. Skip this step for robot data and sonic human data. ```bash cd act python scripts/convert_human_data.py \ --dataset_dir /path/to/human_dataset ``` Processes `episode_*` folders in-place (creates `data_original.json` + `rgb_original/` backups). `--no_backup` to skip creating backups. **b) Convert dataset to HDF5:** ```bash cd act python convert_to_hdf5.py \ --dataset_dir /path/to/dataset \ --output /path/to/output.hdf5 \ --verify ``` To merge multiple data folders into one HDF5: ```bash python convert_to_hdf5.py \ --dataset_dirs /path/to/dataset1 /path/to/dataset2 \ --output merged.hdf5 ``` HDF5 per-episode structure: - `state_body` (T, 31), `action_body` (T, 35) — twist2 (joint-level) - `state_body` (T, 29), `action_token` (T, 64) — sonic (token-level) - `state_wuji_hand_{left,right}` (T, 20), `action_wuji_qpos_target_{left,right}` (T, 20) - `head` (T,) JPEG bytes ### 2) Policy Training ```bash cd act python imitate_episodes.py \ --ckpt_root ./checkpoints \ --policy_class ACT \ --task_name my_task \ --batch_size 8 \ --seed 42 \ --num_epochs 3000 \ --lr 1e-5 \ --dataset_path /path/to/dataset.hdf5 \ --hand_side right \ --kl_weight 10 \ --chunk_size 50 \ --hidden_dim 512 \ --dim_feedforward 3200 \ --temporal_agg \ --wandb ``` `--hand_side`: `left`, `right`, or `both`. `--channel`: `twist2` (default, joint `action_body`) or `sonic` (token-level `action_token`). `--sequential_training --epochs_per_dataset epoch_num_stage_1 epoch_num_stage_2`: train on multiple datasets sequentially. `--resume --ckpt_dir ./checkpoints/my_task/`: resume from checkpoint. Checkpoint location: `///` ### 3) Policy Inference #### a) Twist2 channel, Offline Run the policy on recorded dataset observations and render a side-by-side sim comparison video: ```bash cd act MUJOCO_GL=egl python policy_inference.py eval_offline \ --ckpt_dir ./checkpoints/my_task/ \ --dataset /path/to/dataset.hdf5 \ --episode 0 \ --hand_side both \ --chunk_size 300 \ --temporal_agg ``` Output video is saved to `/eval_ep{N}.mp4`. Use `--save_actions` to also dump predicted/GT actions as `.npy`. Set `MUJOCO_GL=egl` on headless servers. #### b) Twist2 channel, Online Online inference drives the real robot — first start the G1 controller ([G1 Controller](#g1-controller)), the camera (`scripts/realsense_zmq_pub_g1.sh`), and the hand controller `scripts/wuji_hand_qpos_real.sh`. Our trained policy and robot initial poses are available at https://huggingface.co/heng222/humdex. **Step 1 — (Optional) Move robot to initial pose:** ```bash cd act python policy_inference.py init_pose \ --init_pose_file ./checkpoints/my_task/init_pose.json \ --ramp_seconds 3.0 ``` Publishes a fixed body+hand pose and holds until Ctrl-C. Use `--ramp_seconds` to smoothly interpolate from the current position. Alternatively, load from a dataset instead of JSON: ```bash python policy_inference.py init_pose \ --dataset /path/to/dataset.hdf5 --episode 0 --hand_side both ``` **Step 2 — Run policy inference:** ```bash cd act python policy_inference.py eval_online \ --ckpt_dir ./checkpoints/my_task/ \ --hand_side both \ --chunk_size YOUR_CHUNK_SIZE \ --vision_ip \ --temporal_agg ``` Toggle inference on/off with keyboard (`k` = send, `p` = hold position, same as teleoperation). #### c) Sonic channel, Online Same prerequisites as above, but the body controller is the sonic `deploy.sh` (see [G1 Controller](#g1-controller)). ```bash cd act python policy_inference_sonic.py \ --ckpt_dir ./checkpoints/my_task/ \ --hand_side both \ --vision_ip ``` `k` = send/stop, `p` = hold (same as teleop). --- ## Citation If you find this work useful, please cite: ```bibtex @misc{heng2026humdexhumanoiddexterousmanipulation, title={HumDex: Humanoid Dexterous Manipulation Made Easy}, author={Liang Heng and Yihe Tang and Jiajun Xu and Henghui Bao and Di Huang and Yue Wang}, year={2026}, eprint={2603.12260}, archivePrefix={arXiv}, primaryClass={cs.RO}, url={https://arxiv.org/abs/2603.12260}, } ```