# 3D 모델(Asset) 불러오기 및 배치

Isaac Sim에서는 다양한 3D 모델을 불러와 로봇 시뮬레이션 환경에서 활용할 수 있다. 이 주제에서는 Isaac Sim을 사용하여 3D 모델을 불러오고, 이를 시뮬레이션 환경에 배치하는 과정을 기초부터 고급까지 다루겠다. 이 과정에는 모델의 파일 형식, 로드 방식, 배치 방법, 그리고 ROS2와의 연동 방식까지 포함된다.

#### 3D 모델 불러오기

3D 모델을 Isaac Sim에 불러오는 과정은 크게 두 가지 단계로 나눠진다: 모델 파일 준비와 모델 파일을 Isaac Sim에서 읽어오는 과정이다.

**모델 파일 준비**

Isaac Sim은 다양한 3D 모델 파일 형식을 지원한다. 대표적인 파일 형식은 다음과 같다:

* **.urdf**: Universal Robot Description Format은 로봇의 물리적 속성, 조인트 및 링크 구조를 정의하는 XML 기반의 파일 형식이다.
* **.obj**: Wavefront OBJ 형식은 3D 모델링 소프트웨어에서 자주 사용되는 표준 파일 형식이다. 텍스처와 형태를 정의하는 데 사용된다.
* **.fbx**: Autodesk의 파일 형식으로, 복잡한 애니메이션, 텍스처, 재질 등을 포함하는 3D 모델 파일 형식이다.
* **.stl**: 3D 인쇄에 많이 사용되는 파일 형식으로, 단순한 3D 표면을 정의한다.

이 중에서, URDF는 ROS2 환경과의 통합을 고려할 때 특히 중요하며, 다른 형식은 시뮬레이션 환경에서 물체를 시각적으로 다룰 때 사용된다.

**모델 파일을 Isaac Sim에서 읽어오기**

Isaac Sim에서는 `gzsdf` (Gazebo SDF) 형식을 사용하여 모델을 로드한다. 이 모델은 물리 엔진과 상호작용할 수 있도록 설계된 메타 데이터와 3D 모델을 포함한다. 모델 파일을 Isaac Sim에 불러오는 방법은 다음과 같다:

```shell
gz model --load <모델_파일>
```

위 명령어에서 `<모델_파일>`은 불러오려는 3D 모델의 경로를 지정하는 부분이다. 이를 통해 Isaac Sim 내에서 모델을 불러오고, 시뮬레이션 환경에 배치할 수 있다.

#### 3D 모델 배치

3D 모델을 불러온 후, 이를 시뮬레이션 환경에 배치하는 과정은 위치, 회전, 크기 등을 조정하는 작업이다. 이 작업을 위해서는 Isaac Sim의 API를 활용하거나 SDF 파일 내에서 위치를 설정할 수 있다.

**위치 및 회전 설정**

Isaac Sim에서는 `pose`를 사용하여 3D 모델의 위치와 회전을 정의한다. Pose는 위치(`position`)와 회전(`orientation`)을 포함하는 6차원 벡터로 구성된다. 이 값을 설정하는 방법은 다음과 같다:

```cpp
#include <ignition/math/Pose3.hh>

// 모델의 위치와 회전 설정
ignition::math::Pose3d modelPose(0, 0, 0, 0, 0, 0); // x, y, z 위치와 roll, pitch, yaw 회전
model->SetWorldPose(modelPose);
```

여기서 `Pose3d`는 모델의 위치와 회전 정보를 담고 있으며, `SetWorldPose` 함수는 해당 모델의 위치를 설정하는 함수이다. `modelPose`는 6차원 공간에서 모델이 배치될 정확한 위치를 나타낸다.

**배치 후 상호작용 설정**

배치된 모델은 물리 엔진과 상호작용할 수 있다. 예를 들어, 중력, 충돌, 움직임 등을 제어할 수 있다. Isaac Sim에서는 `PhysicsEngine`을 사용하여 이러한 물리적 상호작용을 처리할 수 있다.

```cpp
#include <gazebo/physics/World.hh>
#include <gazebo/physics/PhysicsEngine.hh>

gazebo::physics::WorldPtr world = gazebo::physics::get_world();
gazebo::physics::PhysicsEnginePtr engine = world->GetPhysicsEngine();
```

이 코드는 현재 시뮬레이션 세계의 물리 엔진을 가져오는 코드이다. 이후 물리 엔진을 사용하여 배치된 모델에 중력을 적용하거나 충돌을 처리하는 등 다양한 상호작용을 설정할 수 있다.

#### 고급 배치 및 변형

3D 모델의 배치는 기본적인 위치 설정을 넘어서 다양한 변형을 필요로 할 수 있다. 예를 들어, 모델의 크기 조정, 텍스처 변경, 애니메이션 추가 등이 그것이다. 이러한 고급 기능을 구현하기 위해서는 SDF 파일 내에서의 속성 수정이나, ROS2 메시지를 통해 제어할 수 있다.

**크기 조정 및 스케일링**

모델의 크기를 조정하는 작업은 SDF 파일 내에서 `scale` 속성을 수정함으로써 가능한다. 예를 들어, 모델의 크기를 두 배로 확대하고자 할 때는 다음과 같이 설정할 수 있다:

```xml
<model name="example_model">
  <pose>0 0 0 0 0 0</pose>
  <link name="link">
    <visual name="visual">
      <geometry>
        <box>
          <size>1 1 1</size>
        </box>
      </geometry>
      <material>
        <ambient>0.8 0.8 0.8 1</ambient>
      </material>
    </visual>
    <collision name="collision">
      <geometry>
        <box>
          <size>1 1 1</size>
        </box>
      </geometry>
    </collision>
  </link>
  <scale>2 2 2</scale>
</model>
```

위 XML에서는 `scale` 태그를 사용하여 모델의 크기를 조정하고 있다. 이 값을 변경함으로써 모델의 모든 링크와 시각적 요소의 크기가 조정된다.

**텍스처 변경**

모델에 텍스처를 추가하려면 `material` 속성을 사용하여 텍스처 파일을 지정해야 한다. 텍스처 파일은 보통 `.png`, `.jpg`, `.tga` 형식의 이미지 파일을 사용한다. 예를 들어, 모델에 텍스처를 추가하는 코드는 다음과 같다:

```xml
<material>
  <ambient>0.8 0.8 0.8 1</ambient>
  <diffuse>1 1 1 1</diffuse>
  <texture>file://textures/my_texture.png</texture>
</material>
```

이 코드는 모델에 텍스처를 적용하는 방법을 보여주며, `texture` 태그는 텍스처 이미지 파일의 경로를 지정한다.

```xml
  <ambient>0.8 0.8 0.8 1</ambient>
  <diffuse>0.8 0.8 0.8 1</diffuse>
  <specular>0.2 0.2 0.2 1</specular>
  <emissive>0.0 0.0 0.0 1</emissive>
  <textures>
    <image>textures/texture_image.png</image>
  </textures>
</material>
```

위 XML 코드에서는 `material` 요소를 사용하여 모델에 질감을 적용하는 예를 보여준다. `textures` 태그 안에 텍스처 이미지 파일을 지정하면, 해당 모델에 지정된 텍스처가 적용된다.

**애니메이션 추가**

모델에 애니메이션을 추가하는 것은 더 복잡한 작업으로, `ROS2`와 `Isaac Sim`의 통합을 통해 애니메이션을 제어할 수 있다. Isaac Sim에서는 애니메이션을 여러 방식으로 처리할 수 있으며, 예를 들어 `ROS2`에서 발행하는 메시지로 모델의 상태를 제어할 수 있다. 이를 통해 실시간으로 로봇이나 물체의 움직임을 애니메이션처럼 표현할 수 있다.

```cpp
// ROS2 메시지로 애니메이션 제어
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

void animation_callback(const std_msgs::msg::String::SharedPtr msg)
{
  if (msg->data == "rotate")
  {
    // 모델 회전 처리
    ignition::math::Quaterniond rotation(0, 0, 1, 1);
    model->SetWorldPose(model->WorldPose() + rotation);
  }
}

int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);
  rclcpp::Node::SharedPtr node = rclcpp::Node::make_shared("model_animation_node");

  auto subscription = node->create_subscription<std_msgs::msg::String>(
      "/model/animation", 10, animation_callback);

  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}
```

이 C++ 코드 예시에서는 `ROS2` 메시지를 통해 모델에 애니메이션을 제어하는 방식을 보여준다. 메시지가 "rotate"라는 명령을 수신하면, 모델의 회전을 처리하는 동작을 실행한다.

#### 3D 모델과 ROS2 연동

Isaac Sim은 ROS2와의 긴밀한 연동을 지원한다. ROS2의 메시지 및 서비스 시스템을 통해 시뮬레이션 내에서 모델을 제어하거나, ROS2에서의 상태 변화를 Isaac Sim에 반영할 수 있다. 예를 들어, ROS2에서 특정 위치에 모델을 배치하는 메시지를 발행하고, Isaac Sim에서 해당 메시지를 수신하여 모델을 이동시키는 방법은 매우 효과적이다.

**모델 위치 업데이트**

ROS2에서 위치 업데이트 메시지를 통해 Isaac Sim의 모델 위치를 변경하는 방법은 다음과 같다:

```cpp
#include <geometry_msgs/msg/pose_stamped.hpp>
#include <rclcpp/rclcpp.hpp>
#include "gazebo_msgs/srv/set_model_state.hpp"

void update_model_position(const geometry_msgs::msg::PoseStamped::SharedPtr msg)
{
  gazebo_msgs::srv::SetModelState::Request request;
  request.model_state.model_name = "model_name";  // 모델 이름
  request.model_state.pose.position.x = msg->pose.position.x;
  request.model_state.pose.position.y = msg->pose.position.y;
  request.model_state.pose.position.z = msg->pose.position.z;

  // 모델의 회전 및 위치 업데이트 코드 추가
}

int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);
  rclcpp::Node::SharedPtr node = rclcpp::Node::make_shared("model_position_updater");

  auto subscription = node->create_subscription<geometry_msgs::msg::PoseStamped>(
      "/set_model_position", 10, update_model_position);

  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}
```

이 코드는 ROS2에서 모델의 위치를 업데이트하는 서비스를 호출하는 방식이다. `geometry_msgs::msg::PoseStamped` 메시지를 통해 모델의 새로운 위치를 전달하고, 이를 `gazebo_msgs/srv::SetModelState` 서비스로 처리하여 Isaac Sim에서 모델 위치를 실시간으로 변경한다.

#### 고급 기능: 실시간 물리 및 센서 데이터 연동

3D 모델을 시뮬레이션 환경에 배치한 후, 물리 엔진 및 센서 데이터를 실시간으로 활용할 수 있다. 예를 들어, 센서 데이터를 이용해 모델의 동작을 실시간으로 제어하거나, 로봇의 상호작용을 시뮬레이션할 수 있다.

**센서 연동**

Isaac Sim은 LIDAR, 카메라, IMU 등의 센서 데이터를 처리할 수 있는 API를 제공한다. 이러한 센서 데이터를 ROS2 메시지로 연동하면, 실시간으로 로봇과 환경 간의 상호작용을 시뮬레이션할 수 있다.

예를 들어, LIDAR 센서를 사용하여 환경을 스캔하고, 이를 ROS2에서 처리하는 방법은 다음과 같다:

```cpp
#include "sensor_msgs/msg/laser_scan.hpp"
#include "rclcpp/rclcpp.hpp"

void lidar_callback(const sensor_msgs::msg::LaserScan::SharedPtr msg)
{
  // LIDAR 데이터 처리
  for (size_t i = 0; i < msg->ranges.size(); ++i)
  {
    // 각 LIDAR 포인트에 대해 처리
  }
}

int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);
  rclcpp::Node::SharedPtr node = rclcpp::Node::make_shared("lidar_sensor_node");

  auto subscription = node->create_subscription<sensor_msgs::msg::LaserScan>(
      "/scan", 10, lidar_callback);

  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}
```

이 코드는 LIDAR 센서 데이터를 ROS2 메시지로 수신하고 처리하는 예이다. LIDAR 센서로부터의 거리 데이터를 통해 주변 환경을 인식하고, 이를 바탕으로 로봇의 동작을 제어할 수 있다.

#### 3D 모델의 상호작용과 물리적 효과

Isaac Sim에서 3D 모델은 물리 엔진과 상호작용을 할 수 있다. 이는 중력, 충돌, 마찰 등 다양한 물리적 효과를 모델에 적용할 수 있다는 것을 의미한다. 이러한 상호작용을 통해 모델은 더 사실적인 시뮬레이션 환경에서 동작할 수 있다.

**물리 엔진의 설정**

Isaac Sim에서는 Gazebo의 물리 엔진을 사용하여 시뮬레이션 환경에서의 물리적 상호작용을 처리한다. 물리 엔진은 물체 간의 충돌, 중력 효과, 마찰 등 다양한 물리적 현상을 계산하여 시뮬레이션의 리얼리티를 높인다. 이러한 물리 엔진의 설정은 SDF 파일이나 ROS2 메시지를 통해 제어할 수 있다.

예를 들어, `gazebo/physics/PhysicsEngine`를 사용하여 물리 엔진의 파라미터를 설정하고, 시뮬레이션 환경에 영향을 미치는 중력, 마찰 계수 등을 조정할 수 있다.

```cpp
#include <gazebo/physics/World.hh>
#include <gazebo/physics/PhysicsEngine.hh>

// 물리 엔진 설정
gazebo::physics::WorldPtr world = gazebo::physics::get_world();
gazebo::physics::PhysicsEnginePtr engine = world->GetPhysicsEngine();

// 중력 설정
engine->SetGravity(ignition::math::Vector3d(0, 0, -9.8));  // 중력 벡터 설정 (m/s^2)
```

위의 코드는 물리 엔진을 초기화하고, 중력 값을 설정하는 예이다. 중력 벡터를 설정함으로써, 시뮬레이션 환경에서 모든 물체는 중력의 영향을 받게 된다.

**충돌 및 마찰 처리**

3D 모델이 다른 모델과 충돌할 때, 충돌 처리 및 마찰 효과를 적용할 수 있다. 이는 모델의 `collision` 요소와 `material` 속성을 사용하여 설정할 수 있다. 예를 들어, 모델이 충돌할 때 발생하는 마찰력을 설정할 수 있다:

```xml
<collision name="collision">
  <geometry>
    <box>
      <size>1 1 1</size>
    </box>
  </geometry>
  <material>
    <friction>
      <ode>
        <mu>0.9</mu>  <!-- 마찰 계수 -->
        <mu2>0.8</mu2>
      </ode>
    </friction>
  </material>
</collision>
```

위 XML 코드에서는 모델의 충돌 요소에 마찰 계수를 설정하여, 모델 간의 상호작용 시 마찰력을 제어한다. 이 값을 조정함으로써 물체 간의 움직임이나 마찰 효과를 더욱 정교하게 설정할 수 있다.

#### 로봇 제어와 상호작용

Isaac Sim은 3D 모델을 시뮬레이션뿐만 아니라, 로봇 제어에도 사용할 수 있다. ROS2와 통합된 제어 시스템을 사용하여 로봇의 모션을 제어하거나, 외부 이벤트에 반응할 수 있다. 예를 들어, 로봇이 특정 목표 위치로 이동하거나, 장애물을 회피하는 기능을 구현할 수 있다.

**ROS2로 로봇 제어**

ROS2에서는 다양한 메시지와 서비스를 사용하여 로봇의 동작을 제어할 수 있다. 예를 들어, `geometry_msgs::msg::Twist` 메시지를 사용하여 로봇의 선속도 및 각속도를 제어할 수 있다. 이를 통해 로봇이 목표 위치를 향해 이동하도록 할 수 있다.

```cpp
#include "geometry_msgs/msg/twist.hpp"
#include "rclcpp/rclcpp.hpp"

void move_robot(const geometry_msgs::msg::Twist::SharedPtr msg)
{
  // 로봇 이동 제어 코드
  // msg->linear.x: 선속도
  // msg->angular.z: 각속도
}

int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);
  rclcpp::Node::SharedPtr node = rclcpp::Node::make_shared("robot_move_node");

  auto subscription = node->create_subscription<geometry_msgs::msg::Twist>(
      "/cmd_vel", 10, move_robot);

  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}
```

위 코드는 `Twist` 메시지를 통해 로봇을 제어하는 예이다. `linear.x`와 `angular.z`를 사용하여 로봇의 선속도와 각속도를 지정함으로써, 로봇이 원하는 방향으로 움직이도록 할 수 있다.

#### 실시간 피드백 및 데이터 처리

Isaac Sim에서는 실시간으로 센서 데이터를 처리하고 피드백을 제공하는 것이 가능한다. 이를 통해 로봇은 주변 환경을 인식하고, 이를 바탕으로 실시간 의사결정을 내릴 수 있다.

**센서 데이터 처리**

로봇은 LIDAR, 카메라, IMU와 같은 센서 데이터를 실시간으로 수집하고 이를 ROS2 메시지로 발행할 수 있다. 예를 들어, 카메라 데이터를 수집하여 이를 처리하는 코드는 다음과 같다:

```cpp
#include "sensor_msgs/msg/image.hpp"
#include "rclcpp/rclcpp.hpp"

void image_callback(const sensor_msgs::msg::Image::SharedPtr msg)
{
  // 이미지 처리 코드
  // msg->data: 이미지 데이터
}

int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);
  rclcpp::Node::SharedPtr node = rclcpp::Node::make_shared("image_processing_node");

  auto subscription = node->create_subscription<sensor_msgs::msg::Image>(
      "/camera/image", 10, image_callback);

  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}
```

이 코드는 카메라로부터 실시간 이미지를 수신하고 이를 처리하는 예제이다. 카메라 데이터는 `sensor_msgs::msg::Image` 메시지 형식으로 수신되며, 이 데이터를 사용하여 물체 인식이나 경로 추적과 같은 작업을 수행할 수 있다.

***

이번에는 3D 모델을 Isaac Sim에 불러오고 배치하는 과정부터, 물리적 상호작용과 ROS2와의 연동까지 자세히 다뤘습니다. 모델의 크기 조정, 텍스처 변경, 애니메이션 추가, 그리고 센서 데이터를 처리하는 방식까지, 다양한 고급 기능을 활용할 수 있다. 이를 통해 보다 정밀하고 사실적인 시뮬레이션 환경을 구현할 수 있다.
