wholebody-vlagrootvlawhole-body-vlalerobotunitree-g1sonicdataset

GR00T whole-body VLA data: dùng open dataset

Phần 1 của series GR00T whole-body VLA data pipeline: tải public dataset, chuẩn hóa GR00T-LeRobot, verify, fine-tune và inference.

Nguyễn Anh Tuấn6 tháng 6, 202613 phút đọc
GR00T whole-body VLA data: dùng open dataset

GR00T whole-body VLA data: dùng open dataset

Disclosure: Bài viết có thể chứa affiliate/referral links. Nếu bạn mua hoặc đăng ký qua các link đó, VnRobo có thể nhận commission hoặc credit. Nội dung kỹ thuật ưu tiên tính đúng và khả năng chạy được.

Series này nói thẳng vào phần dễ lỗi nhất khi dùng NVIDIA Isaac-GR00T cho whole-body VLA: dữ liệu. Model GR00T N1.5/N1.7 không chỉ cần ảnh và câu lệnh; nó cần dataset đúng format, đúng state/action slicing, đúng modality.json, đúng embodiment-tag, và nếu dùng whole-body kiểu UNITREE_G1 / GEAR-SONIC, action space còn phải khớp với decoder/controller.

Phần 1 dùng open/public dataset. Mục tiêu là bạn có một dataset tải từ Hugging Face, verify được theo format GR00T-LeRobot rồi chạy được fine-tune hoặc ít nhất open-loop inference.

Nguồn chính và điều cần biết trước

Theo README hiện tại của NVIDIA/Isaac-GR00T, GR00T N1.7 dùng workflow: prepare data → inference → fine-tune → evaluate → deploy. Dataset dùng flavor của LeRobot v2 và cần thêm meta/modality.json để mô tả cách tách state/action/video. N1.7 hiện là Early Access, nên một số path/flag có thể đổi trước GA.

Nguồn kỹ thuật nên đọc kèm:

Những dataset public đáng xem:

Dataset Khi nào dùng Ghi chú
nvidia/Arena-G1-Loco-Manipulation-Task Sim/public G1 loco-manipulation Có thư mục lerobot, meta/modality.json, được mô tả là GR00T-Lerobot formatted dataset.
nvidia/PhysicalAI-Robotics-GR00T-Teleop-G1 Real/teleop G1 public examples Có các task con như g1-pick-pear, cấu trúc LeRobot + modality.json.
PID0930/g1-inspire-pick-cube-gr00t-lerobot Unitree G1 + Inspire hands, nhỏ để smoke test Dataset card ghi dùng NEW_EMBODIMENT và config riêng. Cần kiểm chứng path config trong repo của bạn.
SensoriRobotics/g1_locomanipulation_sdg Synthetic G1 loco-manipulation Dataset card ghi LeRobot v2.0 cho GR00T N1.6; phù hợp học format/mix data.

Không phải dataset G1 nào cũng dùng được với UNITREE_G1_SONIC. Nếu action là raw joint positions, base velocity hoặc custom hand pose, bạn thường phải dùng NEW_EMBODIMENT và modality config riêng. Nếu dataset là SONIC latent action space, lúc đó mới dùng UNITREE_G1_SONIC.

Lộ trình thực dụng để chạy thành công

Nếu mục tiêu của bạn là thật sự train/infer được, đừng bắt đầu bằng custom sim hay robot thật ngay. Đi theo thứ tự này:

1. Tải một public GR00T-LeRobot dataset đã có meta/modality.json
2. Verify dataset và in thử 1 parquet row
3. Chạy smoke fine-tune 100 step với config khớp dataset
4. Load checkpoint bằng open-loop eval hoặc PolicyServer
5. Sau đó mới thay bằng data sim hoặc data real của bạn
6. Nếu dùng G1 + SONIC, giữ rõ 2 phần:
   - GR00T VLA fine-tune: học action 78-dim SONIC latent + hands
   - SONIC controller training: học decoder/controller, xem Phần 4

Điểm quan trọng: Phần 1-3 không train SONIC controller từ motion data. Chúng train/fine-tune GR00T VLA trên LeRobot dataset. SONIC controller có thể dùng checkpoint đã release; chỉ tự train/fine-tune SONIC khi bạn cần controller/motion foundation mới hoặc embodiment mới.

1.1 Download open dataset

Mục tiêu

Tải dataset public về local, không làm mất cấu trúc thư mục, và xác định nó thuộc loại:

  • SONIC latent whole-body: dùng UNITREE_G1_SONIC.
  • G1 raw/custom action: dùng NEW_EMBODIMENT + modality config riêng.
  • Arm/tabletop dataset: không phù hợp trực tiếp cho whole-body, chỉ dùng để test loader hoặc code path.

Yêu cầu môi trường

Máy tải dataset:

  • Ubuntu 22.04/24.04 hoặc Linux tương đương.
  • Python 3.10+.
  • git-lfs, uv, huggingface_hub.
  • Dung lượng trống: ít nhất 2-10 GB cho public dataset nhỏ; nhiều hơn nếu tải dataset video lớn.

Máy train/inference:

  • Inference/open-loop nhỏ: GPU 16-24 GB VRAM có thể thử với batch nhỏ, nhưng dễ OOM.
  • Fine-tune debug: thực dụng hơn với 1 GPU 48-80 GB VRAM.
  • Fine-tune whole-body nghiêm túc: 4+ GPU 80 GB VRAM là mức nên chuẩn bị. NVIDIA GEAR docs khuyến nghị multi-GPU cho workflow G1/SONIC.
  • N1.7 base model tải từ Hugging Face khoảng nhiều GB; cần network ổn định.

Setup repo Isaac-GR00T

sudo apt update
sudo apt install -y git git-lfs
git lfs install

git clone --recurse-submodules https://github.com/NVIDIA/Isaac-GR00T.git
cd Isaac-GR00T

# Theo README N1.7, repo dùng uv.
curl -LsSf https://astral.sh/uv/install.sh | sh
uv sync --all-extras

# Nếu dataset/model cần auth hoặc license acceptance.
uv run hf auth login

Nếu đã clone thiếu submodule:

git submodule update --init --recursive

Tải dataset Arena G1 có thư mục LeRobot

Dataset nvidia/Arena-G1-Loco-Manipulation-Task là lựa chọn tốt để học format vì card ghi có dataset GR00T-Lerobot converted trong thư mục lerobot.

mkdir -p datasets

uv run hf download nvidia/Arena-G1-Loco-Manipulation-Task \
  --repo-type dataset \
  --include "lerobot/**" \
  --local-dir datasets/arena_g1_loco

find datasets/arena_g1_loco -maxdepth 3 -type f | head -40

Nếu repo lưu trực tiếp lerobot/meta, dataset root để đưa vào GR00T sẽ là:

export DATASET_ROOT="$PWD/datasets/arena_g1_loco/lerobot"

Nếu hf download tạo thêm lớp thư mục, kiểm tra bằng:

find datasets/arena_g1_loco -name modality.json -print

Tải G1 teleop public example

uv run hf download nvidia/PhysicalAI-Robotics-GR00T-Teleop-G1 \
  --repo-type dataset \
  --include "g1-pick-pear/**" \
  --local-dir datasets/g1_teleop_public

export DATASET_ROOT="$PWD/datasets/g1_teleop_public/g1-pick-pear"

Dataset này có meta/modality.json với các slice state/action như left_leg, right_leg, waist, left_arm, left_hand, right_arm, right_hand. Đây là ví dụ tốt để hiểu whole-body slicing, nhưng lệnh train chính xác phụ thuộc config/embodiment mà checkpoint của bạn hỗ trợ.

Tải dataset nhỏ để smoke test

uv run hf download PID0930/g1-inspire-pick-cube-gr00t-lerobot \
  --repo-type dataset \
  --local-dir datasets/g1_inspire_pick_cube

export DATASET_ROOT="$PWD/datasets/g1_inspire_pick_cube"

Dataset card ghi dùng --embodiment-tag NEW_EMBODIMENTexamples/G1Inspire/g1_inspire_config.py. Path này cần kiểm chứng theo branch Isaac-GR00T bạn đang dùng; nếu file không tồn tại, bạn phải tự tạo modality config từ meta/modality.json.

1.2 Chuyển/verify GR00T-LeRobot format

Mục tiêu

Dataset hợp lệ phải có ít nhất:

dataset_root/
├── meta/
│   ├── info.json
│   ├── episodes.jsonl
│   ├── tasks.jsonl
│   └── modality.json
├── data/
│   └── chunk-000/
│       ├── episode_000000.parquet
│       └── episode_000001.parquet
└── videos/
    └── chunk-000/
        └── observation.images.ego_view/
            ├── episode_000000.mp4
            └── episode_000001.mp4

Một số LeRobot v2.1 exporter lưu parquet dưới dạng data/train-00000.parquet thay vì data/chunk-000/episode_*.parquet. GR00T docs mô tả cấu trúc chunk/episode, nhưng GR00T-WholeBodyControl data collection docs cũng hiển thị output data/train-00000.parquet. Vì vậy:

  • Nếu dataset đến từ GR00T-WholeBodyControl exporter mới, loader có thể vẫn nhận.
  • Nếu loader Isaac-GR00T của bạn lỗi, cần chuyển về layout chunk/episode hoặc dùng branch đúng version. Đây là điểm cần kiểm chứng theo version.

Kiểm tra file bắt buộc

test -f "$DATASET_ROOT/meta/info.json"
test -f "$DATASET_ROOT/meta/episodes.jsonl"
test -f "$DATASET_ROOT/meta/tasks.jsonl"
test -f "$DATASET_ROOT/meta/modality.json"

python -m json.tool "$DATASET_ROOT/meta/modality.json" | head -80
head -3 "$DATASET_ROOT/meta/episodes.jsonl"
head -3 "$DATASET_ROOT/meta/tasks.jsonl"
find "$DATASET_ROOT/data" -type f | head
find "$DATASET_ROOT/videos" -type f | head

Script verify nhanh

Tạo script:

mkdir -p tools
cat > tools/verify_groot_lerobot_dataset.py <<'PY'
import argparse
import json
from pathlib import Path

import pandas as pd

def read_jsonl(path):
    rows = []
    with path.open("r", encoding="utf-8") as f:
        for line in f:
            line = line.strip()
            if line:
                rows.append(json.loads(line))
    return rows

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("dataset")
    args = parser.parse_args()

    root = Path(args.dataset)
    meta = root / "meta"
    required = ["info.json", "episodes.jsonl", "tasks.jsonl", "modality.json"]
    for name in required:
        path = meta / name
        if not path.exists():
            raise SystemExit(f"missing {path}")

    modality = json.loads((meta / "modality.json").read_text())
    for section in ["state", "action"]:
        if section not in modality:
            raise SystemExit(f"modality.json missing {section}")

    episodes = read_jsonl(meta / "episodes.jsonl")
    tasks = read_jsonl(meta / "tasks.jsonl")
    parquet_files = sorted((root / "data").rglob("*.parquet"))
    video_files = sorted((root / "videos").rglob("*.mp4"))

    if not episodes:
        raise SystemExit("episodes.jsonl is empty")
    if not parquet_files:
        raise SystemExit("no parquet files under data/")

    print(f"episodes: {len(episodes)}")
    print(f"tasks: {len(tasks)}")
    print(f"parquet files: {len(parquet_files)}")
    print(f"video files: {len(video_files)}")
    print("state keys:", list(modality["state"].keys()))
    print("action keys:", list(modality["action"].keys()))

    sample = pd.read_parquet(parquet_files[0])
    print("sample parquet:", parquet_files[0])
    print("columns:", list(sample.columns)[:50])
    print("rows:", len(sample))
    print("OK")

if __name__ == "__main__":
    main()
PY

uv pip install pandas pyarrow
uv run python tools/verify_groot_lerobot_dataset.py "$DATASET_ROOT"

Kết quả đúng:

episodes: ...
tasks: ...
parquet files: ...
video files: ...
state keys: [...]
action keys: [...]
rows: ...
OK

Kiểm tra modality.json

Ví dụ whole-body raw/action slicing thường có dạng:

{
  "state": {
    "left_leg": { "start": 0, "end": 6 },
    "right_leg": { "start": 6, "end": 12 },
    "waist": { "start": 12, "end": 15 },
    "left_arm": { "start": 15, "end": 22 },
    "left_hand": { "start": 22, "end": 29 },
    "right_arm": { "start": 29, "end": 36 },
    "right_hand": { "start": 36, "end": 43 }
  },
  "action": {
    "left_leg": { "start": 0, "end": 6 },
    "right_leg": { "start": 6, "end": 12 }
  },
  "video": {
    "ego_view": {
      "original_key": "observation.images.ego_view"
    }
  }
}

Với SONIC latent action, action không phải raw joint target. Theo GR00T-WholeBodyControl docs, action per inference step cho UNITREE_G1_SONIC là 78 chiều: 64-dim motion token + 7 left hand + 7 right hand. Nếu dataset của bạn không có dạng này, đừng ép dùng UNITREE_G1_SONIC.

Nếu dataset là LeRobot v3

Isaac-GR00T docs nói GR00T hiện dùng LeRobot v2 và có script convert v3 sang v2:

uv run python scripts/lerobot_conversion/convert_v3_to_v2.py \
  --input-dir /path/to/lerobot_v3_dataset \
  --output-dir datasets/my_dataset_v2

Tên flag chính xác cần kiểm chứng bằng:

uv run python scripts/lerobot_conversion/convert_v3_to_v2.py --help

Sau convert, thêm hoặc kiểm tra lại meta/modality.json.

1.3 Training/fine-tune

Mục tiêu

Chạy fine-tune GR00T trên dataset public. Có hai nhánh:

  • Dataset khớp embodiment có sẵn: dùng tag có sẵn.
  • Dataset custom G1/raw whole-body: dùng NEW_EMBODIMENT + modality config.

Chọn embodiment tag

Theo Policy API docs:

Trường hợp Embodiment tag
DROID relative EEF/joint OXE_DROID_RELATIVE_EEF_RELATIVE_JOINT
LIBERO Panda LIBERO_PANDA
G1 + SONIC latent WBC UNITREE_G1_SONIC
Robot/dataset custom NEW_EMBODIMENT

Quy tắc thực dụng:

Nếu modality/action = SONIC latent 64 + hands:
  dùng UNITREE_G1_SONIC
Nếu modality/action = raw joints / custom hands / custom base:
  dùng NEW_EMBODIMENT + config riêng

Preflight trước khi fine-tune

Trước khi copy lệnh dài, kiểm tra launcher và config trước. Đây là bước giúp tránh 80% lỗi shape/tag:

cd Isaac-GR00T

uv run python gr00t/experiment/launch_finetune.py --help | \
  grep -E "dataset|embodiment|modality|base-model|max-steps|num-gpus"

export DATASET_ROOT=/abs/path/to/dataset
export MODALITY_CONFIG=/abs/path/to/configs/my_robot_config.py

test -d "$DATASET_ROOT"
test -f "$DATASET_ROOT/meta/modality.json"
test -f "$MODALITY_CONFIG"
python -m json.tool "$DATASET_ROOT/meta/modality.json" >/tmp/modality.pretty.json

Nếu branch Isaac-GR00T của bạn đổi tên flag, ưu tiên output từ --help của branch đó. Không đoán flag theo bài viết cũ.

Smoke test fine-tune với NEW_EMBODIMENT

export NUM_GPUS=1
export DATASET_ROOT=/abs/path/to/dataset
export MODALITY_CONFIG=/abs/path/to/configs/my_robot_config.py
export OUT=/tmp/groot_public_g1_smoke

test -f "$MODALITY_CONFIG"

CUDA_VISIBLE_DEVICES=0 uv run python \
  gr00t/experiment/launch_finetune.py \
  --base-model-path nvidia/GR00T-N1.7-3B \
  --dataset-path "$DATASET_ROOT" \
  --embodiment-tag NEW_EMBODIMENT \
  --modality-config-path "$MODALITY_CONFIG" \
  --num-gpus $NUM_GPUS \
  --output-dir "$OUT" \
  --save-total-limit 2 \
  --save-steps 100 \
  --max-steps 100 \
  --global-batch-size 4 \
  --dataloader-num-workers 2

Không dùng config SO100 hoặc config robot khác cho dataset G1. Với Unitree G1 / Inspire / raw whole-body dataset, MODALITY_CONFIG phải khớp meta/modality.json. Nếu repo không có sẵn examples/G1Inspire/g1_inspire_config.py, tạo file config riêng là bắt buộc. Đây là điểm cần kiểm chứng theo dataset.

Fine-tune với UNITREE_G1_SONIC

Dùng khi dataset thật sự là SONIC latent action dataset:

export NUM_GPUS=4
export DATASET_ROOT=/abs/path/to/sonic_lerobot_dataset
export MODALITY_CONFIG=gr00t/configs/data/embodiment_configs.py
export OUT=/mnt/checkpoints/groot_g1_sonic_public

test -f "$DATASET_ROOT/meta/modality.json"
test -f "$MODALITY_CONFIG"

uv run torchrun --nproc_per_node=$NUM_GPUS --master_port=29500 \
  gr00t/experiment/launch_finetune.py \
  --base-model-path nvidia/GR00T-N1.7-3B \
  --dataset-path "$DATASET_ROOT" \
  --embodiment-tag UNITREE_G1_SONIC \
  --modality-config-path "$MODALITY_CONFIG" \
  --num-gpus $NUM_GPUS \
  --output-dir "$OUT" \
  --save-total-limit 5 \
  --save-steps 5000 \
  --max-steps 20000 \
  --use-wandb \
  --global-batch-size 32 \
  --color-jitter-params brightness 0.3 contrast 0.4 saturation 0.5 hue 0.08 \
  --dataloader-num-workers 4

Nếu dùng torchrun, README nhấn mạnh nên dùng uv run torchrun để đúng virtual environment.

Output mẫu

/mnt/checkpoints/groot_g1_sonic_public/
├── checkpoint-5000/
├── checkpoint-10000/
├── checkpoint-15000/
├── checkpoint-20000/
├── config.json
├── processor_config.json
└── runs/ hoặc wandb/

Tiêu chí "đã làm đúng"

  • Training không lỗi loader trong 100-500 step đầu.
  • GPU memory ổn định, không tăng vô hạn.
  • Loss giảm hoặc ít nhất không NaN.
  • Checkpoint có config.json, processor_config.json, model weights.
  • Nếu dùng NEW_EMBODIMENT, checkpoint lưu modality config để inference không cần import nhiều config cùng lúc.

1.4 Inference và đánh giá

Open-loop evaluation

Open-loop không chứng minh robot sẽ chạy tốt, nhưng bắt lỗi shape, modality, normalization rất nhanh.

uv run python gr00t/eval/open_loop_eval.py \
  --dataset-path "$DATASET_ROOT" \
  --embodiment-tag NEW_EMBODIMENT \
  --model-path "$OUT/checkpoint-100" \
  --traj-ids 0 \
  --action-horizon 16 \
  --steps 200 \
  --modality-keys single_arm gripper

--modality-keys phải khớp config của bạn. Với whole-body raw G1, giá trị ví dụ single_arm gripper có thể sai. Chạy:

uv run python gr00t/eval/open_loop_eval.py --help

và kiểm tra modality keys từ config/dataset.

PolicyServer cho whole-body / deployment

Nếu checkpoint là UNITREE_G1_SONIC:

Terminal 1, trên GPU machine:

uv run python gr00t/eval/run_gr00t_server.py \
  --model-path /mnt/checkpoints/groot_g1_sonic_public/checkpoint-20000 \
  --embodiment-tag UNITREE_G1_SONIC \
  --device cuda:0 \
  --port 5550

Terminal 2, từ repo GR00T-WholeBodyControl:

python gear_sonic/scripts/launch_inference.py \
  --policy-host <gpu_machine_ip> \
  --policy-port 5550 \
  --camera-host 192.168.123.164 \
  --prompt "pick up the soda can and place it in the bin"

Simulation:

python gear_sonic/scripts/launch_inference.py --sim \
  --policy-host 127.0.0.1 \
  --policy-port 5550 \
  --prompt "pick up the apple"

Tiêu chí "đã làm đúng"

  • run_gr00t_server.py load checkpoint không báo mismatch embodiment.
  • Client ping hoặc request action được.
  • Action shape đúng:
    • UNITREE_G1_SONIC: 78 chiều mỗi inference step theo docs.
    • Custom NEW_EMBODIMENT: đúng action dimension trong modality.json.
  • Open-loop predicted action không NaN/Inf.
  • Nếu chạy sim, robot không đứng im do action all-zero hoặc mismatch control loop.

Lỗi thường gặp và cách fix

Lỗi Nguyên nhân thường gặp Cách fix
missing meta/modality.json Dataset LeRobot thường chưa phải GR00T-LeRobot Tạo meta/modality.json theo state/action/video slicing.
unknown embodiment tag Dùng tag không có trong version repo Chạy policy.md/--help, kiểm tra tag hiện có; dùng NEW_EMBODIMENT nếu custom.
shape mismatch State/action dim trong parquet không khớp modality/config In sample parquet, so sánh dim với modality.json; sửa slicing.
video key not found modality.json video key khác folder video Map đúng original_key và folder videos/....
OOM khi fine-tune Batch quá lớn, worker/shard quá nặng, GPU VRAM thấp Giảm --global-batch-size, --dataloader-num-workers, dùng ít shard, gradient checkpointing nếu repo hỗ trợ.
Address already in use khi PolicyServer Port ZMQ đang bị chiếm Dùng --port 5551 và client cùng port.
Dùng UNITREE_G1_SONIC nhưng action raw joint Sai embodiment/action space Chuyển sang NEW_EMBODIMENT hoặc convert action sang SONIC latent nếu có pipeline chính thức.

Bài viết liên quan

Nguồn

NT

Nguyễn Anh Tuấn

Robotics & AI Engineer. Building VnRobo — sharing knowledge about robot learning, VLA models, and automation.

Khám phá VnRobo

Bài viết liên quan

GR00T whole-body VLA data: có cần data real?
wholebody-vla

GR00T whole-body VLA data: có cần data real?

6/6/202613 phút đọc
NT
GR00T whole-body VLA: train SONIC controller
wholebody-vla

GR00T whole-body VLA: train SONIC controller

6/6/20269 phút đọc
NT
GR00T whole-body VLA data: sinh data sim
wholebody-vla

GR00T whole-body VLA data: sinh data sim

6/6/202614 phút đọc
NT