博客 / 詳情

返回

PAI Physical AI Notebook詳解4:基於仿真的GR00T-N1.5模型微調

在前3期Physical AI詳解系列中,我們詳細解讀了數據採集、擴增、增強的全過程,以及導航模型(X-Mobility)微調訓練的全過程。
在本期,我們將針對更復雜的VLA模型(以GR00T-N1.5為例)進行微調,同樣需要經過人工演示、數據擴增、模仿學習、在環驗證這幾個步驟。

圖片

但是,相比前例中的BC-RNN和X-Mobility模型,GR00T-N1.5是一個更復雜的模型,需要更大規模的數據合成、模仿學習,並需要獨立的服務端-客户端架構進行在環驗證。相對於本系列的前3個最佳實踐,本例可重點學習以下能力:

  • 使用RobotLearningLab公共數據集
  • 使用DLC進行分佈式大規模數據擴增
  • 使用DLC,針對較複雜的VLA模型進行分佈式模仿學習
  • 組合使用兩台DSW進行服務端-客户端架構的軟件在環驗證

在PAI的Notebook Gallery中,我們已經預置了一個最佳實踐,就是這個過程的一個具體示例:
gallery.pai-ml.com/#/preview/d…
圖片

下面我們來詳細解讀這個示例。

1. 環境準備

由於需要交互式環境進行人工演示,我們需要啓動DSW。
以北京Region為例,我們可以基於以下鏡像啓動DSW,其中已經包含了Isaac Lab 2.2及其依賴環境:
bash 體驗AI代碼助手 代碼解讀複製代碼dsw-registry-vpc.cn-beijing.cr.aliyuncs.com/pai-training-algorithm/isaac-sim:isaaclab220-nb4-v7-20250916

建議在啓動時選擇以下規格的公共資源或預付費資源配額,以確保具備Isaac Lab運行所需的RT Core:

  • ecs.ebmgn8is.32xlarge
  • ecs.gn8is-8x.32xlarge
  • ecs.ebmgn8te.32xlarge
  • ecs.ebmgn9t.48xlarge

同時,由於需要使用NVIDIA RobotLearningLab公共數據集,務必在啓動時掛載此數據集到/mnt/RobotLearningLab_Dataset/路徑,從而避免重複下載:
圖片
圖片

在環境啓動後,使用Notebook中的初始化腳本,配置環境變量並下載GR00T模型

import subprocess
import os
from urllib.parse import quote
from pathlib import Path

# 1. 持久&外部存儲的路徑,默認掛載外部存儲到/mnt/data下
os.environ['EXTERNAL_STORAGE_PATH'] = '/mnt/data/isaac_tmp/nb4'
path = os.environ['EXTERNAL_STORAGE_PATH']
os.system(f'mkdir -p "{path}"')
print(f"需要將外部存儲掛載到/mnt/data")
print(f"EXTERNAL_STORAGE_PATH: {path}\n")

# 2. Isaac Lab項目路徑
os.environ['ROBOT_LEARNING_LAB_PATH'] = "/workspace/RobotLearningLab"
path = os.environ['ROBOT_LEARNING_LAB_PATH']
print(f"ROBOT_LEARNING_LAB_PATH: {path}")
path = os.environ['ISAACLAB_PATH']
print(f"ISAACLAB_PATH: {path}\n")

# 3. 相關數據路徑,直接掛載PAI公共數據集RobotLearningLab_Dataset到/mnt/RobotLearningLab_Dataset
os.environ['ROBOT_LEARNING_LAB_DATA_PATH'] = "/mnt/RobotLearningLab_Dataset"
path = os.environ['ROBOT_LEARNING_LAB_DATA_PATH']
print(f"需要將PAI公共數據集RobotLearningLab_Dataset掛載到/mnt/RobotLearningLab_Dataset")
print(f"ROBOT_LEARNING_LAB_DATA_PATH: {path}")
print(f"我們預先採集和處理好的,可以用來訓練的數據集位於: {path}/usecase/galbot_stack_cube/lerobot_joint_space\n")

# 4. Isaac-GR00T代碼路徑
os.environ['ISAAC_GR00T_PATH'] = os.environ['EXTERNAL_STORAGE_PATH']+"/Isaac-GR00T"
path = os.environ['ISAAC_GR00T_PATH']
print(f"需要將代碼下載到指定路徑")
print(f"ISAAC_GR00T_PATH: {path}\n")

# 5. 相關模型路徑
os.environ['ISAAC_GR00T_MODEL_PATH'] = os.environ['EXTERNAL_STORAGE_PATH'] + "/GR00T-N1.5-3B"
path = os.environ['ISAAC_GR00T_MODEL_PATH']

print(f"需要將模型下載到指定路徑")
print(f"ISAAC_GR00T_MODEL_PATH: {path}")
os.environ['ISAAC_GR00T_MODEL_POST_PATH'] = os.environ['EXTERNAL_STORAGE_PATH'] + "/checkpoint-40000"
path = os.environ['ISAAC_GR00T_MODEL_POST_PATH']
print(f"ISAAC_GR00T_MODEL_POST_PATH: {path}\n")
!wget https://pai-vision-data-sh.oss-cn-shanghai.aliyuncs.com/aigc-data/isaac/nb4/gr00t.tar -O /tmp/gr00t.tar
!tar -xf /tmp/gr00t.tar -C /tmp/ && rm /tmp/gr00t.tar && mv /tmp/gr00t $ISAAC_GR00T_PATH
path = os.environ['ISAAC_GR00T_PATH']
print(f"代碼已下載到ISAAC_GR00T_PATH: {path}")
import os
from pathlib import Path

local_dir = Path("/root/isaac_cache")  # 緩存目錄
external_dir = os.environ['EXTERNAL_STORAGE_PATH'] #持久化存儲目錄
print(f"下載模型到: {local_dir}")
if os.path.exists(local_dir):
    !rm -rf {local_dir}
local_dir.mkdir(parents=True, exist_ok=True)

print("開始下載模型...")
package = "GR00T-N1.5-3B.tar"
download_from_oss('aigc-data/isaac/nb4/', package, str(local_dir))
print("下載完成")
print("開始解壓模型...")
zip_file = os.path.join(local_dir, package)
print(zip_file)
!tar -xf {zip_file} -C {local_dir}
!rm {zip_file}
print("解壓完成")

print(f"開始移動到資產目錄: {external_dir}") # 持久化存儲目錄
path = os.environ['ISAAC_GR00T_MODEL_PATH']
if os.path.exists(path):
    !rm -rf {path}
!mv {local_dir}/* {external_dir} 

print(f"GR00T-N1.5-3B已下載到ISAAC_GR00T_MODEL_PATH: {path}")

2. 人工演示

在DSW WebTerminal中,運行以下命令啓動VNC:

/opt/TurboVNC/bin/vncserver :0 -geometry 3840x2160

在本地Terminal中執行以下命令連接DSW:

ssh -L 5900:127.0.0.1:5900 root@DSW公網IP地址 -p DSW公網端口

使用TigerVNC等VNC工具打開VNC窗口,並在VNC中執行以下命令啓動人工演示界面:

# 步驟a: 創建數據集文件夾
mkdir -p /mnt/data/isaac_tmp/nb4/datasets

# 步驟b: 使用選定的teleoperation設備收集數據
# 可用選項: spacemouse, keyboard
cd /workspace/RobotLearningLab && ./isaaclab.sh -p usecase/scripts/record_demos.py --task Isaac-Stack-Cube-Galbot-Left-Arm-RmpFlow-Rel-v0 --teleop_device keyboard --dataset_file /mnt/data/isaac_tmp/nb4/datasets/dataset.hdf5 --num_demos 10

在人工演示界面中,可以通過以下按鍵控制機器人夾爪的運動:

  • 重置所有命令: R
  • 切換夾爪(開/關): K
  • 沿x軸移動機械臂: W/S
  • 沿y軸移動機械臂: A/D
  • 沿z軸移動機械臂: Q/E
  • 沿x軸旋轉機械臂: Z/X
  • 沿y軸旋轉機械臂: T/G
  • 沿z軸旋轉機械臂: C/V

視頻演示>>
通過控制夾爪,將桌面上的立方體按照藍色(底部) -> 紅色(中間) -> 綠色(頂部)的順序疊放,大約需要10個成功的演示。以下是一個成功演示的示例:
視頻演示>>

3. 數據擴增

首先對採集得到的dataset.hdf5文件進行子任務標註:

# 創建數據集目錄
path = os.environ.get("ROBOT_LEARNING_LAB_PATH")

output_path_str = os.environ.get("EXTERNAL_STORAGE_PATH")+"/datasets"
output_path = Path(output_path_str)
output_path.mkdir(parents=True, exist_ok=True)

# 標註
annotate_command = f"""
cd {path} && \
./isaaclab.sh -p usecase/scripts/annotate_demos.py \
--task Isaac-Stack-Cube-Galbot-Left-Arm-RmpFlow-Abs-Mimic-v0 \
--device cuda \
--auto \
--input_file {output_path_str}/dataset.hdf5 \
--output_file {output_path_str}/dataset_annotate.hdf5 \
--headless
"""


print("標註命令:")
print(annotate_command)

# 執行
!{annotate_command}

圖片

在DSW執行以下代碼啓動headless數據擴增過程:

# 創建數據集目錄
path = os.environ.get("ROBOT_LEARNING_LAB_PATH")

output_path_str = os.environ.get("EXTERNAL_STORAGE_PATH")+"/datasets"
output_path = Path(output_path_str)
output_path.mkdir(parents=True, exist_ok=True)

# 擴增
generate_command = f"""
cd {path} && \
./isaaclab.sh -p usecase/scripts/generate_dataset.py \
--task Isaac-Stack-Cube-Galbot-Left-Arm-RmpFlow-Abs-Mimic-v0 \
--device cuda \
--num_envs 10 \
--generation_num_trials 10000 \
--input_file {output_path_str}/dataset_annotate.hdf5 \
--output_file {output_path_str}/dataset_generate.hdf5 \
--headless
"""

print("擴增命令:")
print(generate_command)

# 執行
!{generate_command}

其中:

  • --num_envs 10 代表同一時刻並行擴增10份數據
  • --generation_num_trials 10000 代表目標生成10000份成功操作的數據
  • --input_file {output_path_str}/dataset_annotate.hdf5 代表輸入的數據,即上述步驟中經過子任務標註的數據文件
  • --output_file {output_path_str}/dataset_generate.hdf5 代表輸出的數據文件存放位置
  • --device cuda 代表使用GPU卡進行生成加速
  • --headless 代表以無GUI的方式進行後台數據合成

運行過程中會產生如下日誌:
圖片

其中類似84/144 (58.3%)的數據分別代表成功生成的數據條數、總共嘗試的數據合成次數,以及數據合成的成功率。

3.1 在DLC中執行分佈式數據擴增

可以看到,在DSW使用單機資源進行數據擴增,任務執行較慢。但其實每份數據的合成過程相互獨立,可以使用數據並行(DP)的方式進行分佈式處理:

在DLC中創建任務,環境配置如下:
圖片

鏡像地址:dsw-registry-vpc.cn-beijing.cr.aliyuncs.com/pai-training-algorithm/isaac-sim:isaaclab220-nb4-v7-20250916

自定義數據集:掛載DSW中相同的自定義數據集,以確保可以讀取到annotated_dataset.hdf5

啓動命令:

/workspace/RobotLearningLab/isaaclab.sh -p /mnt/data/isaac_tmp/nb4/datasets/ray_isaac_new.py
  --command "cd /workspace/RobotLearningLab &&         
  ./isaaclab.sh -p 
      /mnt/data/isaac_tmp/nb4/datasets/generate_dataset_ray.py         --task Isaac-Stack-Cube-Galbot-Left-Arm-RmpFlow-Abs-Mimic-v0        --device cuda         
   --num_envs 10         
   --generation_num_trials 625         
   --input_file 
     /mnt/data/isaac_tmp/nb4/datasets/dataset_annotate.hdf5         
   --output_file 
     /mnt/data/isaac_tmp/nb4/datasets/dataset_generate.hdf5         
   --headless"         
   --gpu 1         
   --cpu 10         
   --memory 80         
   --num_per_worker 8

其中:

  • --num_per_worker 8 代表一個節點運行8個數據合成任務
  • --gpu 1 --cpu 10 --memory 80 代表每個任務使用1張GPU、10個CPU核心、80GB內存
  • --generation_num_trials 625 代表每個數據合成任務目標生成625份數據

資源配置如下:
圖片

其中使用了Ray類型任務,以實現任務分佈式執行
設置Ray的Head節點數為1,CPU核數為8,內存數為32GB
設置Ray的Worker節點數為2,每個Worker的GPU卡數為8,CPU核數為90,內存數為700

綜上所述,我們總共將啓動16個數據合成任務,每個任務:

  • CPU核數:10
  • 內存:80GB
  • GPU:1張
  • 目標數據合成數量:625條

這樣總共使用2台8卡的ecs.ebmgn8te.32xlarge資源即可運行。
任務啓動後,可以通過DLC界面右上角的Dashboard打開Ray控制枱,查看任務運行的詳細信息:
圖片

可以看到,16個數據合成任務已經成功啓動並運行:
圖片

4. 數據處理

在完成數據擴增之後,需要經過合併、重放並轉換為Lerobot格式才能用户GR00T N1.5模型的模仿學習。

數據合併:

# 創建數據集目錄
path = os.environ.get("ROBOT_LEARNING_LAB_PATH")

output_path_str = os.environ.get("EXTERNAL_STORAGE_PATH")+"/datasets"
output_path = Path(output_path_str)
output_path.mkdir(parents=True, exist_ok=True)

# 合併DLC生成的Mimic分片文件
def merge_datasets(input_files, output_file="merged_dataset.hdf5"):
    """
    合併多個HDF5數據集
    """
    cmd = f"{path}/isaaclab.sh -p {path}/scripts/tools/merge_hdf5_datasets.py \
    --input_files {' '.join(input_files)} \
    --output_file {output_file}"
    
    print(f"合併數據集: {cmd}")
    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    print(result.stdout)
    
    return output_file

dataset_path = output_path
base_name = "dataset_generate"

success_files = list(dataset_path.glob(f"{base_name}_*.hdf5"))
success_files = [f for f in success_files if "_failed" not in f.name]
 
failed_files = list(dataset_path.glob(f"{base_name}_*_failed.hdf5"))

if success_files:
    print(f"找到 {len(success_files)} 個成功分片文件,開始合併...")
    
    success_input_files = [str(f) for f in success_files]
    success_output = dataset_path / f"{base_name}.hdf5"
    
    merged_dataset = merge_datasets(success_input_files, str(success_output))
    if os.path.exists(success_output):
        print(f"成功文件合併完成: {merged_dataset}")
        for f in success_files:
            f.unlink() 
        
        mimic_dataset_path = success_output
    else:
        print(f"合併失敗或未找到輸出文件 {success_output}。")
    
if failed_files:
    print(f"找到 {len(failed_files)} 個失敗分片文件,開始合併...")
    
    failed_input_files = [str(f) for f in failed_files]
    failed_output = dataset_path / f"{base_name}_failed.hdf5"

    failed_merged_dataset = merge_datasets(failed_input_files, str(failed_output))
    if os.path.exists(failed_output):
        print(f"失敗文件合併完成: {failed_merged_dataset}")
        for f in failed_files:
            f.unlink() 

    else:
        print(f"合併失敗或未找到輸出文件 {failed_output}。")

視頻重放:

# 創建數據集目錄
path = os.environ.get("ROBOT_LEARNING_LAB_PATH")

output_path_str = os.environ.get("EXTERNAL_STORAGE_PATH")+"/datasets"
output_path = Path(output_path_str)
output_path.mkdir(parents=True, exist_ok=True)

# 重放演示命令
command = f"""
cd {path} && \
./isaaclab.sh -p usecase/scripts/replay_demos_with_camera.py \
--task Isaac-Stack-Cube-Galbot-Left-Arm-Image-Based-v0 \
--dataset_file {output_path_str}/dataset_generate.hdf5 \
--num_envs 10 \
--video \
--video_path {output_path_str} \
--camera_view_list ego left_wrist right_wrist \
--headless
"""

print("需要先重播軌跡以生成相應的視頻數據,用於視覺模態的輸入。")
print("生成後的數據會保存在Isaac-Stack-Cube-Galbot-Left-Arm-Image-Based-v0/videos中。")
print(command)

# 執行
!{command}

轉換為Lerobot格式:

# 創建數據集目錄
path = os.environ.get("ROBOT_LEARNING_LAB_PATH")

output_path_str = os.environ.get("EXTERNAL_STORAGE_PATH")+"/datasets"
output_path = Path(output_path_str)
output_path.mkdir(parents=True, exist_ok=True)

# 數據格式轉換命令
command = f"""
cd {path} && \
./isaaclab.sh -p benchmarks/gr00t/convert_hdf5_to_lerobot_joint_space.py \
--data_root {output_path} \
--hdf5_filename dataset_generate.hdf5 \
--hdf5_file_path {output_path}/dataset_generate.hdf5 \
--lerobot_data_dir {output_path}/lerobot_joint_space
"""

print("需要再將所有模態的數據轉成GR00T支持的 LeRobot 格式。")
print(command)

# 執行
!{command}

以下為一段頭部與腕部合成視頻的示例:
頭部:視頻演示>>
腕部:視頻演示>>

5. 模仿學習

我們可以直接在DLC中啓動分佈式模仿學習任務:
圖片

環境配置:
鏡像地址:使用以下預置的鏡像,包含了GR00T N1.5模型訓練所需的環境

dsw-registry-vpc.cn-beijing.cr.aliyuncs.com/pai-training-algorithm/isaac-sim:gr00t-nb4-v1-20250916

數據集掛載:確保掛載了DSW中使用的自定義數據集,以及RobotLearningLab公共數據集

啓動命令:

cd /mnt/data/isaac_tmp/nb4/Isaac-GR00T &&             
    export WANDB_MODE=offline &&             
    NCCL_P2P_DISABLE=1 NCCL_IB_DISABLE=1 PYTHONPATH='./' 
    python scripts/gr00t_finetune.py             
    --base_model_path /mnt/data/isaac_tmp/nb4/GR00T-N1.5-3B             --dataset-path 
        /mnt/data/isaac_tmp/nb4/datasets/lerobot_joint_space            --num-gpus 2             
    --batch-size 2             
    --output-dir /mnt/data/isaac_tmp/nb4/datasets/joint_space_2_2       --max-steps 40000             
    --data-config galbot_joint_space             
    --video-backend decord             
    --no-tune-visual &&             
    sleep 30

其中:

  • --base_model_path /mnt/data/isaac_tmp/nb4/GR00T-N1.5-3B 代表使用GR00T-N1.5-3B模型作為基模進行模仿學習
  • --dataset-path /mnt/data/isaac_tmp/nb4/datasets/lerobot_joint_space 代表使用上述過程中擴增的數據作為訓練集
  • --num-gpus 2 代表使用2張GPU作為訓練資源,這裏可以根據自己的資源配額餘量自行調節
  • export WANDB_MODE=offline && NCCL_P2P_DISABLE=1 NCCL_IB_DISABLE=1 這裏關閉了在線WandB和卡間互聯,這裏也可以根據實際環境和需求調整
    圖片

觀察任務日誌,等待訓練完成:
圖片

6. 閉環評估

待模仿學習過程完成後,我們可以組合使用2個DSW實例組合進行模型的閉環評估。

評估過程使用一個新的DSW作為服務端運行微調後的GR00T-N1.5模型,前面運行Isaac Lab的DSW作為客户端運行機器人本體,連接服務端指揮其動作。
圖片

6.1 啓動服務端DSW

啓動一個新的DSW實例,其中:

  • 使用GR00T模型服務鏡像

    dsw-registry-vpc.${regionId}.cr.aliyuncs.com/pai-training-algorithm/isaac-sim:gr00t-nb4-v1-20250916

    作為啓動鏡像

  • 確保掛載RobotLearningLab_Dataset和保存微調後模型的自定義數據集
    圖片
  • 確保服務端DSW與客户端DSW位於同一個VPC內
    啓動後,在服務端DSW中執行以下代碼獲取服務端IP:
PRI_IP=$(ifconfig eth1 | grep 'inet ' | awk '{print $2}') && echo "我的私網IP是: $PRI_IP"

此處以獲取的服務端IP為10.0.0.207為例。
在服務端DSW中啓動GR00T-N1.5模型服務:

cd /mnt/data/isaac_tmp/nb4/Isaac-GR00T && 
python gr00t_inference_server.py --port 5555   --model_path /mnt/data/isaac_tmp/nb4/checkpoint-40000   --data_config galbot_joint_space

成功啓動後可以觀察到如下日誌:
圖片

6.2 啓動客户端DSW

可以複用數據擴增的DSW作為客户端DSW。

在客户端DSW的VNC桌面中,啓動Isaac Lab環境,同時連接到服務端DSW的模型服務:

cd /workspace/RobotLearningLab && ./isaaclab.sh -p benchmarks/gr00t/gr00t_inference_client.py --server_port 5555 --server_host 10.0.0.207 --num_total_experiments 100 --num_success_steps 8 --policy_type joint_space --task Isaac-Stack-Cube-Galbot-Left-Arm-Joint-Position-Image-Based-v0

請注意將其中的10.0.0.207替換為服務端DSW的實際IP地址。

在啓動的Isaac Lab窗口中,可以看到機器人本體在模型的指揮下,開始執行疊方塊的動作。
視頻演示>>

兩台DSW組合運行的整體效果如下:
視頻演示>>

7. 總結

本最佳實踐綜合使用PAI-DSW、DLC等模塊,針對GR00T-N1.5-3B模型,進行了人工演示、數據擴增、模仿學習以及在環驗證。相對於本系列的前3個最佳實踐,本例可重點學習以下能力:

  • 使用RobotLearningLab公共數據集
  • 使用DLC進行分佈式大規模數據擴增
  • 使用DLC,針對較複雜的VLA模型進行分佈式模仿學習
  • 組合使用兩台DSW進行服務端-客户端架構的軟件在環驗證

經過實際操作可以驗證,經過模仿學習的GR00T-N1.5-3B模型,可以很好的模仿人類的“疊方塊”動作,實現新技能的學習。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.