# SDF에서 Unity로의 변환

#### SDF 파일 포맷 이해

SDF(Simulation Description Format)는 로봇 및 물리 기반 시뮬레이션을 정의하기 위한 XML 기반의 파일 포맷이다. 주로 로봇의 구조와 물리적 특성을 기술하는 데 사용되며, 가제보(Gazebo)와 같은 시뮬레이션 환경에서 널리 사용된다. Unity는 SDF 포맷을 직접적으로 지원하지 않지만, SDF 파일을 Unity로 변환하는 방법을 사용하여 로봇 모델을 가져올 수 있다.

SDF 파일의 주요 구성 요소는 다음과 같다:

* `<world>`: 시뮬레이션 환경을 정의한다. 중력, 시간 단계, 조명 및 기타 환경 설정을 포함한다.
* `<model>`: 로봇이나 물체의 물리적 모델을 정의한다. 모델은 링크와 조인트로 구성된다.
* `<link>`: 모델 내의 개별 파트를 정의하며, 각 파트는 물리적 특성(질량, 관성, 충돌 모양 등)을 갖는다.
* `<joint>`: 링크 사이의 조인트를 정의한다. 조인트는 회전축, 이동축 및 연결된 링크를 명시한다.

#### SDF 파일의 구조적 변환

Unity로의 변환을 위해서는 SDF 파일의 각 구성 요소를 Unity의 구조체로 매핑해야 한다. 주요 변환 대상은 로봇의 각 링크와 조인트이다.

**링크 변환**

SDF의 `<link>`는 Unity의 `GameObject`에 해당한다. SDF에서 로봇의 각 링크는 Unity에서 다음과 같은 구성 요소로 변환된다:

* **질량**: Unity의 `Rigidbody`에서 질량 값을 설정한다. SDF에서는 `<inertial>` 태그를 통해 링크의 질량과 관성 모멘트를 정의할 수 있다. 이를 Unity에서 `Rigidbody.mass`와 관성 텐서 값으로 매핑한다.

  SDF 파일에서의 질량 설정:

  ```xml
  <link name="base_link">
    <inertial>
      <mass>1.0</mass>
      <inertia>
        <ixx>0.1</ixx>
        <iyy>0.1</iyy>
        <izz>0.1</izz>
      </inertia>
    </inertial>
  </link>
  ```

  Unity에서는 이 질량 정보를 다음과 같이 적용한다:

  ```csharp
  Rigidbody rb = gameObject.AddComponent<Rigidbody>();
  rb.mass = 1.0f;
  rb.inertiaTensor = new Vector3(0.1f, 0.1f, 0.1f);
  ```

**링크의 관성 변환**

SDF에서 정의된 관성 행렬은 Unity의 `Rigidbody.inertiaTensor`에 매핑된다. SDF에서 관성 텐서는 $3 \times 3$ 대칭 행렬로 주어진다. 이는 Unity에서 축에 맞는 관성 텐서 값으로 변환된다.

SDF에서의 관성 행렬은 다음과 같이 정의된다:

$$
\mathbf{I} = \begin{pmatrix} I\_{xx} & I\_{xy} & I\_{xz} \ I\_{xy} & I\_{yy} & I\_{yz} \ I\_{xz} & I\_{yz} & I\_{zz} \end{pmatrix}
$$

이 중 대각 성분인 $\mathbf{I\_{xx}}, \mathbf{I\_{yy}}, \mathbf{I\_{zz}}$를 Unity의 `Rigidbody.inertiaTensor`로 변환한다. Unity에서는 직교 좌표계로만 처리하므로, 대각 성분만 사용한다.

SDF에서 정의된 관성 텐서:

```xml
<inertia>
  <ixx>0.1</ixx>
  <iyy>0.1</iyy>
  <izz>0.1</izz>
</inertia>
```

Unity에서 적용 예시:

```csharp
rb.inertiaTensor = new Vector3(0.1f, 0.1f, 0.1f);
```

#### 조인트 변환

SDF에서 정의된 `<joint>`는 Unity에서 `ConfigurableJoint` 또는 `HingeJoint`로 변환할 수 있다. 두 조인트 시스템의 변환은 다음과 같은 기본 절차로 진행된다:

* **조인트 타입**: SDF의 `revolute`, `prismatic`, `fixed` 등의 조인트는 Unity에서 적절한 조인트 타입으로 변환된다. 예를 들어, `revolute` 조인트는 Unity의 `HingeJoint`로 변환된다.

  SDF의 `revolute` 조인트 정의:

  ```xml
  <joint name="joint1" type="revolute">
    <parent>link1</parent>
    <child>link2</child>
    <axis>
      <xyz>0 0 1</xyz>
    </axis>
  </joint>
  ```

  Unity에서의 조인트 설정:

  ```csharp
  HingeJoint joint = gameObject.AddComponent<HingeJoint>();
  joint.axis = new Vector3(0, 0, 1);
  ```

**조인트 제약**

SDF에서 조인트의 회전 각도나 이동 거리 제한이 있다면, Unity에서 이를 설정해야 한다. `HingeJoint`의 경우, 각도 제한을 설정할 수 있다. SDF의 `<limit>` 태그는 Unity의 `limits` 속성으로 변환된다.

SDF에서의 조인트 제약 설정:

```xml
<limit>
  <lower>-1.57</lower>
  <upper>1.57</upper>
</limit>
```

Unity에서의 변환:

```csharp
joint.limits = new JointLimits {
  min = -90,
  max = 90
};
```

#### URDF와 SDF 간의 차이점

SDF와 URDF는 유사한 목적을 가진 파일 포맷이지만, Unity에서의 변환 작업 시 고려해야 할 몇 가지 차이점이 있다. 이 차이점을 이해하는 것이 변환 과정에서 오류를 최소화하는 데 중요하다.

* **SDF의 계층 구조**: SDF는 더 복잡한 계층 구조를 지원하며, 여러 개의 `<model>`을 포함할 수 있다. URDF는 하나의 로봇에 대한 정의만을 포함하는 반면, SDF는 하나의 파일 내에서 여러 로봇을 정의하거나 서로 다른 환경 요소를 포함할 수 있다. 따라서 Unity에서 변환할 때 각 모델을 개별 `Prefab`으로 처리하는 것이 필요할 수 있다.
* **물리 엔진의 설정 차이**: SDF는 URDF보다 더 세밀한 물리 엔진 설정을 제공한다. 예를 들어, SDF는 각 모델의 물리적 속성(예: 마찰력, 감쇠 등)을 더 구체적으로 정의할 수 있다. 이와 같은 세부 물리 속성을 Unity로 변환할 때는 Unity의 `PhysicMaterial`과 같은 요소를 적절히 활용해야 한다.

#### 변환 과정 자동화

SDF 파일에서 Unity로의 변환을 수동으로 하는 것은 매우 비효율적일 수 있으므로, 이 과정을 자동화하는 스크립트를 작성하는 것이 유리한다. Python이나 C#을 사용하여 SDF 파일을 파싱한 뒤, Unity의 오브젝트로 변환하는 자동화 도구를 만들 수 있다.

**Python을 통한 SDF 파싱**

SDF 파일을 파싱하기 위해 Python의 `xml.etree.ElementTree` 모듈을 사용할 수 있다. 이 모듈을 사용하면 SDF 파일을 트리 구조로 읽어들여, 각 노드를 순차적으로 파싱할 수 있다.

SDF 파일을 읽고 Unity 변환용 데이터를 추출하는 Python 코드 예시:

```python
import xml.etree.ElementTree as ET

def parse_sdf(sdf_file):
    tree = ET.parse(sdf_file)
    root = tree.getroot()
    
    for model in root.findall('model'):
        model_name = model.get('name')
        print(f"Model: {model_name}")
        
        for link in model.findall('link'):
            link_name = link.get('name')
            mass = link.find('inertial/mass').text
            print(f"  Link: {link_name}, Mass: {mass}")
            
            # 조인트 처리
            for joint in model.findall('joint'):
                joint_name = joint.get('name')
                parent = joint.find('parent').text
                child = joint.find('child').text
                print(f"  Joint: {joint_name}, Parent: {parent}, Child: {child}")

parse_sdf('robot.sdf')
```

이 파싱 코드를 통해 추출된 정보를 Unity에서 사용할 수 있는 포맷으로 변환하는 C# 스크립트와 연결할 수 있다.

**Unity에서 SDF 데이터 적용**

파싱된 SDF 데이터를 Unity에 적용하려면, 파싱 결과를 C#으로 가져와서 Unity 오브젝트를 생성하는 스크립트를 작성해야 한다. 예를 들어, 각 링크에 해당하는 `GameObject`를 생성하고, 물리 속성을 적용할 수 있다.

C#에서 Python 파싱 결과를 사용하는 예시:

```csharp
using UnityEngine;

public class RobotLoader : MonoBehaviour
{
    public void CreateLink(string linkName, float mass)
    {
        GameObject link = new GameObject(linkName);
        Rigidbody rb = link.AddComponent<Rigidbody>();
        rb.mass = mass;
        
        // 다른 물리 속성 추가
    }

    public void CreateJoint(string jointName, string parentName, string childName)
    {
        GameObject parent = GameObject.Find(parentName);
        GameObject child = GameObject.Find(childName);
        HingeJoint joint = child.AddComponent<HingeJoint>();
        joint.connectedBody = parent.GetComponent<Rigidbody>();
        
        // 다른 조인트 속성 추가
    }
}
```

이처럼 SDF 파일에서 로봇의 링크와 조인트를 자동으로 생성하고 Unity에서 물리적으로 시뮬레이션할 수 있는 환경을 만들 수 있다.

#### 충돌 메쉬 및 시각적 메쉬 처리

SDF에서 각 링크는 충돌 메쉬와 시각적 메쉬를 별도로 지정할 수 있다. Unity로 변환할 때, 이를 Unity의 `MeshCollider`와 `MeshRenderer`로 적절히 처리해야 한다. 충돌 메쉬는 물리적 상호작용을 위한 것이며, 시각적 메쉬는 렌더링을 위한 것이다.

**충돌 메쉬 변환**

SDF의 충돌 메쉬는 `<collision>` 태그 아래에 정의된다. 이를 Unity의 `MeshCollider`로 변환할 수 있다. 충돌 메쉬가 복잡할 경우, 물리 성능을 고려하여 간소화하는 작업이 필요할 수 있다.

SDF 충돌 메쉬 정의:

```xml
<collision name="base_collision">
  <geometry>
    <mesh>
      <uri>model://robot/meshes/base_collision.dae</uri>
    </mesh>
  </geometry>
</collision>
```

Unity에서의 변환 예시:

```csharp
MeshCollider collider = link.AddComponent<MeshCollider>();
collider.sharedMesh = Resources.Load<Mesh>("Meshes/base_collision");
```

**시각적 메쉬 변환**

SDF의 시각적 메쉬는 `<visual>` 태그 아래에 정의된다. 이는 Unity에서 `MeshRenderer`와 `MeshFilter`를 사용하여 로봇의 외형을 렌더링하는 데 사용된다.

SDF 시각적 메쉬 정의:

```xml
<visual name="base_visual">
  <geometry>
    <mesh>
      <uri>model://robot/meshes/base_visual.dae</uri>
    </mesh>
  </geometry>
</visual>
```

Unity에서의 변환 예시:

```csharp
MeshFilter meshFilter = link.AddComponent<MeshFilter>();
meshFilter.mesh = Resources.Load<Mesh>("Meshes/base_visual");
MeshRenderer renderer = link.AddComponent<MeshRenderer>();
renderer.material = Resources.Load<Material>("Materials/RobotMaterial");
```

이와 같이, SDF에서 정의된 시각적 및 충돌 메쉬를 각각 적절한 Unity 구성 요소로 변환할 수 있다.
