OpenArm Simulation & Data Collection: Từ Isaac Lab đến SimpleVLA-RL
Ở bài trước, chúng ta đã deploy SimpleVLA-RL lên OpenArm vật lý. Nhưng nếu bạn chưa có robot — hoặc muốn thử nghiệm nhanh hơn trước khi mua phần cứng $6,500 — thì simulation là con đường tối ưu nhất. Bài này sẽ hướng dẫn bạn từng bước: cài đặt Isaac Lab, chạy OpenArm trong môi trường ảo, thu thập demonstration data, và convert sang format mà SimpleVLA-RL (veRL + OpenVLA-OFT stack) có thể sử dụng.
Đây là bài thuần SimpleVLA-RL — không có LeRobot ở đây. Toàn bộ pipeline từ data collection đến training đều đi qua OpenVLA-OFT format và veRL framework.
Tại sao simulation trước?
Trước khi bạn bỏ ra hàng nghìn đô la cho phần cứng, có 4 lý do cực kỳ thuyết phục để bắt đầu trong simulation:
1. An toàn tuyệt đối — Robot arm 7-DoF có thể gây hư hỏng nghiêm trọng nếu policy sai. Trong simulation, bạn có thể chạy hàng nghìn episodes mà không lo robot đâm vào bàn, rơi vật thể, hay hỏng servo.
2. Tốc độ vượt trội — Isaac Lab chạy trên GPU có thể simulate hàng trăm environments song song. Thu thập 1,000 demonstrations trong simulation mất vài giờ, trong khi real robot cần vài tuần.
3. Chi phí bằng 0 — Bạn chỉ cần một NVIDIA GPU (8GB+ VRAM cho simulation, 24GB+ cho VLA training sau này). Không cần mua robot $6,500, không cần workspace, không cần camera setup.
4. Scalable hoàn toàn — Muốn thử task mới? Thay đổi reward function và chạy lại. Muốn nhiều data hơn? Tăng số parallel environments. Không có bottleneck vật lý nào.
Pipeline tổng quan:
Isaac Lab (OpenArm sim) → Train RL expert → Collect demonstrations
→ Convert to OpenVLA-OFT format → SFT training → RL fine-tuning
→ Sim-to-real transfer → Physical OpenArm
Step 1: Cài đặt Isaac Lab + OpenArm Simulation
Yêu cầu hệ thống
- OS: Ubuntu 22.04 (bắt buộc)
- GPU: NVIDIA với driver 535+ và CUDA 12.x
- Isaac Sim: v5.1.0
- Isaac Lab: v2.3.0
- Python: 3.11
- VRAM: 8GB+ cho simulation, 24GB+ cho VLA training
Phương pháp 1: Docker (Khuyến nghị)
Docker là cách nhanh nhất và ít rủi ro nhất. NVIDIA cung cấp container đã cài sẵn mọi thứ:
# Pull Isaac Lab container
docker pull nvcr.io/nvidia/isaac-lab:2.3.0
# Chạy container với GPU access
docker run --gpus all -it --rm \
--network host \
-v ~/openarm_data:/workspace/data \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
nvcr.io/nvidia/isaac-lab:2.3.0 bash
Giải thích các flags quan trọng:
--gpus all: Mount tất cả GPU vào container-v ~/openarm_data:/workspace/data: Mount thư mục data để lưu demonstrations ra ngoài container-e DISPLAYvà-v /tmp/.X11-unix: Cho phép render GUI (cần khi muốn xem simulation)
Phương pháp 2: Cài đặt local với Conda
Nếu bạn muốn kiểm soát nhiều hơn hoặc cần debug sâu:
# Tạo conda environment
conda create -n isaaclab python=3.11 -y
conda activate isaaclab
# Cài Isaac Sim (theo hướng dẫn NVIDIA)
pip install isaacsim==5.1.0 --extra-index-url https://pypi.nvidia.com
# Clone và cài Isaac Lab
git clone https://github.com/isaac-sim/IsaacLab.git
cd IsaacLab
pip install -e .
Cài đặt OpenArm cho Isaac Lab
Đây là bước quan trọng nhất — clone repo openarm_isaac_lab từ Enactic:
# Clone OpenArm Isaac Lab package
git clone https://github.com/enactic/openarm_isaac_lab.git
cd openarm_isaac_lab
# Cài đặt OpenArm package
pip install -e source/openarm
Package này đăng ký các OpenArm environments vào Isaac Lab registry. Sau khi cài xong, verify bằng cách liệt kê tất cả environments:
# Verify OpenArm environments đã được đăng ký
python ./scripts/tools/list_envs.py
Bạn sẽ thấy các environments sau trong output:
Isaac-Reach-OpenArm-v0— Reach một điểm trong không gianIsaac-Lift-Cube-OpenArm-v0— Nâng khối cube lên khỏi mặt bànIsaac-Open-Drawer-OpenArm-v0— Mở ngăn kéoIsaac-Reach-OpenArm-Bi-v0— Reach với dual-arm (bimanual)
Step 2: Khám phá các Task có sẵn
Chọn task phù hợp
Trong 4 tasks có sẵn, Isaac-Lift-Cube-OpenArm-v0 là lựa chọn tốt nhất để bắt đầu vì:
- Gần nhất với task "gắp hộp" trong thực tế
- Đủ phức tạp để tạo demonstrations có giá trị (approach + grasp + lift)
- Có reward function rõ ràng (cube height > threshold = success)
- Community đã có nhiều baseline để so sánh
Tuy nhiên, nếu bạn mới hoàn toàn, hãy bắt đầu với Isaac-Reach-OpenArm-v0 trước. Task reach đơn giản hơn nhiều (chỉ cần di chuyển end-effector đến target position), training nhanh hơn, và giúp bạn verify setup hoạt động đúng trước khi chuyển sang task khó hơn.
Xem task hoạt động
Chạy play script để visualize task:
# Xem Isaac-Lift-Cube trong simulation
python ./scripts/reinforcement_learning/rsl_rl/play.py \
--task Isaac-Lift-Cube-OpenArm-v0
# Hoặc thử reach trước (đơn giản hơn)
python ./scripts/reinforcement_learning/rsl_rl/play.py \
--task Isaac-Reach-OpenArm-v0
Khi chạy play.py, bạn sẽ thấy cửa sổ Isaac Sim với OpenArm robot trên bàn và một cube đỏ. Robot sẽ thực hiện random actions — đây là để bạn hiểu observation space và action space trước khi training.
Hiểu Action Space và Observation Space
OpenArm là robot 7-DoF (7 joints) + 1 gripper, tổng cộng 8-DoF action space:
| Joint | Mô tả | Range |
|---|---|---|
| Joint 1 | Base rotation | -180° đến 180° |
| Joint 2 | Shoulder | -90° đến 90° |
| Joint 3 | Elbow | -180° đến 180° |
| Joint 4 | Wrist 1 | -180° đến 180° |
| Joint 5 | Wrist 2 | -180° đến 180° |
| Joint 6 | Wrist 3 | -180° đến 180° |
| Joint 7 | Wrist rotation | -180° đến 180° |
| Gripper | Open/close | 0 (đóng) đến 1 (mở) |
Observation space bao gồm:
- Joint positions (8 giá trị)
- Joint velocities (8 giá trị)
- End-effector position (3 giá trị: x, y, z)
- End-effector orientation (4 giá trị: quaternion)
- Object position (3 giá trị — vị trí cube)
- Object orientation (4 giá trị)
- Goal position (3 giá trị — target height cho lift task)
Lưu ý quan trọng: OpenVLA-OFT mặc định xử lý 7-DoF (6 joints + gripper). OpenArm có 8-DoF nên bạn sẽ cần adapt action dimension — chi tiết ở Step 5.
Step 3: Train RL Policy — Tạo "Expert" cho Demonstrations
Mục tiêu của bước này không phải deploy RL policy lên robot thật. Mục tiêu là tạo một expert policy đủ giỏi để thu thập demonstrations chất lượng cao. Những demonstrations này sẽ được dùng để SFT (Supervised Fine-Tuning) cho OpenVLA-OFT.
Chạy training
# Train RL policy với rsl_rl
python ./scripts/reinforcement_learning/rsl_rl/train.py \
--task Isaac-Lift-Cube-OpenArm-v0 \
--headless \
--num_envs 256
# Nếu GPU nhỏ (8GB), giảm num_envs
python ./scripts/reinforcement_learning/rsl_rl/train.py \
--task Isaac-Lift-Cube-OpenArm-v0 \
--headless \
--num_envs 64
Flag --headless tắt rendering để tăng tốc training. --num_envs là số environments chạy song song — đây là sức mạnh của Isaac Lab: 256 environments chạy đồng thời trên GPU, thu thập experience nhanh hơn real-time hàng trăm lần.
OpenArm Isaac Lab hỗ trợ 3 RL frameworks:
- rsl_rl — Nhẹ, nhanh, phù hợp locomotion và manipulation đơn giản
- rl_games — NVIDIA's framework, tối ưu cho Isaac Lab
- skrl — Flexible, dễ customize, hỗ trợ nhiều algorithms
Đối với task Lift Cube, rsl_rl hoặc rl_games đều cho kết quả tốt. Training thường converge sau 500-1000 iterations (khoảng 30 phút đến 2 giờ tùy GPU).
Monitor training với TensorBoard
# Mở TensorBoard trong terminal khác
tensorboard --logdir logs/rsl_rl/Isaac-Lift-Cube-OpenArm-v0
# Hoặc nếu dùng rl_games
tensorboard --logdir logs/rl_games/Isaac-Lift-Cube-OpenArm-v0
Các metrics cần theo dõi:
- Episode reward: Tăng dần và ổn định → policy đang học
- Success rate: Phần trăm episodes mà cube được nâng lên đúng vị trí
- Episode length: Giảm dần → policy tìm được cách giải task nhanh hơn
Khi nào dừng training? Khi success rate đạt >90% ổn định qua 50+ iterations. Bạn không cần policy hoàn hảo — chỉ cần đủ tốt để tạo demonstrations chất lượng. Một policy 95% success rate tạo ra demonstrations tốt hơn nhiều so với teleoperation thủ công.
Verify policy đã train
# Chạy policy đã train để xem kết quả
python ./scripts/reinforcement_learning/rsl_rl/play.py \
--task Isaac-Lift-Cube-OpenArm-v0 \
--checkpoint logs/rsl_rl/Isaac-Lift-Cube-OpenArm-v0/best_model.pt
Bạn sẽ thấy OpenArm tiếp cận cube, gắp, và nâng lên một cách mượt mà. Nếu policy còn lỗi (thả cube, tiếp cận sai hướng), tiếp tục training hoặc tune reward function.
Step 4: Thu thập Demonstrations từ Expert Policy
Đây là bước chuyển đổi từ RL sang VLA pipeline. Thay vì dùng RL policy trực tiếp, chúng ta sẽ dùng nó như một "expert teacher" để tạo ra demonstrations cho SFT.
Tại sao không dùng RL policy trực tiếp?
RL policy hoạt động tốt trong simulation nhưng có nhiều hạn chế khi chuyển sang real:
- Observation gap: RL policy nhận state vector (joint positions, object positions) — real robot chỉ có camera images
- Không generalize: RL policy chỉ biết 1 task cụ thể, không hiểu language instructions
- Brittle: Thay đổi nhỏ trong environment (ánh sáng, vị trí cube) có thể khiến policy fail
SimpleVLA-RL giải quyết tất cả bằng cách: dùng VLA model (OpenVLA-OFT) nhận camera images + language instructions → output actions. Nhưng VLA cần demonstrations để SFT trước.
Setup camera trong simulation
Isaac Lab hỗ trợ thêm camera vào robot hoặc environment. Bạn cần ít nhất 1 camera để capture RGB images cho VLA:
# Thêm camera config vào environment
# File: source/openarm/openarm/tasks/manipulation/lift/lift_env_cfg.py
from isaaclab.sensors import CameraCfg
# Camera gắn trên "head" của robot (hoặc fixed position)
camera_cfg = CameraCfg(
prim_path="/World/envs/env_.*/Robot/camera_link",
update_period=0.1, # 10 Hz
height=256,
width=256,
data_types=["rgb"],
spawn=None, # Camera đã có trong URDF/USD
)
Lưu ý về resolution: VLA models thường hoạt động tốt với 224x224 hoặc 256x256. Resolution cao hơn (640x480) không nhất thiết tốt hơn mà tăng VRAM consumption đáng kể trong training.
Nếu OpenArm URDF/USD chưa có camera link, bạn có thể thêm camera ở vị trí cố định:
# Camera cố định nhìn xuống workspace
fixed_camera_cfg = CameraCfg(
prim_path="/World/envs/env_.*/Camera",
offset=CameraCfg.OffsetCfg(
pos=(0.5, 0.0, 0.8), # Trước mặt robot, nhìn xuống
rot=(0.7071, 0.0, 0.7071, 0.0), # Xoay 90° nhìn xuống
),
update_period=0.1,
height=256,
width=256,
data_types=["rgb"],
)
Chạy expert policy và record demonstrations
import torch
import numpy as np
from pathlib import Path
import json
def collect_demonstrations(
env,
policy,
num_episodes=1000,
save_dir="./openarm_demos"
):
"""
Thu thập demonstrations từ RL expert policy.
Mỗi episode = sequence of (image, state, action, language).
"""
save_path = Path(save_dir)
save_path.mkdir(parents=True, exist_ok=True)
success_count = 0
episode_id = 0
for ep in range(num_episodes):
obs = env.reset()
episode_data = {
"images": [],
"states": [],
"actions": [],
"language": "pick up the red cube and lift it",
"success": False
}
done = False
step = 0
while not done and step < 200: # Max 200 steps per episode
# Get action from expert policy
with torch.no_grad():
action = policy(obs)
# Capture camera image
camera_data = env.scene["camera"].data
rgb_image = camera_data.output["rgb"][0].cpu().numpy()
# Record step data
episode_data["images"].append(rgb_image)
episode_data["states"].append(
obs["joint_pos"][0].cpu().numpy().tolist()
)
episode_data["actions"].append(
action[0].cpu().numpy().tolist()
)
# Step environment
obs, reward, done, info = env.step(action)
step += 1
# Check if episode was successful
if info.get("success", False):
episode_data["success"] = True
success_count += 1
# Chỉ lưu episodes thành công
ep_dir = save_path / f"episode_{episode_id:05d}"
ep_dir.mkdir(exist_ok=True)
# Lưu images
for i, img in enumerate(episode_data["images"]):
np.save(ep_dir / f"image_{i:04d}.npy", img)
# Lưu metadata
meta = {
"language": episode_data["language"],
"states": episode_data["states"],
"actions": episode_data["actions"],
"num_steps": len(episode_data["actions"]),
"success": True
}
with open(ep_dir / "metadata.json", "w") as f:
json.dump(meta, f)
episode_id += 1
if (ep + 1) % 100 == 0:
print(f"Collected {ep+1}/{num_episodes} episodes, "
f"success: {success_count}/{ep+1} "
f"({success_count/(ep+1)*100:.1f}%)")
print(f"\nDone! {success_count} successful episodes saved to {save_dir}")
return success_count
Số lượng demonstrations cần thiết:
- Minimum: 200-300 episodes cho SFT cơ bản
- Recommended: 500-1000 episodes cho kết quả tốt
- Optimal: 1000-2000 episodes nếu bạn có thời gian
Với expert policy 95% success rate và 256 parallel environments, thu thập 1000 episodes thành công mất khoảng 15-30 phút trên RTX 3090.
Thu thập feasible seeds
Tương tự script pre_collect_robotwin2_seed.sh trong SimpleVLA-RL, bạn cần xác định những initial configurations mà expert policy có thể giải quyết thành công:
# Chạy expert policy trên nhiều random seeds
# Lưu lại seeds thành công để dùng cho RL training sau này
python collect_feasible_seeds.py \
--task Isaac-Lift-Cube-OpenArm-v0 \
--checkpoint logs/rsl_rl/best_model.pt \
--num_seeds 5000 \
--output_file feasible_seeds.json
Feasible seeds quan trọng cho RL training phase sau (Step 5 trong bài tổng quan): bạn chỉ muốn RL fine-tune trên các configurations mà task có thể hoàn thành, tránh wasting compute trên impossible scenarios.
Step 5: Convert Data sang OpenVLA-OFT Format
Đây là bước kỹ thuật nhất — và cũng là nơi OpenArm khác biệt so với các robot tiêu chuẩn.
Vấn đề Action Dimension
OpenVLA-OFT mặc định xử lý 7-DoF: 6 joints + 1 gripper. Nhưng OpenArm có 8-DoF: 7 joints + 1 gripper. Bạn cần xử lý sự khác biệt này.
Có 3 cách tiếp cận:
Cách 1: Pad action dimension (Khuyến nghị)
Giữ nguyên 8-DoF và pad thêm zeros cho các action dimensions không dùng trong OpenVLA-OFT:
# Trong rob_dataset.py, khi register OpenArm environment
OPENARM_ACTION_DIM = 8 # 7 joints + 1 gripper
def pad_action_to_openvla(action_8dof):
"""
OpenVLA-OFT expects fixed action dimension.
Pad hoặc truncate để match.
"""
# Option A: Sử dụng 8-DoF trực tiếp
# Cần sửa model config để accept 8-DoF
return action_8dof
Cách 2: Map 8-DoF sang 7-DoF
Nếu một trong 7 joints của OpenArm ít quan trọng (ví dụ: wrist rotation cuối), bạn có thể merge hoặc bỏ:
def map_8dof_to_7dof(action_8dof):
"""
Map 8-DoF OpenArm actions to 7-DoF OpenVLA-OFT format.
Bỏ joint 7 (wrist rotation) vì ít ảnh hưởng đến grasping.
"""
# joints[0:6] = 6 primary joints
# joints[6] = wrist rotation (bỏ)
# joints[7] = gripper
return np.concatenate([
action_8dof[:6], # 6 primary joints
action_8dof[7:8] # gripper
])
Cách 3: Sửa OpenVLA-OFT model
Thay đổi action head trong model để output 8 dimensions. Đây là cách linh hoạt nhất nhưng cần sửa code:
# Trong OpenVLA-OFT config
action_dim = 8 # Thay vì 7 mặc định
Mình khuyến nghị Cách 1 hoặc Cách 3 vì không mất thông tin. Cách 2 đơn giản nhưng có thể ảnh hưởng đến precision khi grasping.
Register OpenArm Environment trong rob_dataset.py
Để SimpleVLA-RL nhận diện OpenArm environment, bạn cần đăng ký nó trong rob_dataset.py:
# Trong simplevla-rl/rob_dataset.py
# Thêm OpenArm environment configuration
OPENARM_LIFT_CONFIG = {
"env_name": "Isaac-Lift-Cube-OpenArm-v0",
"action_dim": 8, # 7 joints + 1 gripper
"image_size": (256, 256),
"max_episode_steps": 200,
"language_instruction": "pick up the red cube and lift it",
"action_scale": 1.0,
"camera_names": ["camera_0"],
}
# Register environment
ENV_CONFIGS["openarm_lift_cube"] = OPENARM_LIFT_CONFIG
Thêm max steps trong rob_rollout.py
# Trong simplevla-rl/rob_rollout.py
# Thêm OpenArm max steps
MAX_STEPS = {
# ... existing environments ...
"openarm_lift_cube": 200,
"openarm_reach": 100,
"openarm_drawer": 250,
}
Convert demonstration data sang OpenVLA-OFT format
OpenVLA-OFT yêu cầu mỗi demonstration episode có format:
episode/
├── image_0000.png # RGB frame tại timestep 0
├── image_0001.png # RGB frame tại timestep 1
├── ...
└── trajectory.json # Actions + language instruction
Script convert:
import json
import numpy as np
from PIL import Image
from pathlib import Path
def convert_to_openvla_format(
raw_demo_dir: str,
output_dir: str,
action_mapping: str = "pad" # "pad", "map7dof", or "direct8dof"
):
"""
Convert OpenArm demonstrations sang OpenVLA-OFT format.
Parameters:
- raw_demo_dir: Thư mục chứa raw demonstrations từ Step 4
- output_dir: Thư mục output cho OpenVLA-OFT
- action_mapping: Cách xử lý 8-DoF → 7-DoF
"""
raw_path = Path(raw_demo_dir)
out_path = Path(output_dir)
out_path.mkdir(parents=True, exist_ok=True)
episodes = sorted(raw_path.glob("episode_*"))
print(f"Converting {len(episodes)} episodes...")
for ep_dir in episodes:
ep_name = ep_dir.name
ep_out = out_path / ep_name
ep_out.mkdir(exist_ok=True)
# Load metadata
with open(ep_dir / "metadata.json") as f:
meta = json.load(f)
# Convert images: .npy → .png
num_steps = meta["num_steps"]
for i in range(num_steps):
img_array = np.load(ep_dir / f"image_{i:04d}.npy")
img = Image.fromarray(img_array.astype(np.uint8))
img.save(ep_out / f"image_{i:04d}.png")
# Process actions
actions = np.array(meta["actions"]) # Shape: (T, 8)
if action_mapping == "map7dof":
# Map 8-DoF → 7-DoF (bỏ joint 7)
actions = np.concatenate([
actions[:, :6],
actions[:, 7:8]
], axis=1)
elif action_mapping == "pad":
# Giữ nguyên 8-DoF, pad nếu cần
pass
# Create trajectory.json cho OpenVLA-OFT
trajectory = {
"language_instruction": meta["language"],
"actions": actions.tolist(),
"states": meta["states"],
"num_steps": num_steps,
"env_name": "openarm_lift_cube",
"action_dim": actions.shape[1],
}
with open(ep_out / "trajectory.json", "w") as f:
json.dump(trajectory, f, indent=2)
print(f"Conversion complete! {len(episodes)} episodes saved to {output_dir}")
# Chạy convert
convert_to_openvla_format(
raw_demo_dir="./openarm_demos",
output_dir="./openarm_openvla_data",
action_mapping="direct8dof" # Hoặc "map7dof" nếu dùng 7-DoF
)
Configure Action Chunks
Action chunking là một kỹ thuật quan trọng trong SimpleVLA-RL. Thay vì predict 1 action tại mỗi timestep, model predict một chuỗi actions (action chunk). Điều này giúp giảm prediction frequency và tăng temporal consistency.
Cho OpenArm, action chunk size phụ thuộc vào task:
| Task | Horizon (steps) | Chunk Size khuyến nghị |
|---|---|---|
| Reach | 50-100 | 10-15 |
| Lift Cube | 100-200 | 15-20 |
| Open Drawer | 150-250 | 20-25 |
# Configure action chunks cho OpenArm
ACTION_CHUNK_CONFIG = {
"openarm_reach": {
"chunk_size": 10,
"overlap": 3, # Overlap giữa consecutive chunks
},
"openarm_lift_cube": {
"chunk_size": 15,
"overlap": 5,
},
"openarm_drawer": {
"chunk_size": 20,
"overlap": 7,
},
}
Step 6: Verify Data Quality
Trước khi chuyển sang SFT training, hãy verify data:
def verify_dataset(data_dir: str):
"""Kiểm tra dataset integrity."""
data_path = Path(data_dir)
episodes = sorted(data_path.glob("episode_*"))
print(f"Total episodes: {len(episodes)}")
action_dims = []
episode_lengths = []
errors = []
for ep_dir in episodes:
traj_file = ep_dir / "trajectory.json"
if not traj_file.exists():
errors.append(f"{ep_dir.name}: missing trajectory.json")
continue
with open(traj_file) as f:
traj = json.load(f)
num_steps = traj["num_steps"]
actions = np.array(traj["actions"])
# Check action dimension consistency
action_dims.append(actions.shape[1])
episode_lengths.append(num_steps)
# Check images exist
for i in range(num_steps):
img_file = ep_dir / f"image_{i:04d}.png"
if not img_file.exists():
errors.append(f"{ep_dir.name}: missing {img_file.name}")
print(f"Action dimensions: {set(action_dims)}")
print(f"Episode lengths: min={min(episode_lengths)}, "
f"max={max(episode_lengths)}, "
f"mean={np.mean(episode_lengths):.1f}")
print(f"Errors: {len(errors)}")
for err in errors[:10]:
print(f" - {err}")
verify_dataset("./openarm_openvla_data")
Output mong đợi:
Total episodes: 1000
Action dimensions: {8}
Episode lengths: min=45, max=198, mean=127.3
Errors: 0
Yêu cầu phần cứng tổng kết
| Bước | GPU VRAM | Thời gian ước tính |
|---|---|---|
| Isaac Lab simulation | 8GB+ | — |
| RL expert training | 8GB+ | 30 phút - 2 giờ |
| Collect 1000 demos | 8GB+ | 15-30 phút |
| SFT training (bài sau) | 24GB+ (A100/4090) | 4-8 giờ |
| RL fine-tuning (bài sau) | 24GB+ (A100/4090) | 8-16 giờ |
Tips và Pitfalls
1. Bắt đầu với Reach, không phải Lift — Isaac-Reach-OpenArm-v0 converge nhanh hơn nhiều (10-15 phút training). Dùng nó để verify toàn bộ pipeline hoạt động trước khi chuyển sang Lift Cube.
2. Camera placement matters — Vị trí camera ảnh hưởng lớn đến sim-to-real transfer. Đặt camera ở vị trí tương tự với setup thật (thường là third-person view từ phía trước, cao khoảng 50-80cm).
3. Domain randomization — Khi thu thập demonstrations, randomize lighting, texture, camera position nhẹ. Điều này giúp VLA model robust hơn khi transfer sang real:
# Randomize trong Isaac Lab
from isaaclab.envs import DirectRLEnvCfg
class LiftCubeRandomizedCfg(DirectRLEnvCfg):
# Randomize vị trí cube
cube_pos_noise = 0.05 # ±5cm
# Randomize lighting
light_intensity_range = (0.5, 1.5)
# Randomize camera
camera_pos_noise = 0.02 # ±2cm
4. Action scale consistency — Đảm bảo action scale giống nhau giữa simulation và real robot. Isaac Lab dùng radians, OpenArm SDK có thể dùng degrees — kiểm tra kỹ.
5. Lưu raw data — Luôn giữ raw demonstrations (images + actions) bên cạnh converted format. Nếu cần thay đổi format sau (ví dụ: từ 8-DoF sang 7-DoF), bạn không cần collect lại.
Bước tiếp theo
Bây giờ bạn đã có dataset OpenArm demonstrations trong format OpenVLA-OFT. Bước tiếp theo trong pipeline SimpleVLA-RL:
- SFT Training — Fine-tune OpenVLA-OFT trên demonstrations (chi tiết trong bài training)
- RL Fine-tuning — Dùng veRL framework với binary rewards trong simulation
- Sim-to-real transfer — Deploy lên OpenArm vật lý (xem bài results)
Toàn bộ pipeline không cần LeRobot — mọi thứ chạy qua veRL + OpenVLA-OFT stack. Đây là điểm khác biệt lớn nhất so với pipeline sử dụng LeRobot: bạn có full control over training loop và reward shaping, đổi lại bạn cần viết nhiều integration code hơn.