# URDF 예제: 단순 로봇 모델 작성

#### URDF 모델 개요

ROS2 환경에서 로봇 모델을 표현하기 위해 주로 사용하는 포맷은 URDF(Universal Robot Description Format)이다. URDF는 XML 기반의 파일 구조로, 로봇의 링크(link)와 조인트(joint)을 계층적으로 기술한다. URDF를 사용하면 다음과 같은 정보들을 효율적으로 담을 수 있다.

* 로봇의 각 부품에 대한 기하학적 형태(Geometry)
* 부품의 물리적 특징(질량, 관성 모멘트 등)
* 부품 간 연결 관계(조인트 유형, 좌표계 변환, 제한사항 등)
* 시각화 정보(메시, 색깔, 텍스처 등)
* 충돌 영역 정의

단순 로봇 모델을 작성해보는 예제를 통해 URDF를 구성하는 기초 방법과 주의할 점을 살펴보자. 이 예제에서는 최소한의 링크와 조인트를 갖춘 로봇을 간단히 구성한다.

#### URDF의 주요 태그 구조

URDF는 대체로 아래와 같은 태그 구조를 가진다.

```xml
<robot name="robot_name">
  <link name="link_1">
    <!-- link_1에 대한 inertial, visual, collision 등이 들어감 -->
  </link>

  <link name="link_2">
    <!-- link_2에 대한 inertial, visual, collision 등이 들어감 -->
  </link>

  <joint name="joint_1" type="revolute">
    <!-- link_1 과 link_2를 연결하는 조인트의 정보 -->
  </joint>

  ...
</robot>
```

* `<robot>` : 로봇 모델 전체를 감싸는 루트 태그
* `<link>` : 하나의 링크(몸체)를 정의하는 태그
* `<joint>` : 두 링크를 연결하는 조인트(조인트)를 정의하는 태그

#### 링크(link)와 조인트(joint)의 기본 개념

* **링크(link)**: 로봇을 구성하는 기본적인 몸체 단위이다. URDF 상에서 하나의 `<link>` 태그는 3D 공간에서 표현되는 물리적인 물체 하나를 의미한다.
* **조인트(joint)**: 두 링크 사이의 상대적인 움직임을 정의한다. URDF에서 조인트의 종류에는 revolute, prismatic, fixed, continuous 등이 있으며, 회전이나 직선 운동을 허용하는 메커니즘의 차이에 따라 달라진다.

#### 링크를 정의할 때 포함되는 정보

링크를 정의할 때는 주로 다음과 같은 태그를 사용한다.

```xml
<link name="link_name">
  <inertial>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <mass value="1.0"/>
    <inertia 
      ixx="1.0" ixy="0.0" ixz="0.0"
      iyy="1.0" iyz="0.0" izz="1.0"/>
  </inertial>

  <visual>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <geometry>
      <box size="1 1 1"/>
    </geometry>
    <material name="gray"/>
  </visual>

  <collision>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <geometry>
      <box size="1 1 1"/>
    </geometry>
  </collision>
</link>
```

* `<inertial>` : 링크의 관성 정보(질량과 관성 모멘트 등). 시뮬레이션이나 동역학 계산에서 중요하다.
* `<visual>` : 시각화를 위한 3D 모델 요소. 주로 CAD에서 추출한 메시(mesh)를 사용하거나, 간단히 `<box>`, `<cylinder>`, `<sphere>`와 같이 기본 도형으로 표현할 수 있다.
* `<collision>` : 충돌 계산용 기하학 정보. `<visual>`과 거의 동일하게 정의하지만, 시뮬레이션 시 빠른 충돌 검사 등을 위해 단순화된 형태(예: 단순 박스)로 구성하기도 한다.

**관성 모멘트(inertia) 계산 간단 예시**

만약 균질 박스(질량이 균일하게 분포된 직육면체)라면, 질량이 $m$, 박스의 가로, 세로, 높이가 각각 $a$, $b$, $c$라 할 때, 중심에 대한 관성 텐서는 다음과 같이 표현된다.

$$
\mathbf{I} = \begin{bmatrix}  \frac{m}{12} (b^2 + c^2) & 0 & 0\ 0 & \frac{m}{12} (a^2 + c^2) & 0 \ 0 & 0 & \frac{m}{12} (a^2 + b^2) \end{bmatrix}
$$

URDF에서는 해당 값을 태그의 속성으로 집어넣으면 된다. 일반적으로 $ixx$, $iyy$, $izz$, $ixy$, $ixz$, $iyz$ 형태로 표기하므로, 위 행렬에 따라 필요한 성분만 기재하면 된다.

#### 조인트(joint)를 정의할 때 포함되는 정보

조인트는 크게 다음과 같은 정보를 포함한다.

```xml
<joint name="joint_name" type="revolute">
  <parent link="link_1"/>
  <child link="link_2"/>
  <origin xyz="0 0 0" rpy="0 0 0"/>
  <axis xyz="0 0 1"/>
  <limit lower="0" upper="1.57" effort="10" velocity="1.0"/>
</joint>
```

* `<parent>`와 `<child>` : 해당 조인트가 어떤 링크들을 연결하는지 표현
* `<origin>` : 부모 링크 좌표계 대비 조인트의 위치와 자세를 정의
* `<axis>` : 회전축 혹은 이동축을 정의 (회전이나 직선 운동 방향)
* `<limit>` : 회전 가능한 범위, 작동 범위, 속도 제한 등을 기술

여기서 `type="revolute"`는 회전 조인트를 의미하며, `type="prismatic"`는 직선 운동 조인트, `type="fixed"`는 링크 간 고정 조인트, `type="continuous"`는 2$\pi$ 주기로 계속해서 회전할 수 있는 조인트를 의미한다.

#### 단순 2-링크 로봇 예제

이번에는 링크 2개와 조인트 1개로 이루어진 아주 간단한 로봇을 예로 들어 URDF를 작성해보자. 다음 조건을 가정하자.

* **Link1**: 기준이 되는 고정 링크(base\_link)로 설정
* **Link2**: Link1과 revolute 조인트로 연결
* **Joint1**: Link1과 Link2를 연결하는 회전 조인트

아래 예시는 Link1과 Link2에 각각 박스 형태의 기하학 정보를 부여하고, Link2가 Link1을 기준으로 회전할 수 있도록 정의한다.

```xml
<robot name="simple_two_link">
  <!-- 1) Link1 정의 -->
  <link name="Link1">
    <inertial>
      <origin xyz="0 0 0" rpy="0 0 0" />
      <mass value="1.0" />
      <inertia 
        ixx="0.1" ixy="0.0" ixz="0.0"
        iyy="0.1" iyz="0.0" izz="0.1" />
    </inertial>

    <visual>
      <origin xyz="0 0 0" rpy="0 0 0" />
      <geometry>
        <box size="0.2 0.2 0.2" />
      </geometry>
      <material name="blue" />
    </visual>

    <collision>
      <origin xyz="0 0 0" rpy="0 0 0" />
      <geometry>
        <box size="0.2 0.2 0.2" />
      </geometry>
    </collision>
  </link>

  <!-- 2) Link2 정의 -->
  <link name="Link2">
    <inertial>
      <origin xyz="0 0 0" rpy="0 0 0" />
      <mass value="1.0" />
      <inertia 
        ixx="0.1" ixy="0.0" ixz="0.0"
        iyy="0.1" iyz="0.0" izz="0.1" />
    </inertial>

    <visual>
      <origin xyz="0 0 0" rpy="0 0 0" />
      <geometry>
        <box size="0.2 0.4 0.2" />
      </geometry>
      <material name="green" />
    </visual>

    <collision>
      <origin xyz="0 0 0" rpy="0 0 0" />
      <geometry>
        <box size="0.2 0.4 0.2" />
      </geometry>
    </collision>
  </link>

  <!-- 3) Joint1 정의 -->
  <joint name="Joint1" type="revolute">
    <parent link="Link1" />
    <child link="Link2" />
    <origin xyz="0 0.2 0" rpy="0 0 0" />
    <axis xyz="0 0 1" />
    <limit lower="0" upper="1.57" effort="5.0" velocity="1.0" />
  </joint>
</robot>
```

위와 같은 URDF는 다음과 같은 구조를 가진다.

* **Link1**: 0.2m 정육면체 크기의 링크. 관성 정보는 단순 예시 값으로 설정
* **Link2**: 0.2 × 0.4 × 0.2m 크기의 링크. 마찬가지로 관성 정보는 예시 값
* **Joint1**: Link1과 Link2 사이에 회전 조인트를 정의. `xyz="0 0.2 0"` 설정으로 인해 Link2의 기준 좌표는 Link1의 상부 쪽에 위치하게 된다. `axis="0 0 1"`이므로 $z$ 축을 중심으로 회전한다.

#### 모델 시각화 구조 이해

이 URDF 모델을 RViz나 Gazebo 등에서 시각화하면, Link1의 윗면(또는 한쪽 면) 근처에 Link2가 장착되어 있으며, $z$ 축을 중심으로 회전 가능하게 표현된다. 조인트 제한값(`<limit lower="0" upper="1.57" ... />`)에 따라 Link2는 0에서 1.57 라디안(약 90도) 범위로 회전 가능하다.

이를 좀 더 직관적으로 이해하기 위해 다이어그램으로 나타내면 다음과 같다.

{% @mermaid/diagram content="flowchart LR
A\[Link1] -->|"Joint1 (revolute)"| B\[Link2]" %}

여기서 A(=Link1)는 고정 기반(base) 역할을 하고, B(=Link2)는 회전하며 상대적인 위치가 달라진다.

#### URDF 파일 구성 시 주의 사항

단순한 예제라도 URDF를 작성할 때에는 아래 사항을 염두에 두어야 한다.

**좌표계 기준 통일**:

* 각 링크와 조인트의 `<origin>` 태그에 기술되는 `xyz`, `rpy`는 로컬 좌표계를 기준으로 한다.
* 기계를 설계하거나 CAD 모델을 사용할 때는 좌표계가 서로 다른 경우가 많으므로, URDF에 적용하기 전에 좌표계를 정해놓고 일관성 있게 적용해야 한다.

**기본 단위 통일**:

* URDF에서 거리 단위는 기본적으로 미터(m)를 권장한다.
* 질량은 킬로그램(kg), 관성 모멘트는 kg·m² 등 국제단위계를 일관되게 사용한다.

**관성 모멘트(inertia)의 정확성**:

* 시뮬레이션 및 동역학 계산 정확도를 높이려면, 실제 로봇의 CAD 모델 등에서 관성 텐서를 정확히 추출하거나, 합리적인 근사치를 사용해야 한다.
* 만약 관성 모멘트가 부정확하면 시뮬레이션이 불안정해질 수 있다.

**충돌 모델(collision) 단순화**:

* 시뮬레이션에서 로봇 간 충돌을 판단하기 위해 실제 모델 그대로 복잡한 메시(mesh)를 사용하면 연산량이 급격히 증가할 수 있다.
* 가능하면 충돌 영역(collision geometry)은 단순 기하학 도형(box, cylinder 등)으로 구성해 시뮬레이션 성능을 향상시키는 것이 좋다.

**조인트 축(axis) 정의**:

* 회전 조인트(revolute)의 경우, 실제 회전축과 정확히 일치해야 한다.
* 축 방향 설정이 잘못되면 시뮬레이션 상에서 의도한 동작과 전혀 다른 결과를 초래할 수 있다.

#### ROS2 환경에서 URDF 로드하기

URDF로 작성된 로봇 모델을 ROS2 환경에서 시각화하거나 활용하려면, 일반적으로 다음 과정을 거친다.

**robot\_description 파라미터 설정**:

* URDF 파일 내용을 ROS2 파라미터에 로드하여 노드들이 로봇 모델 정보를 공유할 수 있도록 한다.
* 주로 `robot_state_publisher`를 통해 로봇의 TF(Transform) 정보를 퍼블리시한다.

**robot\_state\_publisher 사용**:

* ROS2에서 `robot_state_publisher` 노드는 URDF(또는 Xacro)를 읽고, 각 링크 간 좌표계를 생성하여 TF 트리를 방송한다.
* 조인트 상태를 `joint_state_publisher`(혹은 실제 하드웨어 드라이버에서 제공하는 토픽)로부터 받아서 TF를 갱신한다.

아래와 같은 방식으로 `robot_description` 파라미터를 설정하고, `robot_state_publisher` 노드를 실행할 수 있다.

```bash
# 예: URDF 파일을 로드하여 robot_description 파라미터에 설정
ros2 param set /robot_state_publisher robot_description --file path/to/simple_two_link.urdf
```

> **참고**: 실제로는 launch 파일을 사용하여 `robot_description`을 한 번에 설정하고, `robot_state_publisher`, `joint_state_publisher` 등을 함께 실행하는 경우가 많다.

#### RViz에서 시각화

이제 ROS2에 로드된 로봇 모델을 RViz에서 시각화해볼 수 있다.

* RViz2를 실행:

```bash
ros2 run rviz2 rviz2
```

* RViz2의 Displays 메뉴에서 **RobotModel**을 추가하고, `Robot Description` 파라미터를 `robot_description`으로 설정한다.
* 올바르게 로딩되면, RViz 상에서 Link1과 Link2로 구성된 단순 로봇 모델이 확인된다.

#### Gazebo 시뮬레이션에서 로봇 모델 사용하기

작성한 URDF 모델을 Gazebo에서 시뮬레이션하려면, 일반적으로 다음 과정이 필요하다.

**Gazebo 플러그인(Plugin) 설정**:

* URDF 내에서 Gazebo 관련 태그(`<gazebo>` 태그)를 추가하면 시뮬레이션 동작 시 필요한 설정(물리 엔진 파라미터, 센서 플러그인 등)을 명시할 수 있다.
* 예: `<gazebo> <plugin name="..."> ... </plugin> </gazebo>`

**Launch 파일 구성**:

* ROS2 패키지 내에 launch 파일을 작성해 Gazebo를 호출하고, 필요한 ROS2 노드(예: robot\_state\_publisher, controllers 등)를 동시에 실행시킨다.

**URDF vs. SDF**:

* Gazebo는 내부적으로 SDF(Simulation Description Format)을 주로 사용한다.
* ROS2 환경에서는 URDF(또는 Xacro)로 작성한 후, Gazebo 쪽에서 이를 자동 변환해주는 과정을 거쳐 사용할 수 있다.

예시로, `simple_two_link.urdf`를 Gazebo에서 불러와 시뮬레이션하는 코드는 아래와 같은 형태로 구성할 수 있다.

```xml
<launch>
  <arg name="urdf_file" default="$(find-pkg-share my_robot_description)/urdf/simple_two_link.urdf"/>

  <!-- robot_state_publisher 노드 실행 -->
  <node pkg="robot_state_publisher" exec="robot_state_publisher" output="screen">
    <param name="robot_description" command="cat $(var urdf_file)" />
  </node>

  <!-- Gazebo 시뮬레이터 실행 -->
  <node pkg="gazebo_ros" exec="gazebo" output="screen">
    <arg name="world" value="$(find-pkg-share gazebo_ros)/worlds/empty.world"/>
  </node>
</launch>
```

> **참고**: 실제로는 `.launch.py` 형태의 파이썬 기반 ROS2 Launch 파일을 사용하는 경우가 많다.

#### URDF와 Xacro

URDF 파일을 직접 작성하기보다는, 매크로 기능을 제공하는 Xacro를 쓰면 반복되는 링크 또는 복잡한 매개변수를 효율적으로 관리할 수 있다.

* **Xacro**: XML 매크로 언어로, URDF 작성을 쉽게 해주는 툴.
* 파라미터화가 가능하여, 링크 크기나 관성, 색상 등을 인자로 받아 반복적으로 생성할 수 있다.
* `xacro` 명령으로 `.xacro` 파일을 `.urdf`로 변환할 수 있다.

예를 들어, 똑같은 링크 형태를 여러 개 복사/붙여넣기 해야 하는 상황에서 Xacro 매크로를 이용하면 관리가 용이하다.

```xml
<xacro:macro name="my_simple_link" params="name size mass color">
  <link name="${name}">
    ...
    <visual>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
        <box size="${size}"/>
      </geometry>
      <material name="${color}"/>
    </visual>
    ...
  </link>
</xacro:macro>
```

이처럼 매크로를 정의한 후, 여러 링크를 반복해서 생성할 때 인자만 달리 전달하면 된다.
