在前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中,我們已經預置了一個最佳實踐,就是這個過程的一個具體示例:
https://gallery.pai-ml.com/#/preview/deepLearning/cv/isaac_lab_wf4
下面我們來詳細解讀這個示例。
1. 環境準備
由於需要交互式環境進行人工演示,我們需要啓動DSW。
以北京Region為例,我們可以基於以下鏡像啓動DSW,其中已經包含了Isaac Lab 2.2及其依賴環境:
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模型,可以很好的模仿人類的“疊方塊”動作,實現新技能的學習。