659.17 TransformStamped의 프레임 ID (frame_id, child_frame_id)

1. 개요

geometry_msgs/msg/TransformStamped 메시지에는 두 개의 프레임 식별자 필드가 존재한다. header.frame_idchild_frame_id이다. 이 두 필드는 TF2 변환 트리(Transform Tree)에서 부모-자식 관계를 정의하며, 좌표 변환의 방향과 의미를 결정하는 핵심 요소이다. 프레임 식별자의 올바른 설정은 로봇 시스템 전체의 좌표 변환 체계가 정확하게 작동하기 위한 전제 조건이 된다.

2. 두 프레임 식별자의 역할

2.1 header.frame_id — 부모 프레임

header.frame_id는 TransformStamped 메시지에서 부모 프레임(parent frame)을 지정한다. 이 프레임은 변환 트리에서 상위에 위치하는 좌표 프레임으로, 변환의 기준 좌표계(reference frame) 역할을 한다.

변환 트리의 구조적 관점에서, header.frame_id로 지정된 프레임은 트리 간선(edge)의 시작점에 해당한다. 이 프레임을 기준으로 자식 프레임의 위치와 자세(pose)가 기술된다.

2.2 child_frame_id — 자식 프레임

child_frame_id자식 프레임(child frame)을 지정하는 문자열이다. 이 프레임은 변환 트리에서 하위에 위치하며, 부모 프레임에 대한 상대적 위치와 방향이 transform 필드에 의해 기술된다.

변환 트리에서 child_frame_id로 지정된 프레임은 간선의 끝점에 해당하며, 해당 프레임의 원점과 축 방향이 부모 프레임 좌표계에서 어디에, 어떤 자세로 존재하는지를 transform 필드가 정의한다.

3. 변환의 방향성과 의미론적 해석

3.1 변환 방향 규약

TransformStamped 메시지가 표현하는 변환 {}^{A}T_{B}header.frame_id = A(부모 프레임)에서 child_frame_id = B(자식 프레임)로의 변환을 의미한다. 이 변환의 의미를 두 가지 관점에서 해석할 수 있다.

3.1.1 좌표 변환 관점

프레임 B에서 표현된 점 \mathbf{p}^{B}를 프레임 A에서의 좌표 \mathbf{p}^{A}로 변환하는 연산이다.

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

즉, 자식 프레임에서 측정된 데이터를 부모 프레임 좌표계로 변환하는 데 사용된다.

3.1.2 프레임 배치 관점

변환 {}^{A}T_{B}는 프레임 A의 좌표계 내에서 프레임 B의 원점 위치와 축 방향을 기술한다. transform.translation은 프레임 A의 원점에서 프레임 B의 원점까지의 벡터를 나타내고, transform.rotation은 프레임 A의 축에서 프레임 B의 축으로의 회전을 나타낸다.

3.2 변환 방향의 중요성

프레임 식별자의 순서를 혼동하면, 변환의 의미가 정반대가 된다. 예를 들어, header.frame_id = "odom"이고 child_frame_id = "base_link"인 변환은 odom 프레임에서 본 base_link의 위치와 자세를 표현한다. 이를 반대로 설정하면 base_link에서 본 odom의 위치와 자세를 표현하게 되므로, 전혀 다른 의미의 변환이 된다.

4. 프레임 식별자와 변환 트리의 관계

4.1 트리 구조 형성

TF2의 변환 트리는 모든 수신된 TransformStamped 메시지의 header.frame_idchild_frame_id 쌍으로부터 구성된다. 각 메시지는 트리에 하나의 간선을 추가하며, 이 간선은 header.frame_id(부모)에서 child_frame_id(자식) 방향으로 설정된다.

[header.frame_id] ──→ [child_frame_id]
   (부모 프레임)          (자식 프레임)

4.2 다단계 변환 체인

여러 TransformStamped 메시지가 결합되면 다단계 변환 체인이 형성된다. 다음은 전형적인 이동 로봇의 변환 체인 예시이다.

map ──→ odom ──→ base_link ──→ laser_link

이 체인을 구성하는 개별 TransformStamped 메시지의 프레임 식별자 설정은 다음과 같다.

메시지 번호header.frame_idchild_frame_id발행 주체
1mapodom위치 추정 노드
2odombase_link주행 거리 측정 노드
3base_linklaser_linkrobot_state_publisher

TF2는 이러한 체인을 통해 임의의 두 프레임 간 변환을 자동으로 계산한다. 예를 들어, map에서 laser_link로의 변환은 세 개의 변환을 순차적으로 합성하여 구해진다.

{}^{\text{map}}T_{\text{laser}} = {}^{\text{map}}T_{\text{odom}} \cdot {}^{\text{odom}}T_{\text{base}} \cdot {}^{\text{base}}T_{\text{laser}}

4.3 역방향 변환

TF2는 변환 트리에 저장된 간선의 역방향 변환을 자동으로 계산할 수 있다. 즉, header.frame_id = A, child_frame_id = B로 발행된 변환 {}^{A}T_{B}가 존재하면, TF2는 {}^{B}T_{A} = ({}^{A}T_{B})^{-1}을 별도의 발행 없이 내부적으로 산출한다. 따라서 프레임 간 변환을 양방향으로 조회하기 위해 두 개의 별도 TransformStamped 메시지를 발행할 필요가 없다.

5. 프레임 식별자 명명 규칙

5.1 ROS2 명명 규약

ROS2의 프레임 식별자는 다음과 같은 명명 규약을 따른다.

  • 선행 슬래시 미사용: ROS1에서는 프레임 이름에 선행 슬래시(/)를 사용하였으나, ROS2에서는 선행 슬래시를 사용하지 않는다. 예를 들어, base_link가 올바른 표기이며, /base_link는 사용하지 않는다.
  • 소문자와 밑줄: 프레임 이름은 소문자 알파벳과 밑줄(_), 숫자의 조합으로 구성한다. 대문자, 공백, 특수 문자는 사용하지 않는 것이 권장된다.
  • 의미론적 명명: 프레임의 물리적 위치, 기능, 또는 부착된 장치를 반영하는 이름을 부여한다.

5.2 표준 프레임 이름

REP 105(Coordinate Frames for Mobile Platforms)에서 정의한 표준 프레임 이름과 그 의미는 다음과 같다.

프레임 이름의미특성
earth지구 고정 전역 프레임다중 로봇 시스템의 공통 참조
map지도 기반 전역 프레임장기적 정확, 불연속적 보정
odom주행 거리 기반 프레임단기적 정확, 연속적, 누적 오차
base_link로봇 본체 고정 프레임로봇 기하학적 중심
base_footprint로봇 바닥면 투영 프레임base_link의 지면 투영

5.3 센서 프레임 명명

센서 프레임의 명명은 일반적으로 <센서_종류>_link 또는 <센서_종류>_frame 패턴을 따른다.

camera_link         # 카메라 장착 프레임
camera_optical      # 카메라 광학 프레임 (z축 전방)
imu_link            # IMU 장착 프레임
lidar_link          # LiDAR 장착 프레임
gps_link            # GPS 안테나 프레임
sonar_link          # 소나 센서 프레임

5.4 다중 로봇 네임스페이싱

다중 로봇 시스템에서는 프레임 이름 충돌을 방지하기 위해 로봇별 네임스페이스를 접두사로 사용한다.

robot1/base_link
robot1/odom
robot2/base_link
robot2/odom

이 방식을 통해 각 로봇의 독립적인 변환 트리가 유지되며, 필요에 따라 공통 프레임(예: earth 또는 map)을 통해 통합될 수 있다.

6. 프로그래밍 예시

6.1 C++ (rclcpp) 예시

#include <geometry_msgs/msg/transform_stamped.hpp>

geometry_msgs::msg::TransformStamped transform_stamped;

// 부모 프레임 설정
transform_stamped.header.frame_id = "odom";

// 자식 프레임 설정
transform_stamped.child_frame_id = "base_link";

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

// 변환 데이터 설정
transform_stamped.transform.translation.x = 2.0;
transform_stamped.transform.translation.y = 1.5;
transform_stamped.transform.translation.z = 0.0;
transform_stamped.transform.rotation.x = 0.0;
transform_stamped.transform.rotation.y = 0.0;
transform_stamped.transform.rotation.z = 0.3827;
transform_stamped.transform.rotation.w = 0.9239;

6.2 Python (rclpy) 예시

from geometry_msgs.msg import TransformStamped

transform_stamped = TransformStamped()

# 부모 프레임 설정
transform_stamped.header.frame_id = 'map'

# 자식 프레임 설정
transform_stamped.child_frame_id = 'odom'

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

# 변환 데이터 설정
transform_stamped.transform.translation.x = 5.0
transform_stamped.transform.translation.y = 3.0
transform_stamped.transform.translation.z = 0.0
transform_stamped.transform.rotation.x = 0.0
transform_stamped.transform.rotation.y = 0.0
transform_stamped.transform.rotation.z = 0.0
transform_stamped.transform.rotation.w = 1.0

7. 프레임 식별자 설정 시 주의 사항

7.1 자기 참조 방지

header.frame_idchild_frame_id가 동일한 프레임을 참조하는 것은 허용되지 않는다. 자기 자신으로의 변환은 의미론적으로 무의미하며, TF2 시스템에서 거부된다.

// 잘못된 사용: 자기 참조
transform_stamped.header.frame_id = "base_link";
transform_stamped.child_frame_id = "base_link";  // 오류 발생

7.2 빈 문자열 방지

프레임 식별자로 빈 문자열("")을 사용하면 InvalidArgumentException이 발생한다. 모든 TransformStamped 메시지에는 유효한 프레임 이름이 반드시 지정되어야 한다.

7.3 순환 방지

변환 트리에서 순환(cycle)이 형성되어서는 안 된다. 예를 들어, A → B → C → A와 같은 순환 구조는 TF2에서 허용되지 않으며, 이러한 메시지가 발행되면 변환 조회 시 오류가 발생한다.

7.4 다중 부모 방지

하나의 자식 프레임에 대해 서로 다른 부모 프레임을 가진 복수의 변환이 발행되면, TF2는 이를 충돌(conflict)로 판단한다. 각 프레임은 변환 트리에서 정확히 하나의 부모만 가질 수 있다.

# 허용되지 않는 구조
odom ──→ base_link
map  ──→ base_link  (base_link에 두 개의 부모가 존재)

올바른 구조는 다음과 같이 체인 형태를 유지하는 것이다.

# 올바른 구조
map ──→ odom ──→ base_link

7.5 일관된 문자열 사용

프레임 이름은 대소문자를 구분하므로, Base_Linkbase_link는 서로 다른 프레임으로 취급된다. 시스템 전체에 걸쳐 프레임 이름의 철자와 대소문자를 일관되게 유지하여야 한다.

8. lookupTransform()에서의 프레임 식별자

lookupTransform() 함수 호출 시에는 target_framesource_frame이라는 매개변수를 사용하며, 이는 TransformStamped의 프레임 식별자와 다음과 같은 관계를 갖는다.

auto transform = buffer->lookupTransform(
    "target_frame",   // 변환 결과의 기준 프레임
    "source_frame",   // 변환 대상의 기준 프레임
    time);

반환되는 TransformStamped의 필드 관계는 다음과 같다.

  • header.frame_id = target_frame
  • child_frame_id = source_frame

이 관계를 통해 source_frame에서 표현된 데이터를 target_frame 좌표계로 변환할 수 있다.

9. 참고 문헌

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