# 스태틱(static) 변환 vs 동적 변환

#### 변환이란 무엇인가

ROS2의 tf2 패키지는 서로 다른 좌표계(프레임) 사이의 공간적 관계(변환, transform)를 실시간으로 관리하고 조회하는 기능을 제공한다. 이를 통해 로봇 시스템 내의 다양한 센서나 구동 장치 등에서 얻어지는 좌표들을 통일된 기준으로 변환하여 활용할 수 있다. 예를 들어, 카메라에서 인식된 물체의 좌표를 로봇 베이스(base) 프레임으로 바꾸거나, 로봇 엔드이펙터(end-effector) 프레임에서 물체의 위치를 다시 월드(world) 프레임으로 변환하는 등의 작업이 tf2를 통해 자동으로 이뤄진다.

일반적으로 프레임 변환은 3차원 회전과 평행 이동으로 구성되며, 다음과 같이 $4 \times 4$ 동차변환 행렬(homogeneous transformation matrix)로 표현할 수 있다:

$$
^{A}T\_{B} =  \begin{bmatrix} ^{A}R\_{B} & ^{A}\mathbf{p}\_{B} \ \mathbf{0}^\mathsf{T} & 1 \end{bmatrix}
$$

* $^{A}R\_{B}$: 프레임 A에서 프레임 B로 가는 3차원 회전 행렬($3 \times 3$)
* $^{A}\mathbf{p}\_{B}$: 프레임 A에서 프레임 B의 원점까지의 3차원 위치 벡터($3 \times 1$)
* $\mathbf{0}^\mathsf{T}$: $\begin{bmatrix}0 & 0 & 0\end{bmatrix}$ 형태의 $1 \times 3$ 벡터

tf2는 이러한 변환을 내부에서 누적·관리하며, 특정 시점에 원하는 프레임 간의 변환을 얻을 수 있도록 해준다.

#### 스태틱(static) 변환의 개념

스태틱 변환은 말 그대로 “고정된 변환”이다. 로봇 시스템에서 어떤 두 프레임 사이의 상대적 관계가 시간에 따라 변하지 않는다면, 이를 스태틱 변환이라 부른다. 예를 들어 로봇 상체에 장착된 카메라 프레임과 로봇 베이스 프레임이 서로 일정한 위치·방향을 유지한다면, 이 둘 사이의 변환은 “정적인(static) 변환”으로 간주할 수 있다.

tf2 환경에서 스태틱 변환을 관리하는 대표적인 방법은 `static_transform_publisher` 노드를 사용하는 것이다. ROS2에서 스태틱 변환을 퍼블리시하는 명령 예시는 다음과 같다:

```
ros2 run tf2_ros static_transform_publisher \
  0 0 1 0 0 0 base_link camera_link
```

위 명령은 `base_link` 프레임과 `camera_link` 프레임 사이에, $x=0$, $y=0$, $z=1$만큼 평행 이동하고 회전은 없는(roll=0, pitch=0, yaw=0) 변환을 영구적으로 방송(broadcast)한다. 이 변환은 시간에 따라 전혀 변하지 않는다.

**스태틱 변환의 장점**

* **시스템 부담이 적다**: 매 시간 변환을 다시 계산할 필요가 없으므로 CPU 부하가 거의 없다.
* **설정이 단순하다**: 한 번 정의하면 특별한 이유가 없는 한 계속 유지되므로 관리가 편리하다.
* **명확한 의미**: 물리적으로 고정된 두 좌표계를 연결할 때 사용하므로 로봇 하드웨어 구성이 명확해진다.

**스태틱 변환의 주의점**

* **예외적인 움직임**: 만약 하드웨어 특성상 실제로는 조금씩 움직이거나 틀어질 수 있다면, 이를 반영하지 못한다.
* **노드 종료**: `static_transform_publisher` 노드가 종료되면 해당 변환이 사라질 수 있다. 따라서 실제 운용에서 항상 살아있는 별도의 static transform 노드를 두거나, launch 파일에서 재실행되도록 설정해두어야 한다.

#### 동적 변환의 개념

동적 변환은 시간에 따라 위치·회전이 바뀌는 두 프레임 사이의 관계를 의미한다. 예를 들어, 로봇 조인트 하나가 실제로 회전하는 경우, 그 조인트 프레임은 시간에 따라 계속 다른 자세를 가진다. 이때 조인트 프레임과 로봇 베이스 프레임 사이의 변환은 동적으로 바뀌므로, tf2에서 매 시간 갱신되는 데이터를 퍼블리시해야 한다.

동적 변환을 퍼블리시하는 대표적인 예로는 로봇 조인트 상태를 받아 URDF(Universal Robot Description Format) 기반으로 TF 변환을 계산해주는 로봇 상태 퍼블리셔(robot\_state\_publisher) 노드가 있다. 이 노드는 내부적으로 로봇 각 조인트 값(조인트 값)을 읽어들여, 해당 값을 근거로 각 링크(link) 프레임 사이의 변환을 주기적으로 업데이트한다.

**동적 변환의 특징**

* **실시간 갱신**: 센서나 모터 제어기로부터 주기적으로 조인트값이나 위치정보를 수신하여 변환을 업데이트한다.
* **계산 비용**: 매번 새로운 변환 행렬을 계산하고 TF를 퍼블리시해야 하므로 시스템 부하가 스태틱 변환보다 크다.
* **움직임 반영**: 실제 로봇 조인트 또는 센서가 움직이는 상황을 즉시 반영하므로 동적인 상황을 처리할 수 있다.

**동적 변환 예시**

예를 들어 로봇의 각 조인트 상태가 매 10ms마다 갱신된다고 하자. 그때마다 로봇 상태 퍼블리셔나 해당 조인트 상태를 관리하는 노드는 다음과 같은 변환 행렬을 계속 업데이트한다:

$$
T\_{\text{joint}\_i} (t)
========================

\begin{bmatrix} R(\theta\_i(t)) & \mathbf{p}(\theta\_i(t))
\\
\mathbf{0}^\mathsf{T} & 1
\end{bmatrix}
$$

* $\theta\_i(t)$: 시간 $t$에서의 $i$번째 조인트 회전량(또는 리니어 조인트 변위)
* $R(\theta\_i(t))$: $\theta\_i(t)$에 따른 3차원 회전 행렬
* $\mathbf{p}(\theta\_i(t))$: $\theta\_i(t)$에 따른 이동 벡터

로봇 전체의 kinematic chain이 복수의 조인트로 이어져 있다면, 각 조인트마다 이러한 변환을 적용해 전체 링크 변환을 순차적으로 누적할 수 있다.

#### 스태틱 변환과 동적 변환의 혼합 활용

현실적인 로봇 시스템에서는 대부분 스태틱 변환과 동적 변환을 동시에 사용한다. 예를 들어, 로봇 몸체에 고정된 카메라 프레임이나 LiDAR 센서 프레임 등은 스태틱 변환을 통해 베이스 프레임과 연결하고, 실제 움직이는 조인트나 3D 스캔 장비(팬-틸트 등)는 동적 변환을 통해 위치 정보를 지속적으로 갱신한다.

스태틱 변환과 동적 변환이 혼합되는 대표적인 구조는 아래와 같이 표현할 수 있다:

{% @mermaid/diagram content="graph LR;
A\[world] --> B\[base\_link];
B --> C\["camera\_link (static)"];
B --> D\["joint1 (dynamic)"];
D --> E\["joint2 (dynamic)"];
E --> F\["sensor\_link (static)"];" %}

* $\text{world} \rightarrow \text{base\_link}$: 보통 로봇 전체가 차지하는 기준 프레임. 고정 혹은 동적(로봇 자율주행 시)에 따라 달라질 수 있음.
* $\text{base\_link} \rightarrow \text{camera\_link}$: 로봇 베이스에 장착된 카메라. 상대적 움직임이 없으므로 스태틱 변환.
* $\text{base\_link} \rightarrow \text{joint1} \rightarrow \text{joint2} \rightarrow \text{sensor\_link}$: 실제로 조인트가 회전하거나 이동할 수 있는 구조이므로 동적 변환. 단, 최종적으로 센서가 각 조인트에 고정되어 있다면, 센서 프레임은 조인트 프레임에 대해서는 스태틱 변환이 될 수 있다.

이처럼 로봇 시스템 전체를 구성하는 다양한 링크(링크-프레임)들 중에서 “움직이는 부분”은 동적 변환, “움직이지 않는 부분”은 스태틱 변환으로 처리해야 tf2가 올바르고 효율적으로 동작한다.

#### 스태틱 변환 설정 시 주의사항

1. **올바른 프레임 명으로 퍼블리시하기** tf2에서 각 변환은 “부모 프레임(parent)”와 “자식 프레임(child)” 명으로 관리된다. 스태틱 변환을 설정할 때는 실제 하드웨어나 URDF 등에서 정의한 프레임 명을 정확히 일치시켜야 한다.
2. **부모-자식 관계의 일관성** 스태틱 변환은 한 번 정하면 변하지 않기 때문에, 실제 관계(부품 조립 순서 등)를 잘못 정의하면 이후 로봇 상태 퍼블리셔나 다른 tf2 사용자들이 혼동을 겪는다.
3. **좌표계의 축 방향 확인** 특히 센서류(카메라, LiDAR 등)는 “Z축이 전방을 가리키는지”, “X축이 진행 방향인지” 등의 좌표계 정의가 제각각이므로 주의 깊게 확인해야 한다. 이를 헷갈리면 나중에 데이터를 해석할 때 방향이 뒤집히거나 크게 어긋날 수 있다.

#### 동적 변환 설정 시 주의사항

1. **주기적 퍼블리시 주기(Hz) 조정** 동적 변환은 계속해서 새 변환을 퍼블리시해야 한다. 너무 높은 빈도로 퍼블리시하면 시스템 부하가 올라가고, 너무 낮으면 조인트 움직임 반영이 느려져서 로봇 동작 정밀도가 떨어질 수 있다. 적절한 퍼블리시 주기를 선택해야 한다.
2. **시간 동기화(Time stamp) 처리** tf2는 변환에 타임스탬프(timestamp)를 부여하여 특정 시점의 공간적 관계를 조회하는 구조다. 동적 변환을 퍼블리시할 때는 센서나 모터 제어에서 얻은 시간 정보와 일치하도록 타임스탬프를 설정해야 정확한 시점의 변환 조회가 가능하다.
3. **누적 오차 관리** 실제 로봇 조인트는 엔코더(encoder) 오차나 기계적 백래시(backlash) 등으로 인해 근소한 에러가 누적될 수 있다. 동적 변환이 실제와 조금씩 달라질 수 있으며, 이를 캘리브레이션(calibration)이나 외부 센서를 통해 보정해 주어야 한다.

#### 예제: URDF와 스태틱·동적 변환

ROS2에서 로봇 모델(URDF나 Xacro 파일)을 정의하면, 일반적으로는 다음 두 가지 방식으로 스태틱 변환과 동적 변환을 함께 관리한다.

1. **robot\_state\_publisher**
   * URDF 파일 내에서 모든 링크와 조인트(조인트)를 정의한다.
   * 링크 간의 상위-하위 관계(프레임 관계)가 이미 URDF상에 반영되어 있으며, 조인트 타입(revolute, prismatic 등)에 따라 동적 변환으로 처리된다.
   * “고정 조인트(fixed joint)”인 경우 URDF가 이를 스태틱 변환으로 본다.
2. **launch 파일 + static\_transform\_publisher**
   * URDF에는 동적 변환이 발생하는 로봇 조인트만 정의한다.
   * 센서류나 기타 고정 프레임은 launch 파일에서 `static_transform_publisher`를 통해 직접 퍼블리시한다.
   * 로봇 URDF 상에서 정의되지 않은 별도의 주변 장치(예: 작업 공간에 설치된 고정 카메라 등)도 이 방식으로 변환을 제공할 수 있다.

#### TF 디버깅 및 상태 확인 도구 활용

스태틱 변환과 동적 변환이 적절히 설정되었는지 점검하기 위해서는, tf2에서 제공하는 여러 디버깅 툴을 활용할 수 있다.

**tf2\_echo**:

* 특정 두 프레임 간 변환을 CLI에서 실시간으로 확인할 수 있는 유틸리티
* 예:

```
ros2 run tf2_ros tf2_echo base_link camera_link
```

이를 통해 `base_link`와 `camera_link` 간의 변환이 잘 퍼블리시되고 있는지, 시간 정보나 오리엔테이션(roll, pitch, yaw), 위치 벡터 등이 올바른지 확인 가능

**tf2\_monitor**:

* 전체 TF 트리를 모니터링하여 각 프레임 간의 변환에 대한 퍼블리시 주기, 지연(latency), 누락 여부 등을 종합적으로 확인
* 예:

```
ros2 run tf2_tools tf2_monitor
```

이를 통해 어떤 프레임 변환이 제때 퍼블리시되지 않는지, 혹은 일부 변환이 오래된 정보인지를 실시간으로 점검 가능

**RViz2 TF 플러그인**:

* RViz2에서 TF를 시각적으로 확인할 수 있는 플러그인을 활용하면, 3D 화면상에서 각 프레임이 어떻게 배치되어 있는지 직관적으로 파악 가능
* 스태틱 변환(정적인 링크)과 동적 변환(움직이는 링크)이 의도대로 움직이는지, 그리고 축 방향이 맞는지 시각적으로 검증할 수 있어 매우 편리

**rqt\_tf\_tree**:

* rqt 플러그인으로 제공되는 TF 트리 플러그인을 통해, 실제 퍼블리시되고 있는 프레임 간 계층구조를 그래프 형태로 확인 가능
* 만약 의도하지 않은 “순환(cycle)”이 TF 트리에 존재하거나, 부모-자식 관계를 잘못 설정했다면 시각적으로 쉽게 드러난다

#### 스태틱 변환과 동적 변환을 구분하는 기준 재점검

* \*\*“시간에 따른 변환 변화 여부”\*\*가 가장 큰 기준이다.
  * 물리적으로 절대 고정(나사, 볼트 등으로 결합)되어 움직이지 않는다면 스태틱
  * 조인트 회전, 슬라이딩 등으로 계속 바뀌는 관계라면 동적
* **URDF에서 ‘fixed joint’로 정의된 부분**은 일반적으로 스태틱 변환에 해당하지만, 실제 하드웨어가 의도치 않게 약간 움직일 수 있는 구조라면 동적 변환이나 보정 단계를 고려해야 한다.
* 센서 보정, 캘리브레이션
  * 고정된 센서라도 레이저, 카메라 등의 내부 오차나 마운트 위치 오차를 고려해야 할 때가 있다. 이 경우 수동으로 스태틱 변환을 재정의하거나, 자동 캘리브레이션 알고리즘을 적용해 동적으로 변환을 업데이트하기도 한다.

스태틱 변환과 동적 변환은 로봇 시스템에서 서로 보완적인 역할을 하며, 상황에 따라 적절한 선택이 이뤄져야 한다. 이를 위해 tf2 패키지에서 제공하는 퍼블리셔 노드, URDF 기반 로봇 상태 퍼블리셔, 그리고 다양한 디버깅 툴을 적절히 활용하면 된다.
