RL ํ์ต โ Isaac Lab์์ mechadog ์ ์ฑ ๋ง๋ค๊ธฐ
PC(x86_64, NVIDIA RTX 3070+ ๊ถ์ฅ)์์ ์๋ฎฌ๋ ์ด์ ์ผ๋ก PPO ์ ์ฑ ์ ํ์ตํ ๋ค ONNX๋ก exportํฉ๋๋ค. ํ์ต๋ ์ ์ฑ ์ Rock 5C์ RKNN/CPU์์ ์ง์ ๋ฐฐํฌ๋ฉ๋๋ค.
isaac-sim/IsaacLab GitHub](https://github.com/isaac-sim/IsaacLab)์ [Lab-of-AI-and-Robotics/IsaacLab-Tutorial](https://github.com/Lab-of-AI-and-Robotics/IsaacLab-Tutorial)๋ฅผ ๋จผ์ ํ๋ฒ ํต๊ณผํ ๋ค์, ๋ณธ mechadog URDF๋ก ๊ต์ฒดํ๋ ์์๊ฐ ๊ฐ์ฅ ๋น ๋ฆ
๋๋ค.
1. PC ํ๊ฒฝ ์ค๋น
์๊ตฌ ์ฌ์
| ํญ๋ชฉ | ๊ถ์ฅ | ์ต์ |
|---|---|---|
| GPU | NVIDIA RTX 4080 (16 GB VRAM) | RTX 3070 (8GB ยท ๊ถ์ฅ์น ๋ฏธ๋ง) |
| RAM | 64 GB | 32 GB |
| ๋์คํฌ | 500 GB NVMe SSD | 200 GB |
| OS | Ubuntu 22.04 LTS | WSL2 Ubuntu 22.04 (์ฑ๋ฅ โ50%) |
์ค์น
# 1. CUDA(๋๋ผ์ด๋ฒยทtoolkit) โ host system์ ๋ง๊ฒ ์ค์น ํ
# 2. Isaac Sim 5.x via Omniverse Launcher (https://docs.isaacsim.omniverse.nvidia.com/5.1.0/installation/requirements.html)
# 3. Isaac Lab Git Clone
git clone https://github.com/isaac-sim/IsaacLab.git
cd IsaacLab
./docker/container.sh # ๋๋ ์ง์ install
pip install -e .
pip install rsl-rl-lib==2.3.0 # PPO + Student-Teacher Distillation
2. mechadog์ฉ 12 DOF URDF ์ค๋น
๊ธฐ๋ณธ์ ์ผ๋ก ODRI Solo12 12dof v1์ URDF๋ฅผ ์ถ๋ฐ์ ์ผ๋ก ์ฌ์ฉํฉ๋๋ค. URDF๋ ๋ค์์ ๋ง์กฑํด์ผ ํฉ๋๋ค:
base_link1๊ฐ ยทleg_<q>_hip_aa / hip_fe / knee_calf๊ฐ 4๊ฐ์ ์ด 12 ๋งํฌ- ๊ด์ ํ๊ณ (joint limits) ์ ์
- ๋ชจ๋ joint์ effort limit์ MG90S์ ์ ๊ฒฉ ํ ํฌ(โ 0.18 Nยทm)์ ๋ง๊ฒ ์ค์ฌ๋๋ค โ ๊ทธ๋ ์ง ์์ผ๋ฉด ์๋ฎฌ์ ๋ฌดํ ํ ํฌ๋ก ํ์ต๋์ด real์์ ๋ชป ๋ฐ๋ผ๊ฐ
- ๋ฒ ์ด์ค์ IMU frame(BNO055 XยทYยทZ ์ ํฉ)์ ๋ฏธ๋ฆฌ ์ ์
# Solo12 12dof URDF ๋ณต์ฌ ํ ์์
mkdir -p mechadog_description/urdf
cp /path/to/solo_12dof_v1.urdf mechadog_description/urdf/mechadog.urdf
# effort limit ๋ณ๊ฒฝ (ํ
์คํธ ํ ์ค sed)
sed -i 's/effort="[0-9]\+\.[0-9]\+"/effort="0.180"/g' mechadog_description/urdf/mechadog.urdf
sed -i 's/effort="[0-9]\+\.[0-9]\+"/effort="0.180"/g' mechadog.urdf
3. Isaac Lab ํ๊ฒฝ ์ ์
๋ณธ mechadog ํ๋ก์ ํธ๋ ๋ชจ๋ ํ๊ฒฝ ์ ์ setup์์ leggedrobotics/legged_gym ํจํด์ ๊ทธ๋๋ก ์ฌ์ฉํฉ๋๋ค.
# file: mechadog_lab/envs/mechadog/mechadog_env_cfg.py
from isaaclab.envs import ManagerBasedRLEnvCfg
from isaaclab.managers import ObservationGroupCfg, ObservationTermCfg
from isaaclab.assets import ArticulationCfg
from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR
import isaaclab.sim as sim_utils
from isaaclab_tasks.manager_based.locomotion.velocity import mdp
from dataclasses import MISSING
MECHADOG_USD = "mechadog/usd/mechadog.usd" # URDF โ Isaac Sim Import ์ถ์ฒ
@configclass
class MechadogRoughEnvCfg(ManagerBasedRLEnvCfg):
scene = SceneCfg(num_envs=4096, env_spacing=2.5)
robot = ArticulationCfg(
prim_path="{ENV_REGEX_NS}/Robot",
spawn=sim_utils.UsdFileCfg(usd_path=MECHADOG_USD),
actuators={leg_joint: ImplicitActuatorCfg(joint_names_expr=[...], effort_limit=0.18, stiffness=20.0, damping=0.5)},
)
commands = UniformVelocityCommandCfg(sampling_range=(-1.0,1.0), rel_standing_envs=0.1)
rewards = RewardsCfg() # r_track_lin, r_track_ang, r_torque, r_action_rate, r_orientation, r_alive
observations = ObservationsCfg() # 48-dim obs: base state (9) + cmd (3) + joint pos (12) + joint vel (12) + last action (12)
terminations = TerminationsCfg() # base contact terminate, time out 20s
decimation = 4 # 200 Hz decision, 50 Hz control
sim_dt = 0.005
4. PPO ํ์ต launch (rsl_rl)
python scripts/reinforcement_learning/rsl_rl/train.py \
--task Isaac-Mechadog-Velocity-Rough-v0 \
--num_envs 4096 \
--headless \
--max_iterations 2000 \
--run_name mechadog_walk_v26p06
RTX 4080 ๊ธฐ์ค ~15-25๋ถ(iter 1000ํ) ๋ด์ธ. ๋๋ฉ์ธ ๋๋คํ ๊ฐ๋์ ๋ฐ๋ผ 1k~3k iter ํ์.
5. ๋ณด์ ํจ์ (์์ง)
์์ ๊ถ์ฅ๊ฐ:
| ๊ฐ์ค์น | ๊ฐ | ํจ๊ณผ |
|---|---|---|
| w_lin (vx, vy ํธ๋ํน) | 2.0 | ์ง์ ์๋ ์ถ์ข |
| w_ang (yaw ํธ๋ํน) | 1.0 | ํ์ ๋ช ๋ น ์ถ์ข |
| w_tau | 0.0001 | ํ ํฌ ์ต์ํ |
| w_joint_acc | 0.01 | ๊ด์ ๊ฐ์ ํ๋ํฐ |
| w_orient | 0.5 | ์ํ ์์ธ ์ ์ง |
| w_alive | 0.1 | ์ด์์์ ๋ณด๋์ค |
| w_foot | 0.001 | ๋ฐ ์ฌ๋ฆฝยท์ถฉ๊ฒฉ ํก์ |
6. ๋๋ฉ์ธ ๋๋คํ (์ธ๋ถ ์ถฉ๊ฒฉ ์ ํญ ์์ธ ๋ณด์ ํ์ต)
# file: mechadog_lab/envs/mechadog/randomizers.py
from isaaclab.managers import RandomizationTermCfg as RandTerm
import isaaclab.sim as sim_utils
# ๋ณธ์ฒด์ ๊ฐํด์ง๋ ์ํ์ค (์ธ๋ถ ์ถฉ๊ฒฉ ๋ชจ์ฌ)
@torch.no_grad()
def push_impulse(env, env_ids):
env_origins = env.scene.env_origins[env_ids]
# ํ / ํ ํฌ random per env, ์ ์ฉ 0.5s ๋์ 1ํ
for i, env_id in enumerate(env_ids):
# base link apply force/torque
force = torch.tensor(np.random.uniform(-30, 30, 3))
torque = torch.tensor(np.random.uniform(-5, 5, 3))
env.robot.set_external_force_and_torque(force, torque, env_id)
# ๋ค์ reset๋ง๋ค ๋ณธ์ฒด ์ง๋ ยฑ10%, ๋ง์ฐฐ ยฑ25%
@torch.no_grad()
def randomize_mass(env, env_ids):
rng = np.random.uniform(0.9, 1.1, len(env_ids))
env.robot.root_physx_view.set_masses(env_ids, env.base_mass * rng)
7. ONNX export & ๊ฒ์ฆ
python scripts/reinforcement_learning/rsl_rl/play.py \
--task Isaac-Mechadog-Velocity-Rough-v0 \
--num_envs 1 \
--checkpoint /logs/mechadog_walk_v26p06/model_2000.pt \
--export_onnx /out/policy.onnx
์์ฑ๋ policy.onnx(~90 KB)๋ Rock 5C์ ๋ณต์ฌ. CPU 50 Hz ์ถ๋ก ์ ํ inference ~ 3 ms.
8. ์นด๋ฉ๋ผ ์ธ์ง โ ์ต์ : YOLO-Nano depth ์ฌ์ ํ์ต
๋ณธ ๊ฒฉ์ฐจ๋ ํ์ต ์๋ฎฌ์ ์นด๋ฉ๋ผ ์ ๋ ฅ์ 0์ฑ๋ placeholder๋ก ๋๋ฉด ๋ฐ์ํฉ๋๋ค. ๋ณธ ํ๋ก์ ํธ์์๋ ์๋ฎฌ์์ ํฉ์ฑ depth map (4์ฑ๋)์ ์ ์ฑ obs ๋์ ํฉ์นฉ๋๋ค:
# ํฉ์ฑ ์นด๋ฉ๋ผ ์
๋ ฅ (4 depth bins ร 16ร16 sectors = 1024 dim)์ผ๋ก obs ํ์ฅ
cam_feature = simulate_lidar_4bin(env) # Isaac Lab ray cast
obs = torch.cat([base_state, joint_state, last_action, cam_feature], dim=-1)
# obs ์ฐจ์: 48 + 1024 = 1072 โ RoboStudio์์ ์ค์ ๋ก๋ 256-512๋ก ์ฐจ์ ์ถ์
9. ํ์์ฉ ์์ ๋ฃจํธ ์ ๋ฆฌ
- Isaac Lab ์ค์น (RTX 3070+ ๊ถ์ฅ, ์ step 1)
- Solo 12dof URDF ๋ณต์ + effort limit 0.18 Nยทm ์ ์ฉ
leggedrobotics/legged_gym์anymal_c_flatํ๊ฒฝ์ ๊ทธ๋๋ก ๋ณต๋ถํดmechadog_flat์ผ๋ก rename- 50 iter ์งง๊ฒ ํ์ต โ ์ ๋๋์ง 5๋ถ ๋ง์ ํ์ธ
- ์ ๋์ํ๋ฉด 1000~3000 iter ๋ณธ๊ฒฉ ํ์ต
- ONNX export โ Rock 5C์ ๋ณต์ฌ
- ์ ์ด ํ์ด์ง์ ROS2 ๋ ธ๋ + CRSF ํ์๋ก ๋ฐฐํฌ
10. ํ์ต ๋น์ฉ ์ถ์ (ํ์ 1์ธ ๊ธฐ์ค)
| ํญ๋ชฉ | ๊ฐ |
|---|---|
| GPU ์๊ฐ (1000 iter, RTX 4080) | ~25 ๋ถ (~$0.5 / GG ๊ฐ๊ฒฉ ํ์ฐ ์) |
| ๊ฐ๋ฐ ์๊ฐ (URDF + env cfg) | 6~10 ์๊ฐ |
| ๋๋ฒ๊น +ํ๋ (๋๋ฉ์ธ ๋๋คํ iteration) | 10~30 ์๊ฐ ์ถ๊ฐ |