1편 : IsaacSim, IsaacLab 설치하기, 예제 돌려보기, Miniconda 가상 환경 구성

2편 – IsaacLab tutorial 코드 분석

0. tutorials 코드 위치

project 폴더의 scripts/tutorials/

image

1. Empty Scene (create_empty.py)

# Copyright (c) 2022-2025, The Isaac Lab Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""This script demonstrates how to create a simple stage in Isaac Sim.

.. code-block:: bash

    # Usage
    ./isaaclab.sh -p scripts/tutorials/00_sim/create_empty.py

"""

"""Launch Isaac Sim Simulator first."""


import argparse  # 표준 파이썬 모듈: 명령줄 인자를 파싱할 때 사용

from isaaclab.app import AppLauncher  # Isaac Lab에서 제공하는 App 실행 유틸리티

# ArgumentParser 객체 생성 – 명령줄에서 인자를 받을 수 있게 함
parser = argparse.ArgumentParser(description="Tutorial on creating an empty stage.")

# AppLauncher에서 기본 제공하는 CLI 인자들(예: headless mode, livestream 등)을 추가
AppLauncher.add_app_launcher_args(parser)

# 명령줄 인자를 실제로 파싱
args_cli = parser.parse_args()

# AppLauncher 객체를 생성하고, 이를 통해 simulation app 실행
# 이때 환경 설정 및 시뮬레이터 설정도 같이 처리됨
app_launcher = AppLauncher(args_cli)
simulation_app = app_launcher.app  # 실행된 시뮬레이터 앱 객체 저장

# 여기부터 실제 시뮬레이션 관련 코드 작성

from isaaclab.sim import SimulationCfg, SimulationContext  # 시뮬레이션 설정 및 컨텍스트 제어 클래스

def main():
    """메인 실행 함수"""

    # 시뮬레이션 설정 객체 생성 (물리 엔진 설정 포함)
    sim_cfg = SimulationCfg(dt=0.01)  # 시간 간격을 0.01초로 설정 (시뮬레이션 속도 결정)
    sim = SimulationContext(sim_cfg)  # 설정을 기반으로 시뮬레이션 컨텍스트 생성

    # 카메라 위치 및 바라보는 지점 설정 (카메라는 [2.5, 2.5, 2.5] 위치에서 [0,0,0] 중심을 봄)
    sim.set_camera_view([2.5, 2.5, 2.5], [0.0, 0.0, 0.0])

    # 시뮬레이션 재설정: 타임라인 재생 및 물리 초기화 포함 (step 전에 반드시 호출 필요)
    sim.reset()
    print("[INFO]: Setup complete...")  # 준비 완료 메시지 출력

    # 시뮬레이션 루프 실행 (사용자가 시뮬레이터 창을 닫을 때까지 반복)
    while simulation_app.is_running():
        sim.step()  # 물리 시뮬레이션 한 스텝 진행 (렌더링 포함)

if __name__ == "__main__":
    # main 함수 실행
    main()
    # 시뮬레이션 종료 및 리소스 정리
    simulation_app.close()

2. spawn_prims.py

# Copyright (c) 2022-2025, The Isaac Lab Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""This script demonstrates how to spawn prims into the scene.

.. code-block:: bash

    # Usage
    ./isaaclab.sh -p scripts/tutorials/00_sim/spawn_prims.py

"""

"""Launch Isaac Sim Simulator first."""



import argparse  # 명령줄 인자 파싱용 표준 Python 모듈

from isaaclab.app import AppLauncher  # Isaac Lab에서 제공하는 시뮬레이터 런처 클래스

# 명령줄 인자를 처리하기 위한 parser 생성
parser = argparse.ArgumentParser(description="Tutorial on spawning prims into the scene.")
# AppLauncher에서 정의한 공통 인자 추가 (예: headless 모드, 디바이스 선택 등)
AppLauncher.add_app_launcher_args(parser)
# 실제로 인자들을 파싱
args_cli = parser.parse_args()
# 파싱한 인자를 이용해 AppLauncher 인스턴스 생성 → 내부적으로 Isaac Sim 실행
app_launcher = AppLauncher(args_cli)
simulation_app = app_launcher.app  # 실행된 시뮬레이터 핸들 (렌더링 루프 등 제어 가능)

"""아래는 시뮬레이터 실행 이후에 호출되어야 합니다."""

import isaacsim.core.utils.prims as prim_utils  # Prim(기본 단위 오브젝트) 관련 유틸 함수
import isaaclab.sim as sim_utils  # 시뮬레이션 구성(Config) 및 생성 관련 클래스
from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR  # Omniverse Nucleus 서버 자산 경로

def design_scene():
    """씬에 여러 Prim들을 생성하여 환경을 구성하는 함수입니다."""
    
    # 바닥(ground plane)을 생성하는 Config 객체 생성
    cfg_ground = sim_utils.GroundPlaneCfg()
    # func()은 해당 config를 사용하여 실제로 USD 스테이지에 prim을 생성합니다
    cfg_ground.func("/World/defaultGroundPlane", cfg_ground)

    # Distant Light(무한광): 강한 평행광 형태의 빛을 추가
    cfg_light_distant = sim_utils.DistantLightCfg(
        intensity=3000.0,  # 광원 세기
        color=(0.75, 0.75, 0.75),  # 회색 톤
    )
    # 위치를 명시해도 distant light는 무한 원거리 광원이므로 사실 위치는 큰 영향 없음
    cfg_light_distant.func("/World/lightDistant", cfg_light_distant, translation=(1, 0, 10))

    # 객체들을 그룹화하기 위한 transform 노드 생성 (Xform Prim)
    # 이 아래에 생성된 모든 prim은 함께 이동/회전 가능
    prim_utils.create_prim("/World/Objects", "Xform")

    # 빨간색 콘 두 개 생성 (visual만 존재하고, 물리 속성 없음)
    cfg_cone = sim_utils.ConeCfg(
        radius=0.15,
        height=0.5,
        visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0)),  # 빨간색
    )
    cfg_cone.func("/World/Objects/Cone1", cfg_cone, translation=(-1.0, 1.0, 1.0))
    cfg_cone.func("/World/Objects/Cone2", cfg_cone, translation=(-1.0, -1.0, 1.0))

    # 초록색 콘 생성: rigid body + 충돌 + 질량 설정 → 물리 시뮬레이션 가능
    cfg_cone_rigid = sim_utils.ConeCfg(
        radius=0.15,
        height=0.5,
        rigid_props=sim_utils.RigidBodyPropertiesCfg(),  # 강체 설정
        mass_props=sim_utils.MassPropertiesCfg(mass=1.0),  # 질량 1kg
        collision_props=sim_utils.CollisionPropertiesCfg(),  # 충돌 가능
        visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0)),  # 초록색
    )
    # orientation은 쿼터니언 값으로 회전 상태를 설정
    cfg_cone_rigid.func(
        "/World/Objects/ConeRigid", cfg_cone_rigid, translation=(-0.2, 0.0, 2.0), orientation=(0.5, 0.0, 0.5, 0.0)
    )

    # 파란색 변형 가능한 큐브 생성 (deformable body)
    # 내부 응력/신축성 시뮬레이션을 위한 설정 포함
    cfg_cuboid_deformable = sim_utils.MeshCuboidCfg(
        size=(0.2, 0.5, 0.2),  # 직육면체 크기
        deformable_props=sim_utils.DeformableBodyPropertiesCfg(),  # 내부 물성 (신축성 등)
        visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.0, 1.0)),  # 파란색
        physics_material=sim_utils.DeformableBodyMaterialCfg(),  # 마찰, 반발력 등 외부 물성
    )
    cfg_cuboid_deformable.func("/World/Objects/CuboidDeformable", cfg_cuboid_deformable, translation=(0.15, 0.0, 2.0))

    # 외부 USD 파일(table_instanceable.usd)에서 테이블 로드
    # Nucleus 서버에서 제공하는 자산으로, 실제 경로는 ISAAC_NUCLEUS_DIR에 저장
    cfg = sim_utils.UsdFileCfg(usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/Mounts/SeattleLabTable/table_instanceable.usd")
    cfg.func("/World/Objects/Table", cfg, translation=(0.0, 0.0, 1.05))  # 테이블은 살짝 위에 배치


def main():
    """메인 함수 – 시뮬레이터 초기화, 씬 구성, 시뮬레이션 루프 실행"""

    # 시뮬레이션 설정: 시간 간격(dt), 사용할 디바이스 등 지정
    sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device)
    sim = sim_utils.SimulationContext(sim_cfg)

    # 카메라 위치 설정 (시작 위치와 바라보는 포인트)
    sim.set_camera_view([2.0, 0.0, 2.5], [-0.5, 0.0, 0.5])

    # 위에서 정의한 씬 구성 함수 실행 → 여러 prim들을 스테이지에 배치
    design_scene()

    # 시뮬레이터 재설정 (physics 핸들 초기화 및 타임라인 시작)
    sim.reset()
    print("[INFO]: Setup complete...")

    # 시뮬레이션 루프 실행: 앱이 종료될 때까지 계속 실행
    while simulation_app.is_running():
        sim.step()  # 시뮬레이터 한 스텝 진행 (physics + 렌더링)


# 프로그램 실행 진입점
if __name__ == "__main__":
    main()  # 메인 함수 실행
    simulation_app.close()  # 시뮬레이터 정상 종료
image 35

3. Rigid Object (run_rigid_object.py)

# Copyright (c) 2022-2025, The Isaac Lab Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""
This script demonstrates how to create a rigid object and interact with it.

.. code-block:: bash

    # Usage
    ./isaaclab.sh -p scripts/tutorials/01_assets/run_rigid_object.py

"""

"""Launch Isaac Sim Simulator first."""


import argparse  # 명령줄 인자를 처리하는 표준 파이썬 모듈

from isaaclab.app import AppLauncher  # Isaac Lab의 시뮬레이션 앱 실행을 위한 런처

# argparse 설정 – 명령줄에서 인자를 받을 수 있게 구성
parser = argparse.ArgumentParser(description="Tutorial on spawning and interacting with a rigid object.")
# AppLauncher에서 제공하는 공통 인자 추가 (예: --headless, --renderer 등)
AppLauncher.add_app_launcher_args(parser)
# 인자 파싱
args_cli = parser.parse_args()

# AppLauncher를 통해 시뮬레이션 앱 실행
app_launcher = AppLauncher(args_cli)
simulation_app = app_launcher.app  # 시뮬레이션 핸들 얻기

"""아래부터 시뮬레이터가 실행된 이후에 동작해야 하는 코드입니다."""

import torch  # PyTorch: 위치, 속도 등 텐서 기반 시뮬레이션 데이터 처리에 사용

import isaacsim.core.utils.prims as prim_utils  # Prim 생성 및 조작 유틸

import isaaclab.sim as sim_utils  # 시뮬레이터 설정 및 물리 객체 구성 유틸
import isaaclab.utils.math as math_utils  # 무작위 위치 샘플링 등 수학 유틸
from isaaclab.assets import RigidObject, RigidObjectCfg  # 강체 객체 클래스와 설정 객체
from isaaclab.sim import SimulationContext  # 시뮬레이터 컨텍스트 클래스


def design_scene():
    """씬을 구성하는 함수입니다. 바닥, 조명, Xform 그룹, 강체 객체를 생성합니다."""
    # 바닥 생성 (Ground Plane)
    cfg = sim_utils.GroundPlaneCfg()
    cfg.func("/World/defaultGroundPlane", cfg)

    # 돔 라이트(Dome Light): 전체 조명 생성
    cfg = sim_utils.DomeLightCfg(intensity=2000.0, color=(0.8, 0.8, 0.8))
    cfg.func("/World/Light", cfg)

    # Xform 그룹 생성 – 4개의 위치에 원점을 기준으로 그룹 생성 (Origin0 ~ Origin3)
    origins = [[0.25, 0.25, 0.0], [-0.25, 0.25, 0.0], [0.25, -0.25, 0.0], [-0.25, -0.25, 0.0]]
    for i, origin in enumerate(origins):
        prim_utils.create_prim(f"/World/Origin{i}", "Xform", translation=origin)

    # 강체 객체(Rigid Object) 생성 구성
    cone_cfg = RigidObjectCfg(
        prim_path="/World/Origin.*/Cone",  # 정규 표현식으로 Origin0 ~ Origin3 하위 Cone 생성
        spawn=sim_utils.ConeCfg(  # 실제 생성할 객체 정의
            radius=0.1,
            height=0.2,
            rigid_props=sim_utils.RigidBodyPropertiesCfg(),  # 강체 속성 (물리 시뮬레이션 가능)
            mass_props=sim_utils.MassPropertiesCfg(mass=1.0),  # 질량 1kg
            collision_props=sim_utils.CollisionPropertiesCfg(),  # 충돌 처리 활성화
            visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0), metallic=0.2),  # 초록색
        ),
        init_state=RigidObjectCfg.InitialStateCfg(),  # 초기 상태: 위치/회전/속도 모두 0
    )
    cone_object = RigidObject(cfg=cone_cfg)  # 구성 기반으로 강체 객체 인스턴스 생성

    # 객체 및 생성된 위치 반환
    scene_entities = {"cone": cone_object}
    return scene_entities, origins


def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, RigidObject], origins: torch.Tensor):
    """시뮬레이션 루프를 실행하고 객체와 상호작용하는 함수입니다."""
    # 딕셔너리에서 cone 객체 가져오기 (실제로는 InteractiveScene에서 관리하게 됨)
    cone_object = entities["cone"]

    # 시뮬레이션 시간 간격(dt) 및 초기화
    sim_dt = sim.get_physics_dt()
    sim_time = 0.0
    count = 0

    # 시뮬레이션 루프 실행
    while simulation_app.is_running():
        # 매 250스텝마다 객체 상태 초기화 (reset)
        if count % 250 == 0:
            sim_time = 0.0
            count = 0

            # 기본 루트 상태를 복사 (위치, 회전, 속도 0)
            root_state = cone_object.data.default_root_state.clone()

            # 초기 위치 조정 – 지정된 origin 위치에 배치
            root_state[:, :3] += origins
            # 원기둥 위의 임의 위치 샘플링하여 높이 무작위화
            root_state[:, :3] += math_utils.sample_cylinder(
                radius=0.1, h_range=(0.25, 0.5), size=cone_object.num_instances, device=cone_object.device
            )

            # 시뮬레이터에 위치/회전/속도 적용
            cone_object.write_root_pose_to_sim(root_state[:, :7])
            cone_object.write_root_velocity_to_sim(root_state[:, 7:])
            cone_object.reset()  # 내부 상태 초기화
            print("----------------------------------------")
            print("[INFO]: Resetting object state...")

        # 기타 상태 쓰기 (현재 예제에서는 외력 없음, 포함만 함)
        cone_object.write_data_to_sim()

        # 시뮬레이터 한 스텝 진행
        sim.step()

        # 시간 및 카운터 업데이트
        sim_time += sim_dt
        count += 1

        # 객체 상태 업데이트 (내부 버퍼 반영)
        cone_object.update(sim_dt)

        # 50스텝마다 위치 출력
        if count % 50 == 0:
            print(f"Root position (in world): {cone_object.data.root_state_w[:, :3]}")


def main():
    """메인 함수 – 시뮬레이터 설정 및 전체 실행 흐름"""
    sim_cfg = sim_utils.SimulationCfg(device=args_cli.device)
    sim = SimulationContext(sim_cfg)  # 시뮬레이션 초기화

    # 카메라 설정: 시점과 바라보는 위치
    sim.set_camera_view(eye=[1.5, 0.0, 1.0], target=[0.0, 0.0, 0.0])

    # 씬 구성 실행 및 객체 가져오기
    scene_entities, scene_origins = design_scene()
    # PyTorch 텐서로 변환 (GPU 시뮬레이션 지원)
    scene_origins = torch.tensor(scene_origins, device=sim.device)

    # 시뮬레이터 리셋 (타임라인 시작 및 초기화)
    sim.reset()
    print("[INFO]: Setup complete...")

    # 시뮬레이터 루프 실행
    run_simulator(sim, scene_entities, scene_origins)


# 스크립트 진입점
if __name__ == "__main__":
    main()  # 메인 함수 실행
    simulation_app.close()  # 시뮬레이션 종료
image 36

4. run_deformable_object.py

# Copyright (c) 2022-2025, The Isaac Lab Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""
This script demonstrates how to work with the deformable object and interact with it.

.. code-block:: bash

    # Usage
    ./isaaclab.sh -p scripts/tutorials/01_assets/run_deformable_object.py

"""

"""Launch Isaac Sim Simulator first."""


import argparse  # 명령줄 인자 처리를 위한 파이썬 표준 모듈

from isaaclab.app import AppLauncher  # Isaac Lab에서 시뮬레이터 실행을 위한 런처 클래스

# argparse 설정
parser = argparse.ArgumentParser(description="Tutorial on interacting with a deformable object.")
AppLauncher.add_app_launcher_args(parser)  # App 실행 관련 인자 추가
args_cli = parser.parse_args()

# 시뮬레이터 실행
app_launcher = AppLauncher(args_cli)
simulation_app = app_launcher.app  # 실행된 시뮬레이터 핸들 저장

"""아래부터는 Isaac Sim이 실행된 이후에 호출되어야 하는 코드입니다."""

import torch  # 텐서 연산용 (변형체의 상태 관리에 활용)

import isaacsim.core.utils.prims as prim_utils  # prim (기본 오브젝트) 생성 유틸리티
import isaaclab.sim as sim_utils  # 시뮬레이션 구성 클래스 및 설정
import isaaclab.utils.math as math_utils  # 수학적 유틸 (회전, 무작위 샘플링 등)
from isaaclab.assets import DeformableObject, DeformableObjectCfg  # 변형 객체 클래스 및 설정 객체
from isaaclab.sim import SimulationContext  # 시뮬레이션 컨텍스트


def design_scene():
    """씬 구성 함수: 바닥, 조명, 그룹, 변형체 생성 포함"""

    # 바닥 생성
    cfg = sim_utils.GroundPlaneCfg()
    cfg.func("/World/defaultGroundPlane", cfg)

    # 돔 라이트 추가 (전반적인 조명)
    cfg = sim_utils.DomeLightCfg(intensity=2000.0, color=(0.8, 0.8, 0.8))
    cfg.func("/World/Light", cfg)

    # Xform 그룹 4개 생성 (Origin0 ~ Origin3)
    origins = [[0.25, 0.25, 0.0], [-0.25, 0.25, 0.0], [0.25, -0.25, 0.0], [-0.25, -0.25, 0.0]]
    for i, origin in enumerate(origins):
        prim_utils.create_prim(f"/World/Origin{i}", "Xform", translation=origin)

    # 변형 가능한 큐브 설정
    cfg = DeformableObjectCfg(
        prim_path="/World/Origin.*/Cube",  # Origin 그룹 아래 위치
        spawn=sim_utils.MeshCuboidCfg(
            size=(0.2, 0.2, 0.2),  # 큐브 크기
            deformable_props=sim_utils.DeformableBodyPropertiesCfg(
                rest_offset=0.0, contact_offset=0.001),  # 변형 세부 설정
            visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.5, 0.1, 0.0)),  # 시각 재질 (갈색)
            physics_material=sim_utils.DeformableBodyMaterialCfg(
                poissons_ratio=0.4, youngs_modulus=1e5),  # 포아송 비, 영률 → 변형 물성 정의
        ),
        init_state=DeformableObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 1.0)),  # 초기 위치 (높이 1m)
        debug_vis=True,  # 디버그 시각화 활성화
    )
    cube_object = DeformableObject(cfg=cfg)  # 실제 객체 생성

    # 생성된 객체 및 그룹 반환
    scene_entities = {"cube_object": cube_object}
    return scene_entities, origins


def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, DeformableObject], origins: torch.Tensor):
    """시뮬레이션 루프 실행: deformable object를 제어하고 상태를 업데이트합니다."""

    # 큐브 객체 가져오기
    cube_object = entities["cube_object"]

    # 시뮬레이션 시간 간격 및 변수 초기화
    sim_dt = sim.get_physics_dt()
    sim_time = 0.0
    count = 0

    # 변형 객체의 vertex(정점) 위치 제어용 배열 복사 (수정 가능한 형태)
    nodal_kinematic_target = cube_object.data.nodal_kinematic_target.clone()

    # 시뮬레이션 루프 실행
    while simulation_app.is_running():
        # 매 250 스텝마다 상태 초기화
        if count % 250 == 0:
            sim_time = 0.0
            count = 0

            # 초기 정점 상태 복사
            nodal_state = cube_object.data.default_nodal_state_w.clone()

            # 위치: 무작위 이동 → 기존 origin 위치 위에 무작위 오프셋 추가
            pos_w = torch.rand(cube_object.num_instances, 3, device=sim.device) * 0.1 + origins
            # 회전: 랜덤 쿼터니언 회전 적용
            quat_w = math_utils.random_orientation(cube_object.num_instances, device=sim.device)
            # 위치/회전 변환 적용
            nodal_state[..., :3] = cube_object.transform_nodal_pos(nodal_state[..., :3], pos_w, quat_w)

            # 정점 상태를 시뮬레이터에 적용
            cube_object.write_nodal_state_to_sim(nodal_state)

            # 정점 상태를 kinematic 타겟으로 복사 (위치 제어용)
            nodal_kinematic_target[..., :3] = nodal_state[..., :3]
            nodal_kinematic_target[..., 3] = 1.0  # 모든 정점은 자유 상태 (1.0 = unconstrained)
            cube_object.write_nodal_kinematic_target_to_sim(nodal_kinematic_target)

            # 내부 상태 초기화
            cube_object.reset()
            print("----------------------------------------")
            print("[INFO]: Resetting object state...")

        # 특정 정점(k=0)의 z축 위치를 조금씩 증가시켜 위로 움직임 생성
        nodal_kinematic_target[[0, 3], 0, 2] += 0.001
        # 해당 정점은 kinematic 제어 상태로 설정 (0.0 = constrained)
        nodal_kinematic_target[[0, 3], 0, 3] = 0.0
        # 시뮬레이터에 업데이트
        cube_object.write_nodal_kinematic_target_to_sim(nodal_kinematic_target)

        # 추가 데이터 시뮬레이터에 기록
        cube_object.write_data_to_sim()
        # 시뮬레이터 한 스텝 실행
        sim.step()
        # 시간 및 카운터 업데이트
        sim_time += sim_dt
        count += 1
        # 객체 내부 버퍼 업데이트
        cube_object.update(sim_dt)

        # 50 스텝마다 루트 위치 출력
        if count % 50 == 0:
            print(f"Root position (in world): {cube_object.data.root_pos_w[:, :3]}")


def main():
    """메인 함수: 시뮬레이터 설정, 씬 구성, 시뮬레이션 실행"""
    sim_cfg = sim_utils.SimulationCfg(device=args_cli.device)
    sim = SimulationContext(sim_cfg)

    # 카메라 위치 설정
    sim.set_camera_view(eye=[3.0, 0.0, 1.0], target=[0.0, 0.0, 0.5])

    # 씬 구성 및 객체 가져오기
    scene_entities, scene_origins = design_scene()
    scene_origins = torch.tensor(scene_origins, device=sim.device)

    # 시뮬레이터 초기화
    sim.reset()
    print("[INFO]: Setup complete...")

    # 시뮬레이터 루프 실행
    run_simulator(sim, scene_entities, scene_origins)


# 실행 진입점
if __name__ == "__main__":
    main()
    simulation_app.close()
image 37

5. run_articulation.py

image 38
# Copyright (c) 2022-2025, The Isaac Lab Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""This script demonstrates how to spawn a cart-pole and interact with it.

.. code-block:: bash

    # Usage
    ./isaaclab.sh -p scripts/tutorials/01_assets/run_articulation.py

"""

"""Launch Isaac Sim Simulator first."""



import argparse  # 명령줄 인자 파싱용

from isaaclab.app import AppLauncher  # Isaac Lab 시뮬레이터 실행용 런처 클래스

# argparse 설정
parser = argparse.ArgumentParser(description="Tutorial on spawning and interacting with an articulation.")
AppLauncher.add_app_launcher_args(parser)  # --headless 등 공통 인자 추가
args_cli = parser.parse_args()  # 인자 파싱

# 시뮬레이터 실행
app_launcher = AppLauncher(args_cli)
simulation_app = app_launcher.app  # Isaac Sim 앱 핸들 저장

"""아래부터는 시뮬레이터 실행 이후에 가능한 코드입니다."""

import torch  # 텐서 연산용 (위치, 속도, 관절 상태 등 제어에 사용)

import isaacsim.core.utils.prims as prim_utils  # prim(Xform 등) 생성 유틸
import isaaclab.sim as sim_utils  # 시뮬레이터 설정 유틸
from isaaclab.assets import Articulation  # 관절 기반 로봇 클래스
from isaaclab.sim import SimulationContext  # 시뮬레이터 제어용 클래스

## 사전에 정의된 CartPole 설정 (ArticulationCfg 기반)
from isaaclab_assets import CARTPOLE_CFG  # isort:skip


def design_scene() -> tuple[dict, list[list[float]]]:
    """씬 구성 함수: 바닥, 조명, Xform 그룹, CartPole 로봇 생성"""

    # 바닥 생성
    cfg = sim_utils.GroundPlaneCfg()
    cfg.func("/World/defaultGroundPlane", cfg)

    # 돔 라이트 조명 생성
    cfg = sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
    cfg.func("/World/Light", cfg)

    # Origin 그룹 생성 (Xform): 각각 CartPole을 담을 위치
    origins = [[0.0, 0.0, 0.0], [-1.0, 0.0, 0.0]]
    prim_utils.create_prim("/World/Origin1", "Xform", translation=origins[0])
    prim_utils.create_prim("/World/Origin2", "Xform", translation=origins[1])

    # Articulation 객체 생성 (CartPole): 관절 구성 정보를 담고 있음
    cartpole_cfg = CARTPOLE_CFG.copy()
    cartpole_cfg.prim_path = "/World/Origin.*/Robot"  # 정규표현식으로 여러 개 생성
    cartpole = Articulation(cfg=cartpole_cfg)  # 실제 객체 생성

    # 생성된 객체 반환
    scene_entities = {"cartpole": cartpole}
    return scene_entities, origins


def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articulation], origins: torch.Tensor):
    """시뮬레이션 루프: CartPole 상태를 초기화하고 무작위 제어 명령을 적용"""

    # CartPole 객체 가져오기
    robot = entities["cartpole"]

    # 시뮬레이션 시간 간격 및 카운터
    sim_dt = sim.get_physics_dt()
    count = 0

    # 시뮬레이션 루프
    while simulation_app.is_running():
        # 매 500 스텝마다 상태 초기화
        if count % 500 == 0:
            count = 0  # 카운터 초기화

            # 루트 상태 초기화 후 Origin에 맞게 위치 오프셋 적용
            root_state = robot.data.default_root_state.clone()
            root_state[:, :3] += origins  # 월드 좌표계 기준 위치 보정
            robot.write_root_pose_to_sim(root_state[:, :7])  # 위치 + 회전
            robot.write_root_velocity_to_sim(root_state[:, 7:])  # 선속도 + 각속도

            # 관절 상태 초기화 + 무작위 오프셋 추가
            joint_pos, joint_vel = robot.data.default_joint_pos.clone(), robot.data.default_joint_vel.clone()
            joint_pos += torch.rand_like(joint_pos) * 0.1  # 무작위 초기 위치
            robot.write_joint_state_to_sim(joint_pos, joint_vel)  # 관절 상태 반영

            robot.reset()  # 내부 버퍼 초기화
            print("[INFO]: Resetting robot state...")

        # 무작위 관절 토크 생성 및 적용
        efforts = torch.randn_like(robot.data.joint_pos) * 5.0  # 무작위 토크 값 (스케일링 포함)
        robot.set_joint_effort_target(efforts)  # 목표 토크 설정
        robot.write_data_to_sim()  # 시뮬레이터에 반영

        # 시뮬레이션 한 스텝 진행
        sim.step()
        count += 1  # 카운터 증가

        # 내부 버퍼 업데이트 (관절 상태 동기화)
        robot.update(sim_dt)


def main():
    """메인 함수: 시뮬레이터 설정, 씬 구성, 시뮬레이션 실행"""
    sim_cfg = sim_utils.SimulationCfg(device=args_cli.device)
    sim = SimulationContext(sim_cfg)

    # 카메라 위치 설정 (옆에서 CartPole 2개가 잘 보이도록)
    sim.set_camera_view([2.5, 0.0, 4.0], [0.0, 0.0, 2.0])

    # 씬 구성 및 원점 좌표 가져오기
    scene_entities, scene_origins = design_scene()
    scene_origins = torch.tensor(scene_origins, device=sim.device)  # 텐서로 변환

    # 시뮬레이터 초기화
    sim.reset()
    print("[INFO]: Setup complete...")

    # 시뮬레이션 루프 실행
    run_simulator(sim, scene_entities, scene_origins)


if __name__ == "__main__":
    # 메인 함수 실행
    main()
    # 시뮬레이터 종료
    simulation_app.close()
image 39

0 Comments

Leave a Reply