659.121 TF2와 use_sim_time 파라미터 (TF2 and the use_sim_time Parameter)

659.121 TF2와 use_sim_time 파라미터 (TF2 and the use_sim_time Parameter)

1. use_sim_time 파라미터의 정의

use_sim_time은 ROS2 노드의 시계 소스(clock source)를 결정하는 부울형(boolean) 파라미터이다. 이 파라미터가 true로 설정되면, 노드의 내부 시계(rclcpp::Clock 또는 rclpy.clock.Clock)는 시스템 시간(wall clock) 대신 /clock 토픽으로부터 수신되는 시뮬레이션 시간(simulated time)을 사용한다. 모든 ROS2 노드는 이 파라미터를 내장 파라미터(built-in parameter)로 자동 선언하며, 기본값은 false이다.

이 파라미터는 TF2 프레임워크의 동작에 결정적인 영향을 미친다. TF2의 TransformBroadcaster, TransformListener, Buffer 등의 모든 구성 요소는 노드의 시계 객체를 참조하여 타임스탬프를 생성하거나 해석하므로, use_sim_time의 설정 상태가 변환 데이터의 시간적 일관성을 좌우한다.

2. 파라미터의 설정 방법

2.1 런치 파일에서의 설정

from launch_ros.actions import Node

robot_state_publisher = Node(
    package='robot_state_publisher',
    executable='robot_state_publisher',
    parameters=[{
        'robot_description': robot_description,
        'use_sim_time': True,
    }],
)

2.2 전역 설정을 통한 일괄 적용

동일 런치 파일 내의 모든 노드에 일괄적으로 use_sim_time을 적용하려면 SetParameter 액션을 사용한다.

from launch import LaunchDescription
from launch.actions import SetParameter

def generate_launch_description():
    return LaunchDescription([
        SetParameter(name='use_sim_time', value=True),
        # 이후 모든 Node 액션에 자동 적용
    ])

2.3 명령행에서의 동적 변경

실행 중인 노드의 use_sim_time 파라미터를 동적으로 변경할 수 있다.

ros2 param set /my_node use_sim_time true

그러나 이 동적 변경은 이미 생성된 tf2::Buffer의 시계 소스에 즉시 반영되지 않을 수 있으므로, 가능한 한 노드 기동 시점에 설정하는 것이 권장된다.

2.4 YAML 파라미터 파일에서의 설정

/**:
    ros__parameters:
        use_sim_time: true

YAML 파일의 /** 와일드카드는 이 파일을 로드하는 모든 노드에 해당 파라미터를 적용한다.

3. TF2 구성 요소에 대한 영향

3.1 TransformBroadcaster

TransformBroadcaster가 변환을 발행할 때, 변환 메시지의 header.stamp 필드에 현재 시각을 기록한다. 개발자가 this->get_clock()->now()를 사용하여 타임스탬프를 생성하면, use_sim_time 설정에 따라 시스템 시간 또는 시뮬레이션 시간이 기록된다.

geometry_msgs::msg::TransformStamped t;
t.header.stamp = this->get_clock()->now();  // use_sim_time에 의존
t.header.frame_id = "odom";
t.child_frame_id = "base_link";
tf_broadcaster_->sendTransform(t);

use_sim_time: true인 환경에서 rclcpp::Clock(RCL_SYSTEM_TIME).now()를 사용하여 시스템 시간을 강제로 타임스탬프에 기록하면, 시뮬레이션 시간 기준의 다른 변환과 시간적 불일치가 발생하므로 이러한 혼용은 반드시 지양하여야 한다.

3.2 TransformListener와 Buffer

TransformListener/tf/tf_static 토픽에서 수신한 변환 메시지를 Buffer에 저장한다. 저장 시 메시지에 포함된 타임스탬프를 그대로 사용하므로, use_sim_time 설정과 무관하게 발행자가 기록한 타임스탬프가 보존된다.

BufferlookupTransform() 호출 시 요청 시각은 호출자가 명시적으로 전달하거나 tf2::TimePointZero를 사용한다. 호출자가 this->get_clock()->now()로 요청 시각을 생성하면, 이 시각은 use_sim_time 설정에 따라 결정된다. 발행자와 소비자의 시간 소스가 일치하지 않으면 보간 및 외삽 과정에서 오류가 발생한다.

3.3 robot_state_publisher

robot_state_publisherjoint_states 토픽의 타임스탬프와 자체 시계를 사용하여 변환 타임스탬프를 결정한다. use_sim_time: true가 설정되면, joint_states의 타임스탬프(시뮬레이터에서 발행 시 시뮬레이션 시간 기준)와 robot_state_publisher의 내부 시계가 모두 시뮬레이션 시간을 사용하므로 일관성이 보장된다.

use_sim_time 설정이 robot_state_publisher에는 적용되고 joint_state_publisher에는 적용되지 않으면, 관절 상태의 타임스탬프와 변환의 타임스탬프 간에 불일치가 발생할 수 있다.

4. /clock 토픽의 구조와 발행 조건

4.1 메시지 형식

/clock 토픽은 rosgraph_msgs/msg/Clock 메시지 유형을 사용하며, 단일 필드 clockbuiltin_interfaces/msg/Time 형식의 시간 값을 포함한다.

rosgraph_msgs/msg/Clock
  builtin_interfaces/msg/Time clock
    int32 sec
    uint32 nanosec

4.2 발행 소스

/clock 토픽을 발행하는 주요 소스는 다음과 같다.

소스발행 조건시간 특성
Gazebo Classiclibgazebo_ros_init.so 플러그인 로드 시물리 엔진 시간 단계에 동기화
Ignition Gazeboros_gz_bridge 통해 연결 시물리 엔진 시간 단계에 동기화
ros2 bag play --clock--clock 옵션 지정 시기록된 메시지의 타임스탬프 기준
사용자 정의 노드명시적 발행 시사용자 정의

/clock 토픽이 발행되지 않는 상태에서 use_sim_time: true가 설정되면, 노드의 시계가 초기값(0)에서 진행하지 않아 모든 시간 기반 연산이 정상적으로 동작하지 않는다. 이 경우 lookupTransform()에서 지속적으로 ExtrapolationException이 발생하거나, 타이머 콜백이 트리거되지 않는 현상이 나타난다.

5. 불일치 시나리오의 상세 분석

5.1 시나리오 1: 단일 노드 설정 누락

시스템 내 대부분의 노드가 use_sim_time: true로 설정되어 있으나, 특정 센서 처리 노드만 false인 경우를 고려한다. 이 노드가 센서 데이터의 타임스탬프(시뮬레이션 시간)를 사용하여 lookupTransform()을 호출하면, 요청 시각은 시뮬레이션 시간이지만 노드의 get_clock()->now()는 시스템 시간을 반환한다. 변환 발행자들이 시뮬레이션 시간 기준의 타임스탬프로 발행하고 있으므로, 센서 타임스탬프 기준의 조회 자체는 성공할 수 있다. 그러나 노드 내부에서 now()를 사용한 시간 계산(예: 지연 시간 측정, 타임아웃 검사)은 시간 기준의 불일치로 인해 비정상적인 결과를 산출한다.

5.2 시나리오 2: /clock 토픽 부재

시뮬레이터 없이 실제 로봇에서 use_sim_time: true가 잔존하는 경우, /clock 토픽이 발행되지 않으므로 노드의 시계가 진행하지 않는다. TF2의 관점에서 get_clock()->now()가 항상 시간 0을 반환하게 되어, 변환 조회 시 항상 외삽 범위를 초과하는 결과가 발생한다.

이러한 상황을 탐지하기 위해 노드 초기화 시 /clock 토픽의 발행 여부를 확인하는 방어적 검증 로직을 구현할 수 있다.

if (this->get_parameter("use_sim_time").as_bool()) {
    auto clock_sub = this->create_subscription<rosgraph_msgs::msg::Clock>(
        "/clock", 10,
        [this](const%20rosgraph_msgs::msg::Clock::SharedPtr) {
            clock_received_ = true;
        });
    
    // 5초 내에 /clock 수신 여부 확인
    rclcpp::sleep_for(std::chrono::seconds(5));
    if (!clock_received_) {
        RCLCPP_FATAL(this->get_logger(),
            "use_sim_time이 true이나 /clock 토픽이 수신되지 않음. "
            "시뮬레이터 또는 rosbag 재생이 필요합니다.");
    }
}

6. 디버깅 도구와 검증 명령

6.1 파라미터 설정 상태 일괄 확인

# 실행 중인 모든 노드의 use_sim_time 설정 확인
for node in $(ros2 node list); do
    echo -n "$node: "
    ros2 param get $node use_sim_time 2>/dev/null || echo "파라미터 없음"
done

6.2 시간 소스 일관성 검증

# /clock 토픽의 현재 시간 확인
ros2 topic echo /clock --once

# /tf 메시지의 타임스탬프와 /clock 비교
ros2 topic echo /tf --field transforms[0].header.stamp --once

두 시간 값이 유사한 범위에 있으면 시간 소스가 일관되게 설정된 것이며, 큰 차이(수십 초 이상)가 있으면 설정 불일치를 의심하여야 한다.

7. 모범 사례 요약

항목권장 사항
시뮬레이션 환경모든 노드에 use_sim_time: true 일관 적용
실제 로봇 환경모든 노드에 use_sim_time: false (기본값)
런치 파일 설정SetParameter를 이용한 전역 설정 사용
/clock 부재 대비노드 초기화 시 /clock 수신 확인 로직 구현
시간 기준 전환노드 재시작을 통해 전환 (동적 변경 비권장)
혼합 환경절대 금지 (일관되지 않은 use_sim_time 설정)

참고 문헌 및 출처

  • ROS2 설계 문서, Clock and Time, https://design.ros2.org/articles/clock_and_time.html
  • ROS2 geometry2 리포지터리, tf2_ros 패키지, https://github.com/ros2/geometry2
  • Foote, T. (2013). “tf: The transform library.” IEEE International Conference on Technologies for Practical Robot Applications (TePRA), pp. 1–6.
  • ROS2 공식 문서, Launching Nodes, https://docs.ros.org/en/humble/Tutorials/Intermediate/Launch/Launch-Main.html

버전: 1.0