659.15 TransformStamped 메시지 구조

1. 개요

ROS2의 TF2 좌표 변환 시스템에서 좌표 프레임 간의 변환 정보를 전달하기 위한 핵심 메시지 타입이 geometry_msgs/msg/TransformStamped이다. 이 메시지는 두 좌표 프레임 사이의 공간적 관계를 시간 정보와 함께 캡슐화하며, TF2가 변환 트리(Transform Tree)를 구성하고 유지하는 데 있어 근본적인 데이터 단위로 기능한다. TransformStamped 메시지는 변환의 시간적 유효성, 프레임 관계 식별, 그리고 구체적인 변환 데이터를 하나의 통합된 구조체로 표현함으로써, 분산 로봇 시스템에서의 좌표 변환 관리를 체계화한다.

2. TransformStamped 메시지의 정의

TransformStamped 메시지는 geometry_msgs 패키지에 정의되어 있으며, ROS2 IDL(Interface Definition Language)을 통해 다음과 같이 기술된다.

# geometry_msgs/msg/TransformStamped

std_msgs/Header header
string child_frame_id
geometry_msgs/Transform transform

이 메시지 정의는 세 개의 주요 구성 요소로 이루어진다. 첫째, header 필드는 시간 스탬프와 부모 프레임의 식별자를 포함한다. 둘째, child_frame_id 필드는 자식 프레임의 식별자를 명시한다. 셋째, transform 필드는 부모 프레임에서 자식 프레임으로의 실제 좌표 변환(병진 및 회전)을 기술한다.

3. 메시지 필드 상세 구조

3.1 header 필드

header 필드는 std_msgs/msg/Header 타입으로, 다음과 같은 하위 필드를 포함한다.

# std_msgs/msg/Header

builtin_interfaces/Time stamp
string frame_id
  • stamp: builtin_interfaces/msg/Time 타입으로, 해당 변환이 유효한 시간을 나타낸다. 이 필드는 sec(초, int32)과 nanosec(나노초, uint32)의 두 하위 필드로 구성된다. TF2 버퍼는 이 시간 스탬프를 기반으로 변환의 시간적 유효성을 관리하며, 보간(interpolation) 및 외삽(extrapolation) 연산의 기준점으로 활용한다.
  • frame_id: 변환의 부모 프레임(parent frame) 식별자를 지정하는 문자열이다. TF2의 변환 트리에서 이 프레임이 변환 관계의 출발점(source)이 된다. 예를 들어, "odom", "map", "base_link" 등의 표준 프레임 이름이 사용된다.

3.2 child_frame_id 필드

child_frame_id는 변환의 자식 프레임(child frame) 식별자를 지정하는 문자열이다. 이 필드는 headerframe_id와 함께 변환 트리에서의 부모-자식 관계를 정의한다. TransformStamped 메시지가 표현하는 변환은 header.frame_id 프레임에서 child_frame_id 프레임으로의 좌표 변환을 의미한다.

변환 트리의 구조적 관점에서, header.frame_id는 트리에서 상위 노드에 해당하고, child_frame_id는 하위 노드에 해당한다. 이 관계는 단방향적이며, TF2는 트리의 역방향 변환을 자동으로 계산할 수 있다.

3.3 transform 필드

transform 필드는 geometry_msgs/msg/Transform 타입으로, 실제 좌표 변환 데이터를 담고 있다.

# geometry_msgs/msg/Transform

geometry_msgs/Vector3 translation
geometry_msgs/Quaternion rotation

3.3.1 translation 하위 필드

translationgeometry_msgs/msg/Vector3 타입으로 병진(translation) 변환을 기술한다.

# geometry_msgs/msg/Vector3

float64 x
float64 y
float64 z

이 필드는 부모 프레임의 원점에서 자식 프레임의 원점까지의 3차원 위치 벡터를 나타낸다. 단위는 미터(m)이며, ROS2의 단위 규약(REP 103)을 따른다. 각 성분 x, y, z는 64비트 부동소수점(float64)으로 표현되어 높은 정밀도를 보장한다.

3.3.2 rotation 하위 필드

rotationgeometry_msgs/msg/Quaternion 타입으로 회전(rotation) 변환을 기술한다.

# geometry_msgs/msg/Quaternion

float64 x
float64 y
float64 z
float64 w

쿼터니언은 단위 쿼터니언(unit quaternion)으로 표현되며, \lVert q \rVert = \sqrt{x^2 + y^2 + z^2 + w^2} = 1 조건을 만족하여야 한다. 쿼터니언의 각 성분은 다음과 같다.

  • x, y, z: 쿼터니언의 벡터 부분(허수부)으로, 회전축의 방향 정보를 인코딩한다.
  • w: 쿼터니언의 스칼라 부분(실수부)으로, 회전각의 정보를 인코딩한다.

항등 변환(identity rotation), 즉 회전이 없는 상태는 q = (0, 0, 0, 1)로 표현된다. TF2는 내부적으로 모든 회전 연산에 쿼터니언을 사용하여 짐벌 락(gimbal lock) 문제를 회피하고, 보간 연산에서 구면 선형 보간(SLERP)을 적용할 수 있도록 한다.

4. 메시지 구조의 계층적 구성

TransformStamped 메시지의 전체 계층적 구조를 정리하면 다음과 같다.

TransformStamped
├── header (std_msgs/Header)
│   ├── stamp (builtin_interfaces/Time)
│   │   ├── sec (int32)
│   │   └── nanosec (uint32)
│   └── frame_id (string)           ← 부모 프레임
├── child_frame_id (string)          ← 자식 프레임
└── transform (geometry_msgs/Transform)
    ├── translation (geometry_msgs/Vector3)
    │   ├── x (float64)
    │   ├── y (float64)
    │   └── z (float64)
    └── rotation (geometry_msgs/Quaternion)
        ├── x (float64)
        ├── y (float64)
        ├── z (float64)
        └── w (float64)

이 구조는 총 11개의 원시 데이터 필드(2개의 정수, 7개의 부동소수점, 2개의 문자열)로 구성되며, 직렬화(serialization) 시 일정한 크기의 고정 필드와 가변 길이의 문자열 필드를 포함한다.

5. 변환의 의미론적 해석

TransformStamped 메시지가 표현하는 변환 T는 다음과 같이 해석된다. header.frame_id를 프레임 A, child_frame_id를 프레임 B라 할 때, 이 변환은 프레임 A에서 프레임 B로의 변환 {}^{A}T_{B}를 나타낸다. 구체적으로, 프레임 B에서 표현된 점 \mathbf{p}^{B}를 프레임 A에서의 좌표 \mathbf{p}^{A}로 변환하는 데 사용된다.

\mathbf{p}^{A} = {}^{A}T_{B} \cdot \mathbf{p}^{B}

동차 변환 행렬(homogeneous transformation matrix)로 표현하면 다음과 같다.

{}^{A}T_{B} = \begin{bmatrix} R & \mathbf{t} \\ \mathbf{0}^T & 1 \end{bmatrix}

여기서 R은 쿼터니언 rotation으로부터 도출되는 3 \times 3 회전 행렬이고, \mathbf{t} = (t_x, t_y, t_z)^Ttranslation에 해당하는 병진 벡터이다.

6. 프로그래밍 언어별 사용 예시

6.1 C++ (rclcpp) 사용 예시

#include <geometry_msgs/msg/transform_stamped.hpp>

geometry_msgs::msg::TransformStamped t;

// 시간 스탬프 설정
t.header.stamp = this->get_clock()->now();

// 프레임 관계 설정
t.header.frame_id = "odom";
t.child_frame_id = "base_link";

// 병진 변환 설정 (단위: 미터)
t.transform.translation.x = 1.0;
t.transform.translation.y = 0.5;
t.transform.translation.z = 0.0;

// 회전 변환 설정 (단위 쿼터니언)
t.transform.rotation.x = 0.0;
t.transform.rotation.y = 0.0;
t.transform.rotation.z = 0.0;
t.transform.rotation.w = 1.0;  // 항등 회전

6.2 Python (rclpy) 사용 예시

from geometry_msgs.msg import TransformStamped

t = TransformStamped()

# 시간 스탬프 설정
t.header.stamp = self.get_clock().now().to_msg()

# 프레임 관계 설정
t.header.frame_id = 'odom'
t.child_frame_id = 'base_link'

# 병진 변환 설정 (단위: 미터)
t.transform.translation.x = 1.0
t.transform.translation.y = 0.5
t.transform.translation.z = 0.0

# 회전 변환 설정 (단위 쿼터니언)
t.transform.rotation.x = 0.0
t.transform.rotation.y = 0.0
t.transform.rotation.z = 0.0
t.transform.rotation.w = 1.0  # 항등 회전

7. 메시지 설계상의 고려 사항

7.1 시간 스탬프의 역할

TransformStamped 메시지에 포함된 시간 스탬프는 단순한 메타데이터가 아니라, TF2 시스템의 핵심 기능인 시간 기반 변환 조회(temporal transform lookup)의 기반이 된다. TF2 버퍼는 수신된 TransformStamped 메시지를 시간 순서대로 저장하며, lookupTransform() 호출 시 지정된 시간에 대응하는 변환을 검색하거나 보간하여 반환한다.

7.2 프레임 식별자의 명명 규칙

frame_idchild_frame_id에 사용되는 프레임 이름은 ROS2 표준 규약(REP 103, REP 105)을 따르는 것이 권장된다. 프레임 이름은 슬래시(/)를 접두사로 사용하지 않으며, 네임스페이스를 통한 계층적 구분이 필요할 경우 적절한 접두사를 활용한다. 예를 들어, 다중 로봇 환경에서는 robot1/base_link, robot2/base_link와 같은 네임스페이싱 패턴을 적용한다.

7.3 쿼터니언 정규화의 중요성

rotation 필드에 저장되는 쿼터니언은 반드시 정규화된 단위 쿼터니언이어야 한다. 정규화되지 않은 쿼터니언은 변환 연산의 정확성을 저하시키며, 특히 연속적인 변환 합성(composition) 과정에서 수치적 오차가 누적될 수 있다. TF2 내부에서는 수신된 쿼터니언의 정규화 여부를 명시적으로 검증하지 않으므로, 발행 측에서 정규화를 보장하여야 한다.

7.4 기본값의 주의 사항

TransformStamped 메시지의 rotation 필드는 기본 초기화 시 모든 성분이 0으로 설정된다. 즉, q = (0, 0, 0, 0)이 되며 이는 유효한 쿼터니언이 아니다. 따라서 메시지를 생성한 후에는 반드시 rotation.w = 1.0으로 설정하여 유효한 항등 회전을 지정하거나, 원하는 회전에 해당하는 쿼터니언 값을 명시적으로 대입하여야 한다.

8. 직렬화와 전송

TransformStamped 메시지는 ROS2의 DDS(Data Distribution Service) 미들웨어를 통해 직렬화되어 네트워크 상에서 전송된다. CDR(Common Data Representation) 인코딩 방식이 적용되며, 메시지의 고정 크기 필드(숫자 타입)는 바이트 정렬(alignment) 규칙에 따라 직렬화되고, 가변 길이의 문자열 필드(frame_id, child_frame_id)는 길이 접두사와 함께 인코딩된다.

이 메시지는 /tf 또는 /tf_static 토픽을 통해 tf2_msgs/msg/TFMessage 내에 배열 형태로 포함되어 발행된다. 단일 TFMessage에 다수의 TransformStamped를 담아 발행함으로써 네트워크 오버헤드를 줄이고 효율적인 일괄 전송이 가능하다.

9. 참고 문헌

  • ROS2 공식 문서, “geometry_msgs/msg/TransformStamped,” https://docs.ros.org/en/humble/p/geometry_msgs/
  • ROS Enhancement Proposal (REP) 103, “Standard Units of Measure and Coordinate Conventions,” https://www.ros.org/reps/rep-0103.html
  • ROS Enhancement Proposal (REP) 105, “Coordinate Frames for Mobile Platforms,” https://www.ros.org/reps/rep-0105.html
  • Foote, T., “tf: The transform library,” 2013 IEEE Conference on Technologies for Practical Robot Applications (TePRA), 2013.
  • ROS2 Humble Hawksbill 기준 (2022)