3.5 ManiSkill 강화학습 환경
ManiSkill은 GPU 가속 병렬 시뮬레이션을 제공하는 강화학습 전용 로봇 환경입니다.
목차
ManiSkill 소개
ManiSkill이란?
ManiSkill은 로봇 조작(manipulation) 강화학습을 위한 벤치마크 환경으로, SAPIEN 물리 엔진 기반의 GPU 병렬 시뮬레이션을 제공합니다.
┌─────────────────────────────────────┐
│ ManiSkill 아키텍처 │
├─────────────────────────────────────┤
│ │
│ ┌─────────────────────────────┐ │
│ │ Gymnasium API │ │
│ │ (RL 표준 인터페이스) │ │
│ └─────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────┐ │
│ │ SAPIEN (물리 엔진) │ │
│ │ - GPU 병렬화 │ │
│ │ - PhysX 기반 │ │
│ └─────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────┐ │
│ │ 로봇 에이전트 │ │
│ │ - XLeRobot │ │
│ │ - Fetch, Panda 등 │ │
│ └─────────────────────────────┘ │
│ │
└─────────────────────────────────────┘
주요 특징
- GPU 병렬 시뮬레이션: 수백 개 환경 동시 실행
- Gymnasium 호환: 표준 RL 인터페이스
- 벤치마크 태스크: PushCube, PickPlace, AssemblyTask 등
- 다양한 로봇: Fetch, Panda, XLeRobot 등
- 사실적인 환경: ReplicaCAD 씬 (실제 방 스캔)
MuJoCo vs ManiSkill 비교
| 항목 | MuJoCo | ManiSkill |
|---|---|---|
| 목적 | 제어 개발 | 강화학습 |
| 병렬화 | 1개 환경 | 수백 개 (GPU) |
| Gym 호환 | 수동 래핑 | 네이티브 |
| 태스크 | 직접 구현 | 사전 정의됨 |
| 학습 속도 | 느림 (CPU) | 빠름 (GPU) |
| 커스터마이징 | 쉬움 | 중간 |
환경 설정
시스템 요구사항
| 구성 요소 | 최소 | 권장 |
|---|---|---|
| OS | Ubuntu 20.04 | Ubuntu 22.04 |
| Python | 3.8 | 3.10+ |
| GPU | NVIDIA GTX 1060 (6GB) | RTX 3060 (12GB) |
| CUDA | 11.8+ | 12.0+ |
| RAM | 8GB | 16GB |
| 디스크 | 10GB | 20GB |
1단계: ManiSkill 설치
# XLeRobot ManiSkill 디렉토리로 이동
cd ~/XLeRobot/simulation/Maniskill/
# Python 가상 환경 생성
python3 -m venv .venv
source .venv/bin/activate
# ManiSkill 설치 (최신 버전)
pip install mani-skill
# 추가 의존성
pip install gymnasium
pip install sapien
pip install numpy
pip install torch # PyTorch (선택, RL 학습용)
설치 시간: 5-10분
2단계: XLeRobot 에이전트 등록 확인
# XLeRobot 에이전트 파일 확인
ls ~/XLeRobot/simulation/Maniskill/agents/xlerobot/
# 예상 출력:
# __init__.py
# xlerobot.py
agents/xlerobot/xlerobot.py 파일에 XLeRobot 에이전트 정의되어 있음:
xlerobot_single: 단일 팔 버전xlerobot_dual: 듀얼 팔 버전
3단계: 첫 실행 테스트
# ManiSkill 디렉토리에서
cd ~/XLeRobot/simulation/Maniskill/
# 가상 환경 활성화
source .venv/bin/activate
# 간단한 시뮬레이션 실행
python run_xlerobot_sim.py
예상 출력:
Starting XLeRobot simulation...
Environment created successfully!
Observation space: Dict(...)
Action space: Box(...)
Control mode: pd_joint_delta_pos
Reward mode: normalized_dense
Simulation is running. Close the viewer window to exit.
Keyboard Controls:
- W/S: Base Forward/Back
- A/D: Base Rotation
- Arrow keys: Arm control
- Space: Pause/Resume
3D 뷰어 창이 열리고 XLeRobot이 PushCube 태스크 환경에 있어야 합니다.
XLeRobot 에이전트
에이전트 종류
ManiSkill XLeRobot 구현에는 2가지 버전이 있습니다:
1. xlerobot_single (단일 팔)
robot_uids = "xlerobot_single"
구성:
- 옴니휠 베이스 (3자유도)
- 단일 SO-100 팔 (6자유도)
- 그리퍼 (1자유도)
- 총 10 DOF
용도: 단순한 pick-and-place 태스크
2. xlerobot_dual (듀얼 팔)
robot_uids = "xlerobot_dual"
구성:
- 옴니휠 베이스 (3자유도)
- 왼팔 SO-100 (6자유도)
- 오른팔 SO-100 (6자유도)
- 양손 그리퍼 (2자유도)
- 총 17 DOF
용도: 양손 협업 작업 (조립, 정리 등)
제어 모드
ManiSkill은 여러 제어 모드를 지원합니다:
| 모드 | 설명 | 입력 차원 | 난이도 |
|---|---|---|---|
pd_joint_delta_pos |
관절 증분 위치 | DOF 수 | ⭐⭐ 쉬움 |
pd_joint_pos |
관절 절대 위치 | DOF 수 | ⭐⭐ 쉬움 |
pd_joint_vel |
관절 속도 | DOF 수 | ⭐⭐⭐ 중간 |
pd_ee_delta_pose |
엔드이펙터 증분 포즈 | 7 (pos + quat) | ⭐⭐⭐⭐ 어려움 |
pd_ee_target_pose |
엔드이펙터 목표 포즈 | 7 | ⭐⭐⭐⭐ 어려움 |
추천: pd_joint_delta_pos (학습 초기)
관찰 모드
| 모드 | 설명 | 크기 | 용도 |
|---|---|---|---|
state |
로봇 상태 (관절 각도, 속도) | ~20-50 | 초기 학습 |
rgbd |
RGB-D 이미지 | (H, W, 4) | 비전 기반 정책 |
pointcloud |
포인트 클라우드 | (N, 3) | 3D 인식 |
sensor |
모든 센서 데이터 | 가변 | 복합 정책 |
추천: state (학습 초기)
키보드 제어 실습
ManiSkill에서 XLeRobot을 키보드로 제어하며 환경을 이해합니다.
실행 방법
cd ~/XLeRobot/simulation/Maniskill/
source .venv/bin/activate
# 키보드 제어 예제 실행 (단일 팔)
python examples/demo_ctrl_action_ee_keyboard_single.py \
-e "ReplicaCAD_SceneManipulation-v1" \
-r "xlerobot_single" \
--render-mode="human" \
--shader="rt-fast" \
-c "pd_joint_delta_pos"
파라미터 설명:
-e: 환경 ID (ReplicaCAD_SceneManipulation-v1)-r: 로봇 UID (xlerobot_single)--render-mode: 렌더링 모드 (human= GUI)--shader: 셰이더 (rt-fast= 빠른 렌더링)-c: 제어 모드 (pd_joint_delta_pos)
키보드 제어 키
베이스 제어
| 키 | 동작 |
|---|---|
W |
전진 |
S |
후진 |
A |
좌회전 |
D |
우회전 |
Q |
좌이동 |
E |
우이동 |
팔 제어 (화살표 키)
| 키 | 동작 |
|---|---|
↑ |
팔 위로 |
↓ |
팔 아래로 |
← |
팔 왼쪽으로 |
→ |
팔 오른쪽으로 |
[ |
팔 앞으로 |
] |
팔 뒤로 |
그리퍼
| 키 | 동작 |
|---|---|
G |
그리퍼 열기 |
H |
그리퍼 닫기 |
시스템
| 키 | 동작 |
|---|---|
Space |
일시정지/재개 |
R |
환경 리셋 |
ESC |
종료 |
실습: PushCube 태스크
목표: 큐브를 목표 위치로 밀기
절차:
- 프로그램 실행 (위 명령어)
- 뷰어 창에서 큐브와 목표 영역 확인
W/A/S/D로 베이스 이동- 화살표 키로 팔 위치 조정
- 큐브에 접근하여 밀기
- 목표 영역에 큐브가 들어가면 성공!
성공 기준:
- 큐브가 녹색 목표 영역 안에 위치
- Reward 값이 1.0에 가까워짐
강화학습 학습
ManiSkill의 진정한 강점은 GPU 병렬 시뮬레이션을 통한 빠른 RL 학습입니다.
1. Gymnasium 환경 생성
# simple_rl_example.py
import gymnasium as gym
import numpy as np
from mani_skill.envs.sapien_env import BaseEnv
# 병렬 환경 생성 (64개)
env_kwargs = dict(
obs_mode="state",
control_mode="pd_joint_delta_pos",
render_mode=None, # 학습 시에는 렌더링 OFF
robot_uids="xlerobot_single",
num_envs=64, # GPU 병렬화!
sim_backend="gpu", # GPU 시뮬레이션
)
env: BaseEnv = gym.make("PushCube-v1", **env_kwargs)
# 환경 리셋
obs, info = env.reset(seed=0)
print(f"Observation shape: {obs['agent']['qpos'].shape}") # (64, 10)
print(f"Action shape: {env.action_space.shape}") # (64, 10)
# 랜덤 액션 실행
for _ in range(100):
action = env.action_space.sample()
obs, reward, terminated, truncated, info = env.step(action)
print(f"Mean reward: {reward.mean():.3f}")
if terminated.any():
env.reset()
env.close()
실행:
python simple_rl_example.py
출력:
Observation shape: (64, 10) ← 64개 환경 병렬
Action shape: (64, 10)
Mean reward: -0.124
Mean reward: -0.089
...
2. PPO 학습 예제
Stable Baselines3를 사용한 PPO 학습:
# Stable Baselines3 설치
pip install stable-baselines3
# train_ppo.py
import gymnasium as gym
from stable_baselines3 import PPO
from stable_baselines3.common.vec_env import DummyVecEnv
# 환경 생성 함수
def make_env():
env = gym.make(
"PushCube-v1",
obs_mode="state",
control_mode="pd_joint_delta_pos",
robot_uids="xlerobot_single",
num_envs=1,
render_mode=None,
)
return env
# 벡터화된 환경 (8개 병렬)
env = DummyVecEnv([make_env for _ in range(8)])
# PPO 모델 생성
model = PPO(
"MultiInputPolicy",
env,
verbose=1,
learning_rate=3e-4,
n_steps=2048,
batch_size=64,
n_epochs=10,
gamma=0.99,
device="cuda", # GPU 사용
)
# 학습 시작 (100만 스텝)
model.learn(total_timesteps=1_000_000)
# 모델 저장
model.save("xlerobot_pushcube_ppo")
print("Training complete!")
실행:
python train_ppo.py
예상 학습 시간:
- CPU: 8-12시간
- GPU (RTX 3060): 2-3시간
3. 학습된 모델 평가
# eval_policy.py
import gymnasium as gym
from stable_baselines3 import PPO
# 모델 로드
model = PPO.load("xlerobot_pushcube_ppo")
# 환경 생성 (렌더링 ON)
env = gym.make(
"PushCube-v1",
obs_mode="state",
control_mode="pd_joint_delta_pos",
robot_uids="xlerobot_single",
num_envs=1,
render_mode="human",
)
# 평가 루프
obs, _ = env.reset()
for _ in range(1000):
action, _ = model.predict(obs, deterministic=True)
obs, reward, terminated, truncated, info = env.step(action)
env.render()
if terminated or truncated:
obs, _ = env.reset()
env.close()
실행:
python eval_policy.py
관찰 포인트:
- 로봇이 자동으로 큐브를 목표 위치로 이동
- 부드러운 움직임 (학습된 정책)
커스텀 태스크 생성
ManiSkill에서 자신만의 태스크를 만들 수 있습니다.
예제: PickAndPlace 태스크
# custom_task.py
import numpy as np
import sapien
from mani_skill.envs.sapien_env import BaseEnv
from mani_skill.utils import sapien_utils
from mani_skill.utils.registration import register_env
@register_env("XLeRobotPickPlace-v1", max_episode_steps=200)
class XLeRobotPickPlaceEnv(BaseEnv):
"""XLeRobot Pick and Place 태스크"""
SUPPORTED_ROBOTS = ["xlerobot_single"]
agent: "XLeRobotSingle"
def __init__(self, *args, robot_uids="xlerobot_single", **kwargs):
super().__init__(*args, robot_uids=robot_uids, **kwargs)
def _load_scene(self, options: dict):
"""씬 로드: 바닥, 테이블, 큐브, 목표 영역"""
# 바닥
self.scene.add_ground(altitude=0)
# 테이블
builder = self.scene.create_actor_builder()
builder.add_box_collision(half_size=[0.4, 0.4, 0.02])
builder.add_box_visual(half_size=[0.4, 0.4, 0.02], color=[0.8, 0.6, 0.4])
self.table = builder.build_static(name="table")
self.table.set_pose(sapien.Pose([0.5, 0, 0.4]))
# 큐브 (집을 물체)
builder = self.scene.create_actor_builder()
builder.add_box_collision(half_size=[0.02, 0.02, 0.02])
builder.add_box_visual(half_size=[0.02, 0.02, 0.02], color=[1, 0, 0])
self.cube = builder.build(name="cube")
# 목표 위치 시각화
builder = self.scene.create_actor_builder()
builder.add_sphere_visual(radius=0.03, color=[0, 1, 0, 0.5])
self.goal_site = builder.build_static(name="goal")
def _initialize_episode(self, env_idx: np.ndarray, options: dict):
"""에피소드 초기화: 로봇, 큐브, 목표 위치 랜덤 배치"""
# 큐브 위치 랜덤 (테이블 위)
cube_pos = np.array([0.5, 0, 0.45]) + np.random.uniform(-0.1, 0.1, 3)
cube_pos[2] = 0.45 # Z는 고정
self.cube.set_pose(sapien.Pose(cube_pos))
# 목표 위치 랜덤
goal_pos = np.array([0.5, 0, 0.45]) + np.random.uniform(-0.15, 0.15, 3)
goal_pos[2] = 0.45
self.goal_site.set_pose(sapien.Pose(goal_pos))
# 로봇 초기 위치
self.agent.robot.set_pose(sapien.Pose([0, 0, 0]))
def evaluate(self):
"""보상 계산"""
# 큐브와 목표 사이 거리
cube_pos = self.cube.pose.p
goal_pos = self.goal_site.pose.p
distance = np.linalg.norm(cube_pos - goal_pos)
# 거리 기반 보상
reward = -distance
# 성공 판정 (거리 < 5cm)
success = distance < 0.05
return {
"success": success,
"distance": distance,
"reward": reward,
}
def compute_dense_reward(self, obs, action, info):
"""Dense reward"""
return info["reward"]
def compute_normalized_dense_reward(self, obs, action, info):
"""Normalized reward [0, 1]"""
max_dist = 0.5
return np.clip(1 - info["distance"] / max_dist, 0, 1)
사용 방법:
# test_custom_task.py
import gymnasium as gym
from custom_task import XLeRobotPickPlaceEnv
env = gym.make(
"XLeRobotPickPlace-v1",
obs_mode="state",
control_mode="pd_joint_delta_pos",
render_mode="human",
)
obs, _ = env.reset()
for _ in range(1000):
action = env.action_space.sample()
obs, reward, terminated, truncated, info = env.step(action)
env.render()
print(f"Reward: {reward:.3f}, Distance: {info['distance']:.3f}")
if terminated or truncated:
obs, _ = env.reset()
env.close()
고급 기능
1. 데이터셋 수집
# collect_dataset.py
import gymnasium as gym
from mani_skill.utils.wrappers import RecordEpisode
# 환경 래핑 (데이터 기록)
env = gym.make("PushCube-v1", render_mode="cameras")
env = RecordEpisode(
env,
output_dir="./demos",
save_trajectory=True,
save_video=True,
)
# 데모 수집 (키보드 텔레오퍼레이션)
# ... 키보드로 제어하며 데이터 수집
# 저장된 데이터:
# - demos/trajectory_0.h5 (상태, 액션)
# - demos/video_0.mp4 (비디오)
활용: Imitation Learning (BC, GAIL)
2. ReplicaCAD 씬
# ReplicaCAD: 실제 방 스캔 데이터 기반 환경
env = gym.make(
"ReplicaCAD_SceneManipulation-v1",
robot_uids="xlerobot_single",
render_mode="human",
)
# 사실적인 가구, 물체 배치
# 복잡한 네비게이션 + 조작 태스크
3. Multi-Task Learning
# 여러 태스크 동시 학습
tasks = ["PushCube-v1", "PickCube-v1", "StackCube-v1"]
for task in tasks:
env = gym.make(task, robot_uids="xlerobot_dual")
# ... 학습
문제 해결
문제 1: ImportError: cannot import name 'XLeRobotSingle'
원인: XLeRobot 에이전트가 등록되지 않음
해결:
# 스크립트 맨 위에 추가
import sys
sys.path.insert(0, "/home/사용자명/XLeRobot/simulation/Maniskill")
from agents.xlerobot import xlerobot
문제 2: GPU 메모리 부족
원인: num_envs 너무 큼
해결:
# num_envs 줄이기
env_kwargs = dict(
num_envs=16, # 64 → 16
sim_backend="gpu",
)
문제 3: 렌더링 느림
원인: 고급 셰이더 사용
해결:
# rt-fast 대신 minimal 사용
--shader="minimal"
요약
핵심 포인트
- ManiSkill: GPU 병렬 RL 환경 (수백 개 동시 실행)
- XLeRobot 에이전트:
xlerobot_single,xlerobot_dual - 제어 모드:
pd_joint_delta_pos(초보자 추천) - 학습: PPO, SAC 등 표준 RL 알고리즘 사용
- 커스텀 태스크:
BaseEnv상속으로 쉽게 생성
다음 단계
- 3.6 URDF/MJCF 모델 →: 로봇 모델 파일 구조 이해
- 4장 하드웨어 →: 실제 로봇 조립