# HUSKY: Humanoid Skateboarding System via Physics-Aware Whole-Body Control 基于 [HUSKY](https://arxiv.org/abs/2602.03205) 思路的人形滑板全身控制实验代码:mjlab 训练、`rsl_rl` 与 MuJoCo 评测脚本。本仓库包含个人开发与 **Docker** 封装。 **目录:** [`src/mjlab_husky`](src/mjlab_husky) · [`rsl_rl/`](rsl_rl/) · [`dataset/`](dataset/) · [`test_scene/`](test_scene/) · [`ckpts/`](ckpts/) --- ## 本地安装(Ubuntu 22.04,推荐 `uv`) ```bash curl -LsSf https://astral.sh/uv/install.sh | sh git clone https://github.com/<你的用户名>/humanoid_skateboarding.git cd humanoid_skateboarding uv sync && uv pip install -e . ``` **(可选)LeRobot v3 导出 / 边播边录** 需要额外安装 `lerobot`(不在默认 `pyproject` 依赖里): ```bash uv pip install lerobot ``` 若安装后出现 `import torch` 报 NCCL 符号错误,可尝试: ```bash uv pip uninstall nvidia-nccl-cu12 uv pip install --force-reinstall "nvidia-nccl-cu13>=2.29" ``` --- ## 训练 ```bash cd humanoid_skateboarding uv run train Mjlab-Skater-Flat-Unitree-G1 --env.scene.num-envs 4096 ``` 查看全部参数: ```bash uv run train Mjlab-Skater-Flat-Unitree-G1 --help ``` --- ## 回放 `play` 任务名固定为 **`Mjlab-Skater-Flat-Unitree-G1`**(注册在 `mjlab_husky.tasks`)。 ### 通用 ```bash uv run play Mjlab-Skater-Flat-Unitree-G1 --checkpoint_file ckpts/test.pt ``` - **`--viewer auto`**(默认):有 `DISPLAY` / `WAYLAND_DISPLAY` 时用 **native**,否则 **rerun**。 - **`--viewer native`**:本机有图形界面时使用 MuJoCo 原生窗口。 - **`--viewer rerun`**:Rerun Web Viewer(无头服务器常用)。 - **`--viewer viser`**:Viser。 完整参数: ```bash uv run play Mjlab-Skater-Flat-Unitree-G1 --help ``` ### 无头 OpenGL(MuJoCo 离屏相机) 在无 `DISPLAY` 的 Linux 上,`play` 会在导入 MuJoCo 前尽量设置 **`MUJOCO_GL=egl`**(见 `mjlab_husky/mujoco_gl.py`)。若仍失败可手动指定: ```bash export MUJOCO_GL=egl # GPU 无头(常见) # 或 export MUJOCO_GL=osmesa # 纯 CPU 软件光栅(更慢) ``` ### Rerun:端口与远程浏览器 Rerun 需要 **两个端口**:**Web**(默认 `8080`)+ **gRPC**(默认多为 `9876`,以终端打印为准)。 **浏览器与 `play` 在同一台机器**:直接打开终端里 **`http://127.0.0.1:/?url=...`** 完整链接(不要只打开无 `?url=` 的首页)。 **浏览器在自己电脑、`play` 在云主机**:必须在本机做 **SSH 双端口转发**(把 `user@host` 换成你的登录方式,端口与 `play` 一致): ```bash ssh -N -L 8080:127.0.0.1:8080 -L 9876:127.0.0.1:9876 user@云主机IP ``` 若使用 `~/.ssh/config` 里的 `Host` 别名(例如 `Seoul`): ```bash ssh -N -L 8080:127.0.0.1:8080 -L 9876:127.0.0.1:9876 Seoul ``` 指定密钥时: ```bash ssh -i ~/.ssh/你的_key -N -L 8080:127.0.0.1:8080 -L 9876:127.0.0.1:9876 ubuntu@云主机IP ``` **本机 8080/9876 已被占用**时,改用空闲本地端口,并同时改 `?url=` 里 gRPC 端口,例如: ```bash ssh -N -L 18080:127.0.0.1:18080 -L 19876:127.0.0.1:19876 user@云主机IP ``` 云主机上 `play` 需一致: ```bash uv run play Mjlab-Skater-Flat-Unitree-G1 --checkpoint_file ckpts/test.pt \ --viewer rerun \ --rerun-web-port 18080 \ --rerun-grpc-port 19876 ``` **不经 SSH、浏览器直连公网**:安全组放行 Web + gRPC 端口,并指定(示例): ```bash uv run play ... --viewer rerun --rerun-connect-host <云主机公网IP> ``` ### Rerun 常用性能参数(可选) ```bash uv run play Mjlab-Skater-Flat-Unitree-G1 --checkpoint_file ckpts/test.pt \ --viewer rerun \ --rerun-viewer-width 640 --rerun-viewer-height 360 \ --rerun-camera-log-stride 4 --rerun-qpos-log-stride 8 \ --rerun-camera-max-side 480 \ --no-rerun-open-browser ``` 说明:本项目 CLI 使用 **tyro**,布尔开关一般为 **`--xxx` / `--no-xxx`**(例如 `--lerobot-record`、`--no-rerun-open-browser`),不要写成 `--lerobot-record True`。 --- ## LeRobot v3 数据(`lerobot_data/`) LeRobot **v3** 为 **Parquet + `meta/`**(不是 HDF5)。本仓库提供两种方式写入 **`observation.state`(qpos,float32 向量)**。 ### 1)离线批量导出(不跑 Rerun) 需已 `uv pip install lerobot`。 ```bash uv run python -m mjlab_husky.scripts.export_lerobot_qpos \ --task-id Mjlab-Skater-Flat-Unitree-G1 \ --checkpoint-file ckpts/test.pt \ --out-dir lerobot_data \ --dataset-name mjlab_husky_skater_qpos \ --episodes 1 \ --steps-per-episode 1000 \ --overwrite ``` ### 2)`play` + Rerun 同时边播边录 ```bash uv run play Mjlab-Skater-Flat-Unitree-G1 \ --checkpoint_file ckpts/test.pt \ --viewer rerun \ --rerun-web-port 18080 \ --rerun-grpc-port 19876 \ --lerobot-record \ --lerobot-out-dir lerobot_data \ --lerobot-dataset-name mjlab_husky_live \ --lerobot-overwrite ``` 要点: - **`--lerobot-overwrite`**:每次启动会 **删除** 同名数据集目录;要 **累积** 多次运行,请 **去掉** 该参数,或换 `--lerobot-dataset-name`。 - 默认每录满 **`--lerobot-steps-per-episode`**(默认 1000)帧会 `save_episode()` 一次;仿真里多次 `reset` **不会**自动切分,除非打开 **`--lerobot-save-on-env-reset`**。 - 退出 `play`(如 Ctrl+C)时会 `finalize()`,避免 Parquet 不完整。 按仿真每次 `done -> reset` 存成一个 LeRobot episode: ```bash uv run play Mjlab-Skater-Flat-Unitree-G1 ... --lerobot-record --lerobot-save-on-env-reset ``` ### 检查数据集是否可读(行数 / episode) ```bash uv run python -c " from pathlib import Path import json info = json.loads(Path('lerobot_data/mjlab_husky_live/meta/info.json').read_text()) print('total_episodes', info.get('total_episodes'), 'total_frames', info.get('total_frames')) " ``` --- ## Docker(推荐) 基础环境:Ubuntu 22.04、CUDA 13、`uv` 与项目依赖。镜像 **`MUJOCO_GL=egl`**,默认 **`CMD`** 为 **Rerun** 回放(`--no-rerun-open-browser`)。 **构建** ```bash docker build -t husky-skate:latest . ``` **GPU 运行**(需 [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html)) ```bash docker run --gpus all -it --rm -p 8080:8080 -p 9876:9876 husky-skate:latest ``` 在**宿主机浏览器**打开容器日志里打印的 **`http://127.0.0.1:8080/?url=...`**(若浏览器不在宿主机,需自行把对应端口转发到本机)。 **宿主机 8080/9876 已被占用**(例如已有其他容器映射):换主机端口 + 覆盖容器内 `play` 端口,例如: ```bash docker run --gpus all -it --rm -p 18080:18080 -p 19876:19876 husky-skate:latest \ uv run play Mjlab-Skater-Flat-Unitree-G1 --checkpoint_file ckpts/test.pt \ --viewer rerun \ --rerun-web-port 18080 \ --rerun-grpc-port 19876 \ --no-rerun-open-browser ``` **仅 CPU**(较慢) ```bash docker run -it --rm -p 8080:8080 -p 9876:9876 husky-skate:latest ``` **进入容器 Shell** ```bash docker run --gpus all -it --rm --entrypoint /bin/bash husky-skate:latest ``` **容器内训练** ```bash docker run --gpus all -it --rm husky-skate:latest \ uv run train Mjlab-Skater-Flat-Unitree-G1 --env.scene.num-envs 4096 ``` **(可选)容器内录 LeRobot**:需先安装 `lerobot`,并把目录挂载出来,例如: ```bash docker run --gpus all -it --rm \ -p 18080:18080 -p 19876:19876 \ -v "$(pwd)/lerobot_data:/app/lerobot_data" \ husky-skate:latest \ bash -lc 'uv pip install lerobot && uv run play Mjlab-Skater-Flat-Unitree-G1 \ --checkpoint_file ckpts/test.pt --viewer rerun \ --rerun-web-port 18080 --rerun-grpc-port 19876 --no-rerun-open-browser \ --lerobot-record --lerobot-out-dir lerobot_data --lerobot-dataset-name mjlab_docker_live \ --lerobot-overwrite' ``` --- ## PyTorch / CUDA 提示 若日志出现 **driver too old(如 12080)** 且 `torch.cuda.is_available()` 为 `False`,多为 **PyTorch cu13x 与当前驱动 API 不匹配**。可选: - 安装与驱动匹配的 **cu12x** 轮子,例如: `uv pip install torch torchvision --index-url https://download.pytorch.org/whl/cu124` - 或升级宿主机 NVIDIA 驱动以匹配当前 PyTorch 所要求的 CUDA。 仿真侧 **Warp/MuJoCo** 仍可能显示 `cpu`,与 **`torch.cuda.is_available()` 为 `play` 选的 device** 一致。 --- ## 轻量 MuJoCo 评测 ```bash bash test_scene/sim.sh your-onnx-path ``` | Viser | MuJoCo | |-------|--------| | ![](media/viser.gif) | ![](media/mjc.gif) | --- ## 论文引用(原论文) ```bibtex @article{han2026husky, title={HUSKY: Humanoid Skateboarding System via Physics-Aware Whole-Body Control}, author={Jinrui Han and Dewei Wang and Chenyun Zhang and Xinzhe Liu and Ping Luo and Chenjia Bai and Xuelong Li}, journal={arXiv preprint arXiv:2602.03205}, year={2026} } ```