ROS 2 Humble 시계(Clock) 개념

ROS 2 Humble 시계(Clock) 개념

1. 서론

로보틱스 시스템의 근간을 이루는 수많은 알고리즘은 시간과 동기화에 본질적으로 의존한다. 센서 데이터의 타임스탬프, 제어 루프의 주기성, 분산 노드 간의 데이터 일관성은 모두 정확하고 신뢰할 수 있는 시간 정보에 기반하여 그 성능이 결정된다.1 본 안내서는 ROS 2 Humble에서 이러한 복잡한 요구사항을 충족시키기 위해 설계된 정교한 시계(Clock) 및 시간(Time) 개념을 심층적으로 분석한다. 실제 로봇 운영 환경뿐만 아니라, 시뮬레이션, 데이터 로깅 및 재현(playback), 고급 디버깅 등 다양한 개발 시나리오를 포괄적으로 지원하기 위해, ROS 2는 단순한 시스템 시간(wall-clock time)을 넘어선 추상화된 시간 개념을 도입했다.1 이 추상화 계층은 시간의 흐름을 가속, 감속, 심지어 정지시킬 수 있는 강력한 유연성을 제공하며, 이는 견고하고 재현 가능한 로봇 시스템 개발에 필수적인 요소다.2 본 안내서는 이러한 시간 추상화의 필요성에서 출발하여, 각 시계 유형의 정의와 특성, 실제 구현 메커니즘, 그리고 C++(rclcpp)와 Python(rclpy)에서의 활용법을 상세히 다룰 것이다.

2. ROS 2 시계의 기본 철학

ROS 2의 시간 시스템은 단순히 “현재 시간을 알려주는” 기능을 넘어, “시간을 제어하는” 철학을 기반으로 설계되었다. 이는 로보틱스 개발의 전체 라이프사이클, 즉 초기 개발 단계부터 시뮬레이션, 테스트, 그리고 실제 배포에 이르는 전 과정을 고려한 결과물이다. 시간이라는 개념을 외부 환경 변수가 아닌, 시스템이 능동적으로 관리하고 제어할 수 있는 내부 자원으로 취급함으로써 개발의 생산성과 시스템의 신뢰성을 극대화하고자 한다.

2.1 시간 추상화의 동기: 시뮬레이션, 데이터 재현, 디버깅

ROS 2가 정교한 시간 추상화 계층을 도입한 배경에는 세 가지 핵심적인 사용 사례가 있다.

  • 데이터 재현(Data Playback): rosbag과 같은 도구를 사용하여 기록된 데이터를 재생할 때, 시간의 흐름을 가속하거나 감속하여 특정 구간을 집중적으로 분석하거나, 특정 시점에서 시스템을 일시 정지하여 심층적으로 디버깅하는 기능은 매우 중요하다.1 만약 시스템이 실제 시간(wall-clock)에만 의존한다면, 기록된 데이터의 타임스탬프와 시스템의 나머지 부분이 동기화되지 않아 알고리즘이 오작동할 수 있다.1 예를 들어, 10배속으로 데이터를 재생하는 상황에서 제어 루프가 실제 시간 기준으로 동작한다면 시스템은 제대로 반응할 수 없다.

  • 시뮬레이션(Simulation): 시뮬레이션 환경은 실제 하드웨어 없이 알고리즘을 검증하는 필수적인 단계다. 시뮬레이터는 종종 시스템의 연산 병목 지점이 되며, 실제보다 빠르거나 느리게 실행될 수 있다.1 로봇 개발 과정에서 모든 노드가 각자의 시스템 시간을 기준으로 동작한다면, 시뮬레이션 환경 내에서 노드 간의 상호작용은 비결정론적이고 예측 불가능하게 된다.3 이를 해결하기 위해 ROS 2의 시간 추상화는 시뮬레이터를 시간의 원천(Time Source)으로 삼아, 모든 노드가 공유하고 동의할 수 있는 단일한 “가상 시간“에 동기화되도록 한다.2 시뮬레이션이 일시 정지되면, ROS 시스템 전체도 함께 멈출 수 있어 일관된 상태 분석이 가능하다.4

  • 디버깅(Debugging): 시간의 흐름을 조작하는 능력, 특히 시간을 완전히 멈추는 기능은 복잡한 시스템의 상태를 특정 시점에서 “얼려” 분석하는 데 필수적이다.1 ROS 2의 시간 추상화는 이러한 고급 디버깅 기법을 가능하게 하는 기반을 제공하여, 재현하기 어려운 버그를 체계적으로 분석할 수 있게 돕는다.

2.2 세 가지 핵심 시계 유형 소개: SystemTime, SteadyTime, ROSTime

이러한 요구사항을 충족시키기 위해, ROS 2는 C++ 표준 라이브러리인 std::chronosystem_clocksteady_clock에 대응하도록 설계된 세 가지 주요 시간 추상화 유형을 제공한다.1

  • SystemTime: 시스템의 벽시계 시간(wall-clock time)에 직접 연결된다. 현실 세계의 시간과 일치한다.1

  • SteadyTime: 시스템 부팅 이후 단조롭게 증가하는(monotonically increasing) 시간을 제공하여, 시간 경과를 오차 없이 정확하게 측정하는 데 사용된다.1

  • ROSTime: ROS 시간 소스의 활성화 여부에 따라 SystemTime과 동일하게 동작하거나, 시뮬레이션 시간을 따르는 유연한 시계다.5

개발자는 이 세 가지 시계 유형 중에서 상황에 맞는 것을 선택해야 하며, 특별한 이유가 없는 한 ROS 노드 간의 통신과 동기화를 위해 ROSTime을 사용하는 것이 기본적으로 권장된다.1

3. 시계 유형별 심층 분석

ROS 2의 세 가지 시계 유형은 각각 명확한 목적과 특성을 가지며, 이를 이해하는 것은 상황에 맞는 올바른 도구를 선택하는 첫걸음이다.

3.1 RCL_SYSTEM_TIME: 현실 세계의 시간 (The “Watch”)

  • 정의: RCL_SYSTEM_TIME은 운영체제의 시스템 시간, 즉 우리가 일상적으로 사용하는 벽시계 시간(wall-clock time)과 동일한 값을 보고한다. 이는 C++의 std::chrono::system_clock에 해당한다.5

  • 특성: 이 시계는 현재 시각을 알려주는 “손목시계“에 비유할 수 있다.5 그러나 이 시간은 NTP(Network Time Protocol)와 같은 외부 소스에 의해 동기화 과정에서 시간이 뒤로 가거나 앞으로 점프할 수 있다. 이러한 비연속적인 특성 때문에, 두 이벤트 사이의 시간 간격을 정밀하게 측정하는 용도로는 부적합할 수 있다.5

  • 주요 사용 사례: 외부 세계와의 상호작용이 필요할 때 주로 사용된다. 예를 들어, 로그 파일에 현실 세계의 타임스탬프를 기록하거나, 특정 절대 시간에 작업을 예약하는 경우가 이에 해당한다.1 하드웨어 드라이버 등에서 외부 장치와 시간을 맞춰야 할 때도 필요하지만, 이 경우에도 ROS 네트워크를 통해 다른 노드와 통신할 때는 ROSTime으로 변환하여 타임스탬프를 찍는 것이 권장된다.1

3.2 RCL_STEADY_TIME: 경과 시간 측정의 기준 (The “Stopwatch”)

  • 정의: RCL_STEADY_TIME은 시스템 부팅과 같은 특정 기준점 이후로 단조롭게 증가(monotonically increasing)만 하는 시간을 보고한다. 이는 C++의 std::chrono::steady_clock에 해당한다.6

  • 특성: 이 시계는 경과 시간을 측정하는 “스톱워치“에 비유할 수 있다.5 외부 요인에 의해 시간이 변경되거나 뒤로 가지 않으므로, 시간 간격 측정, 타임아웃 계산 등 정밀한 시간 측정이 필요한 작업에 이상적이다.5

  • 주요 사용 사례: 하드웨어 드라이버에서 주변 장치와의 통신 타임아웃을 처리하는 경우가 대표적인 사용 사례다.1 또한, rclcpp::Node::create_wall_timer()는 이 RCL_STEADY_TIME을 사용하여 시뮬레이션 시간의 흐름과 무관하게 실제 시간을 기준으로 콜백을 트리거한다. 이를 통해 시뮬레이션이 일시 정지된 상태에서도 시스템 모니터링과 같은 작업을 계속 수행할 수 있다.5

3.3 RCL_ROS_TIME: 유연성의 핵심, ROS 시간

  • 정의: RCL_ROS_TIME은 ROS 시간 추상화의 핵심으로, use_sim_time 파라미터의 값에 따라 동적으로 동작이 변한다. ROS 시간 소스가 활성화되지 않은 경우(use_sim_timefalse), RCL_SYSTEM_TIME과 동일하게 동작한다.5 반면, ROS 시간 소스가 활성화된 경우(use_sim_timetrue), /clock 토픽에서 발행된 최신 시간을 보고한다.1

  • 특성: use_sim_time 파라미터 값에 따라 동작이 동적으로 변경되는 유연성을 가진다. 시뮬레이션 환경에서는 시뮬레이터가 발행하는 /clock 토픽을 따라가고, 실제 환경에서는 시스템 시간을 따라감으로써 동일한 코드를 수정 없이 두 환경 모두에서 사용할 수 있게 한다.1

  • 주의사항: use_sim_timetrue로 설정되었지만 /clock 토픽이 아직 발행되지 않은 상태에서 now()와 같은 시간 관련 API를 호출하면, 시간이 초기화되지 않았음을 의미하는 0을 반환할 수 있다.2 이는 초기화 단계에서 문제를 일으킬 수 있으므로, rclcpp::Clock::started()wait_until_started()와 같은 함수를 사용하여 시계가 유효한 시간을 보고하기 시작했는지 확인하는 것이 안전하다.9

3.4 Table 1: ROS 2 시계 유형 비교

속성 (Attribute)RCL_SYSTEM_TIMERCL_STEADY_TIMERCL_ROS_TIME
개념적 비유 (Analogy)손목시계 (Watch)스톱워치 (Stopwatch)지능형 시계 (Smart Watch)
C++ std::chrono 대응std::chrono::system_clockstd::chrono::steady_clockN/A (ROS 고유)
시간의 연속성 (Continuity)보장 안 됨 (점프 가능)단조 증가 (Monotonic)use_sim_time에 따라 다름
시간의 원천 (Time Source)OS의 시스템 시간OS의 단조 시계 (e.g., 부팅 기준)use_sim_time=false: 시스템 시간 use_sim_time=true: /clock 토픽
주요 사용 사례 (Primary Use Case)- 외부 세계 시간 기록
- 절대 시간 기반 작업
- 시간 간격 측정
- 타임아웃 계산
- create_wall_timer
- ROS 노드 간의 시간 동기화
- 시뮬레이션 및 데이터 재현
참고 (Reference)561

4. 시뮬레이션 시간의 구현: /clockuse_sim_time

RCL_ROS_TIME의 유연성은 use_sim_time 파라미터와 /clock 토픽의 긴밀한 상호작용을 통해 구현된다. 이 메커니즘을 이해하는 것은 시뮬레이션 환경에서 ROS 2 시스템을 안정적으로 운영하는 데 필수적이다.

4.1 use_sim_time 파라미터의 역할과 노드별 설정의 의미

use_sim_time은 각 ROS 2 노드가 가지는 불리언(boolean) 타입의 파라미터다.8 이 파라미터가 true로 설정되면, 해당 노드는 자신의 시간 소스를 시스템 시간에서 /clock 토픽으로 전환한다.4

ROS 1에서는 이 파라미터가 전역적(global)으로 단 하나만 존재했지만, ROS 2에서는 각 노드별로 독립적으로 설정된다.12 이는 ROS 2의 핵심 설계 원칙인 “전역 상태 제거“를 따르는 것으로, 각 노드를 외부 환경에 암묵적으로 의존하지 않는 독립적인 컴포넌트로 만들어 모듈성과 예측 가능성을 높인다.8 하지만 이러한 설계는 개발자에게 시스템 전체의 시간 일관성을 보장해야 하는 더 큰 책임을 부여한다. 이는 유연성과 책임의 트레이드오프 관계를 보여주는 대표적인 사례다.

이 파라미터는 런치 파일 14, ros2 param set CLI 명령어 4, 또는 노드 생성 시 NodeOptions를 통해 설정할 수 있다.5

4.2 /clock 토픽: 시뮬레이션 시간의 원천

/clockrosgraph_msgs/msg/Clock 타입의 메시지를 발행하는 특수한 토픽이다.4 시뮬레이터(e.g., Gazebo, Isaac Sim)나 ros2 bag play --clock 명령어는 이 토픽을 통해 현재 시뮬레이션 시간을 시스템에 주기적으로 전파한다.2 use_sim_timetrue로 설정된 모든 노드들은 내부적으로 이 토픽을 구독하고, 수신된 메시지의 타임스탬프를 자신의 RCL_ROS_TIME으로 사용한다.11 /clock 토픽이 발행되는 주기는 시간 동기화의 정밀도에 직접적인 영향을 미친다. 주기가 너무 길면 시간 해상도가 낮아져 정밀한 작업에 불리하고, 너무 짧으면 네트워크와 CPU 부하가 증가할 수 있어 시스템의 전체 성능에 영향을 줄 수 있다.16

4.3 시간 불일치 문제: 분산 시스템에서의 잠재적 위험과 해결 방안

use_sim_time이 노드별로 설정되기 때문에 발생하는 가장 심각한 문제는, 개발자의 실수로 일부 노드는 true로, 다른 노드는 false로 설정되는 상황이다.12 이 경우, 시스템 내에 시뮬레이션 시간과 실제 시간이 공존하는 “시간 분열(temporal split-brain)” 상태가 발생하여 예측하기 어렵고 디버깅이 매우 까다로운 문제를 야기한다.3

  • 영향: 시뮬레이션 시간(보통 0초에서 시작하여 수십, 수백 초)을 사용하는 노드와 시스템 시간(Unix Epoch, 즉 1970년 1월 1일부터 현재까지 약 17억 초)을 사용하는 노드의 타임스탬프는 비교 자체가 무의미하다.3 이러한 불일치는 TF 변환 시 ExtrapolationException 오류, 메시지 필터의 동기화 실패, 오래된 데이터로 인한 제어 오작동 등 심각한 문제로 이어진다.

  • 해결 방안:

  1. 일관된 런치 파일 관리: 시뮬레이션을 위한 런치 파일에서는 시스템을 구성하는 모든 노드(RViz, rqt, 직접 작성한 노드, Gazebo 플러그인 등)의 use_sim_time 파라미터를 일관되게 true로 설정하는 것이 필수적이다.12

  2. 전역 파라미터화: 런치 파일의 최상단에서 LaunchConfiguration을 사용하여 use_sim_time 값을 변수로 정의하고, 모든 노드 선언부에 이 변수를 전달하는 설계 패턴을 사용하여 실수를 방지한다.

  3. 시스템 검증: ros2 topic echo <topic_name>을 통해 메시지 헤더의 타임스탬프를 확인하고, ros2 param listros2 param get을 사용하여 각 노드의 use_sim_time 설정을 주기적으로 확인하여 불일치가 없는지 검증하는 습관이 중요하다.3

5. rclcpp를 이용한 시간 관리

C++ 클라이언트 라이브러리인 rclcpp는 ROS 2의 시간 개념을 효과적으로 다룰 수 있는 강력한 클래스와 함수들을 제공한다.

5.1 핵심 클래스: rclcpp::Clock, Time, Duration

  • rclcpp::Clock: 시간의 원천을 나타내는 클래스. 생성 시 RCL_ROS_TIME, RCL_SYSTEM_TIME, RCL_STEADY_TIME 중 하나의 타입을 인자로 받는다. now() 메소드를 통해 현재 시간을 rclcpp::Time 객체로 반환한다.9

  • rclcpp::Time: 특정 시점을 나타내는 클래스. 초(seconds)와 나노초(nanoseconds)로 구성되며, 어떤 종류의 시계에서 생성되었는지를 나타내는 clock_type 정보를 내부적으로 가진다.17 중요한 점은, 서로 다른 clock_type을 가진 Time 객체 간의 뺄셈과 같은 연산은 런타임 에러를 발생시킨다는 것이다. 이는 의미 없는 시간 비교를 컴파일 시점이 아닌 실행 시점에 방지하기 위함이다.9

  • rclcpp::Duration: 두 Time 객체 사이의 시간 간격을 나타내는 클래스. Time 객체 간의 뺄셈 연산의 결과로 생성될 수 있으며, Time 객체에 더하거나 뺄 수 있다.18

5.2 노드에서의 시계 획득: node->get_clock() 패턴의 중요성

RCL_ROS_TIME 타입의 시계를 사용할 때는 rclcpp::Clock my_clock(RCL_ROS_TIME);과 같이 시계를 직접 생성하는 것을 반드시 피해야 한다.9 이렇게 생성된 시계는 TimeSource(use_sim_time 파라미터 확인 및 /clock 구독 로직을 담당하는 내부 메커니즘)가 연결되어 있지 않다. 그 결과, use_sim_time 파라미터가 true이더라도 /clock 토픽을 무시하고 항상 시스템 시간처럼 동작하는 예기치 않은 결과를 초래한다.9

올바른 방법은 항상 node->get_clock()를 사용하는 것이다. 노드는 생성 시 내부적으로 use_sim_time 파라미터와 /clock 토픽을 관리하는 TimeSource를 생성하고 자신의 기본 시계에 연결한다.8 따라서 node->get_clock()->now()는 항상 개발자가 의도한 대로(시뮬레이션 또는 실제 시간) 동작함을 보장한다.

5.3 타이머 유형 비교: create_timercreate_wall_timer

rclcpp는 두 가지 종류의 타이머 생성 함수를 제공하며, 이 둘의 차이점은 사용하는 시계의 종류에 있다.

  • create_timer: 이 함수는 노드의 기본 시계, 즉 node->get_clock()가 반환하는 RCL_ROS_TIME 시계를 사용하여 타이머를 생성한다. 따라서 use_sim_timetrue이면 시뮬레이션 시간에 맞춰 콜백이 트리거된다. 시뮬레이션이 멈추면 이 타이머도 함께 멈춘다.5

  • create_wall_timer: 이 함수는 RCL_STEADY_TIME을 사용하는 별도의 시계를 기반으로 타이머를 생성한다.5 따라서 시뮬레이션 시간의 흐름과 완전히 무관하게, 항상 실제 시간을 기준으로 콜백이 트리거된다. 시뮬레이션이 멈춰도 이 타이머는 계속해서 동작한다.5

선택 가이드: 로봇의 제어 루프, 센서 데이터 주기적 발행 등 시뮬레이션 세계의 시간 흐름과 동기화되어야 하는 작업에는 create_timer를 사용해야 한다. 반면, 시스템의 상태 모니터링, 외부와의 통신 타임아웃 처리, 사용 중인 GUI의 응답성 유지 등 시뮬레이션 상태와 무관하게 실제 시간 기준으로 반드시 실행되어야 하는 작업에는 create_wall_timer를 사용해야 한다.

5.4 주요 연산 및 활용 사례: C++ 코드 예제

  • 시간 획득 및 출력:
#include "rclcpp/rclcpp.hpp"

//... Node 클래스 내부...
void time_examples()
{
// 노드의 기본 시계 (ROSTime) 사용
auto now = this->get_clock()->now();
RCLCPP_INFO(this->get_logger(), "Current ROS Time: %f seconds", now.seconds());

// SystemTime 시계 명시적 생성 및 사용
rclcpp::Clock system_clock(RCL_SYSTEM_TIME);
rclcpp::Time system_time_now = system_clock.now();
RCLCPP_INFO(this->get_logger(), "Current System Time: %f seconds", system_time_now.seconds());

// SteadyTime 시계 명시적 생성 및 사용
rclcpp::Clock steady_clock(RCL_STEADY_TIME);
rclcpp::Time steady_time_now = steady_clock.now();
RCLCPP_INFO(this->get_logger(), "Current Steady Time (nanosec): %ld", steady_time_now.nanoseconds());
}

9

  • 시간 간격 계산:
rclcpp::Time start_time = this->get_clock()->now();
//... 시간이 소요되는 작업 수행 (예: 복잡한 계산)...
rclcpp::Time end_time = this->get_clock()->now();
rclcpp::Duration elapsed_time = end_time - start_time;
RCLCPP_INFO(this->get_logger(), "Elapsed time: %.4f seconds", elapsed_time.seconds());

18

  • 타이머 사용 예제:
#include <chrono>
using namespace std::chrono_literals;

//... Node 클래스 생성자 내부...
// 시뮬레이션 시간을 따르는 타이머 (1초 주기)
sim_timer_ = this->create_timer(1s, std::bind(&MyNode::sim_timer_callback, this));

// 실제 시간을 따르는 타이머 (500ms 주기)
wall_timer_ = this->create_wall_timer(500ms, std::bind(&MyNode::wall_timer_callback, this));

5

6. rclpy를 이용한 시간 관리

Python 클라이언트 라이브러리인 rclpy 또한 C++와 대등한 시간 관리 기능을 제공한다.

6.1 핵심 클래스: rclpy.clock.Clock, time.Time, duration.Duration

  • rclpy.clock.Clock: C++와 유사하게 시간의 원천을 나타낸다. ClockType 열거형(ROS_TIME, SYSTEM_TIME, STEADY_TIME)을 clock_type 인자로 받아 생성할 수 있다.20

  • rclpy.time.Time: 특정 시점을 나타내는 클래스. secondsnanoseconds를 키워드 인자로 받아 생성하며, clock_type을 지정할 수 있다.20

  • rclpy.duration.Duration: 시간 간격을 나타내는 클래스. seconds 또는 nanoseconds를 키워드 인자로 받아 생성하며, Time 객체 간의 뺄셈이나 Time 객체와의 덧셈/뺄셈 연산에 사용된다.21

6.2 노드에서의 시계 활용 및 타이머 생성

  • 시계 획득: node.get_clock() 메소드를 통해 노드에 연결된 기본 시계(일반적으로 ROS_TIME)를 얻는다. node.get_clock().now()를 호출하여 현재 시간을 Time 객체로 얻는 것이 표준적인 방법이다.23

  • 타이머 생성:

  • node.create_timer(): 첫 번째 인자로 주기(초), 두 번째 인자로 콜백 함수를 받는다. 기본적으로 노드의 시계를 사용하므로 시뮬레이션 시간을 따른다.24

  • node.create_wall_timer(): rclpy에는 create_wall_timer라는 별도의 함수가 없다. 대신 create_timer 함수의 clock 인자에 STEADY_TIME 타입의 Clock 객체를 명시적으로 전달하여 wall timer와 동일한 효과를 구현한다.

6.3 주요 연산 및 활용 사례: Python 코드 예제

  • 시간 획득 및 출력:
import rclpy
from rclpy.node import Node
from rclpy.clock import Clock, ClockType

#... Node 클래스 내부...
def time_examples(self):
# 노드의 기본 시계 (ROSTime) 사용
now = self.get_clock().now()
self.get_logger().info(f'Current ROS Time: {now.nanoseconds / 1e9} seconds')

# SteadyTime 시계 명시적 생성 및 사용
steady_clock = Clock(clock_type=ClockType.STEADY_TIME)
steady_now = steady_clock.now()
self.get_logger().info(f'Current Steady Time: {steady_now.nanoseconds} nanoseconds')

20

  • 시간 간격 계산 및 TF2에서의 활용:
from rclpy.duration import Duration
from rclpy.time import Time

#...
start_time = self.get_clock().now()
#... 시간이 소요되는 작업 수행...
end_time = self.get_clock().now()
elapsed_time = end_time - start_time
self.get_logger().info(f'Elapsed time: {elapsed_time.nanoseconds / 1e9:.4f} seconds')

# TF2에서 5초 전의 transform을 조회하는 예제
when = self.get_clock().now() - Duration(seconds=5.0)
try:
trans = self.tf_buffer.lookup_transform(
'target_frame', 'source_frame', when, timeout=Duration(seconds=0.05))
except Exception as e:
self.get_logger().error(f'Could not transform: {e}')

22

  • 타이머 사용 예제:
from rclpy.clock import Clock, ClockType

#... Node 클래스 생성자 내부...
# 시뮬레이션 시간을 따르는 타이머 (1Hz)
self.sim_timer = self.create_timer(1.0, self.sim_timer_callback)

# 실제 시간을 따르는 타이머 (2Hz)
self.wall_clock = Clock(clock_type=ClockType.STEADY_TIME)
# create_timer에 clock 인자를 전달하여 wall timer 구현
self.wall_timer = self.create_timer(
0.5, self.wall_timer_callback, clock=self.wall_clock)

24

7. 고급 주제 및 모범 사례

ROS 2의 시간 시스템을 올바르게 사용하는 것은 단순히 API를 아는 것을 넘어, 작성하는 코드가 어떤 “시간의 영역(Temporal Domain)“에 속하는지(시뮬레이션 세계, 실제 물리 세계, 또는 그 사이의 인터페이스)를 명확히 이해하고 설계하는 아키텍처적 결정이다.

7.1 시간 점프(Time Jump) 처리 메커니즘

use_sim_time이 활성화된 상태에서 ros2 bag play의 되감기 기능이나 시뮬레이션 리셋 등으로 인해 시간이 과거로 이동하는 “시간 점프“가 발생할 수 있다.1 이는 시간의 연속적인 증가를 가정하는 많은 알고리즘(예: 칼만 필터, PID 제어기)에 치명적인 오류를 유발할 수 있다. 이를 처리하기 위해 rclcpp::Clockcreate_jump_callback() 메소드를 제공한다.9 개발자는 이 콜백을 사용하여 시간 점프가 발생했을 때 상태 변수를 초기화하거나, 데이터 버퍼를 비우는 등 시스템의 상태를 일관되게 유지하는 로직을 구현할 수 있다. 이는 시스템의 안정성을 위해 매우 중요한 기능이다.

7.2 외부 시간 동기화의 필요성 (NTP/chrony)

여러 대의 컴퓨터에 분산된 ROS 시스템이 RCL_SYSTEM_TIME 또는 use_sim_time=false 상태의 RCL_ROS_TIME을 사용할 경우, 각 컴퓨터의 시스템 시간이 동기화되어 있지 않으면 심각한 문제가 발생한다.2 ROS는 자체적으로 시간 동기화 기능을 제공하지 않으므로, NTPchrony와 같은 표준 네트워크 시간 동기화 프로토콜을 사용하여 모든 장비의 시계를 수 밀리초(ms) 이내의 오차로 정밀하게 동기화하는 것이 필수적이다.2 동기화되지 않은 시간은 특히 tf2와 같이 여러 소스에서 오는 데이터의 타임스탬프를 기반으로 계산을 수행하는 시스템에서 치명적인 오류의 원인이 된다.

7.3 상황별 최적의 시계 유형 선택 가이드라인

각 기능의 요구사항을 분석하여 ROSTime, SteadyTime, SystemTime 중 가장 적절한 시간의 척도를 선택해야 한다. 이는 코드의 기능적 정확성뿐만 아니라, 시스템 전체의 안정성과 예측 가능성에 직접적인 영향을 미치는 중요한 설계 결정이다.

  • 일반적인 ROS 노드: 기본적으로 ROSTime(node->get_clock(), create_timer)을 사용해야 한다. 이는 시뮬레이션과 실제 환경 간의 코드 호환성을 보장하는 가장 안전하고 유연한 방법이다.1

  • 실시간 제어 루프: 하드웨어와 직접 상호작용하며 엄격한 주기성을 요구하는 제어 루프는 실시간 운영체제(RTOS)와 함께 SteadyTime(create_wall_timer)을 사용하는 것을 고려해야 한다.27 이는 시뮬레이션의 변동성에 영향을 받지 않는 결정론적(deterministic) 동작을 보장한다.

  • 하드웨어 드라이버: 주변 장치와의 통신 타임아웃 등 실제 시간 기반의 이벤트 처리가 필요할 때는 SteadyTime을 사용해야 한다.1 하지만 드라이버가 ROS 네트워크에 데이터를 발행할 때는, 해당 데이터의 타임스탬프를 ROSTime(node->get_clock()->now())으로 찍어 발행하는 것이 시스템 전체의 시간 일관성을 위한 모범 사례다.1

  • 데이터 로깅 및 분석: 외부 세계의 사건과 시간을 맞춰야 하는 데이터 로깅 노드는 SystemTime을 사용하여 타임스탬프를 기록할 수 있다. 단, 이 경우 분산 시스템의 모든 로깅 장비의 시간이 NTP로 정밀하게 동기화되어 있다는 전제가 반드시 필요하다.26

8. 결론

ROS 2 Humble의 시계 시스템은 SystemTime, SteadyTime, ROSTime이라는 세 가지 핵심 추상화를 통해 단순한 시간 조회를 넘어 시간의 흐름을 제어하는 강력한 프레임워크를 제공한다. use_sim_time 파라미터와 /clock 토픽의 상호작용은 시뮬레이션과 실제 환경 간의 원활한 전환을 가능하게 하는 핵심 메커니즘이지만, 동시에 개발자에게 시스템 전체의 시간 일관성을 보장해야 하는 중요한 책임을 부여한다.

견고한 로봇 애플리케이션을 구축하기 위해, 개발자는 rclcpprclpy에서 제공하는 Clock, Time, Duration 객체와 create_timer/create_wall_timer와 같은 API를 정확히 이해하고 사용해야 한다. 특히, 자신이 개발하는 기능이 어떤 시간 영역(시뮬레이션 세계 또는 물리적 실제 세계)에 속하는지 명확히 인식하고 그에 맞는 시계 유형을 선택하는 것이 견고하고 예측 가능한 로봇 시스템을 구축하는 핵심이다. 본 안내서에서 제시된 개념과 모범 사례를 따름으로써, 개발자는 ROS 2의 정교한 시간 관리 기능을 최대한 활용하여 고품질의 로봇 애플리케이션을 개발할 수 있을 것이다.

9. 참고 자료

  1. Clock and Time - ROS2 Design, https://design.ros2.org/articles/clock_and_time.html
  2. Clock - ROS Wiki, https://wiki.ros.org/Clock
  3. [ROS2][RViz2] can’t compare times with different time sources - ROS Answers archive, https://answers.ros.org/question/337851/
  4. ROS2 Clock - Isaac Sim Documentation, https://docs.isaacsim.omniverse.nvidia.com/4.5.0/ros2_tutorials/tutorial_ros2_clock.html
  5. ros2 - What is the difference between RCL_STEADY_TIME …, https://robotics.stackexchange.com/questions/105321/what-is-the-difference-between-rcl-steady-time-rcl-system-time-and-rcl-ros-time
  6. rostime: ros::SteadyTime Class Reference, http://docs.ros.org/en/melodic/api/rostime/html/classros_1_1SteadyTime.html
  7. Enum rcl_clock_type_e — rcl: Humble 5.3.10 documentation, https://docs.ros.org/en/humble/p/rcl/generated/enum_time_8h_1abe9b464041de61b4ac9e264eb18d4749.html
  8. ROS Time and use_sim_time · Issue #350 · ros2/ros2 - GitHub, https://github.com/ros2/ros2/issues/350
  9. Class Clock — rclcpp: Humble 16.0.15 documentation, https://docs.ros.org/en/humble/p/rclcpp/generated/classrclcpp_1_1Clock.html
  10. Understanding parameters — ROS 2 Documentation: Humble documentation, https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Parameters/Understanding-ROS2-Parameters.html
  11. ROS2 Clock - Isaac Sim Documentation - NVIDIA, https://docs.isaacsim.omniverse.nvidia.com/latest/ros2_tutorials/tutorial_ros2_clock.html
  12. Ros2: use_sim_time leads to inconsistent clocks - ROS General - Open Robotics Discourse, https://discourse.openrobotics.org/t/ros2-use-sim-time-leads-to-inconsistent-clocks/42030
  13. ros2 - ROS 2 time handling - Robotics Stack Exchange, https://robotics.stackexchange.com/questions/86435/ros-2-time-handling
  14. ros2_control extra bits - Articulated Robotics, https://articulatedrobotics.xyz/tutorials/mobile-robot/applications/ros2_control-extra/
  15. ROS 2 Clock - Isaac Sim Documentation, https://docs.isaacsim.omniverse.nvidia.com/5.1.0/ros2_tutorials/tutorial_ros2_clock.html
  16. Of clocks and simulation, betimes and otherwise - ROS General - Open Robotics Discourse, https://discourse.openrobotics.org/t/of-clocks-and-simulation-betimes-and-otherwise/1587
  17. rclcpp::Time Class Reference - ROS Documentation, https://docs.ros2.org/foxy/api/rclcpp/classrclcpp_1_1Time.html
  18. ROS2.0 Timing/Clock implementation for use with Foxy : r/ROS - Reddit, https://www.reddit.com/r/ROS/comments/hyxpj3/ros20_timingclock_implementation_for_use_with_foxy/
  19. Class Duration — rclcpp: Rolling 30.1.1 documentation, https://docs.ros.org/en/rolling/p/rclcpp/generated/classrclcpp_1_1Duration.html
  20. rclpy.time module - ROS documentation, https://docs.ros.org/en/iron/p/rclpy/rclpy.time.html
  21. Using time (Python) — ROS 2 Documentation: Foxy documentation, https://docs.ros.org/en/foxy/Tutorials/Intermediate/Tf2/Learning-About-Tf2-And-Time-Py.html
  22. Traveling in time (Python) — ROS 2 Documentation, https://docs.ros.org/en/galactic/Tutorials/Intermediate/Tf2/Time-Travel-With-Tf2-Py.html
  23. Is there a more elegant way to get the time in seconds in a ROS 2 node? (taken over from ROS Answers), https://robotics.stackexchange.com/questions/111251/is-there-a-more-elegant-way-to-get-the-time-in-seconds-in-a-ros-2-node-taken-o
  24. Node — rclpy 0.6.1 documentation, https://docs.ros2.org/foxy/api/rclpy/api/node.html
  25. rclcpp::Clock Class Reference - ROS Documentation, https://docs.ros2.org/dashing/api/rclcpp/classrclcpp_1_1Clock.html
  26. How to sync time between Robot and Host machine for ROS2 | by RoboFoundry - Medium, https://robofoundry.medium.com/how-to-sync-time-between-robot-and-host-machine-for-ros2-ecbcff8aadc4
  27. Real-time control in ROS and ROS 2.0, https://roscon.ros.org/2015/presentations/RealtimeROS2.pdf
  28. Proposal for Implementation of Real-time Systems in ROS 2 - ROS2 Design, https://design.ros2.org/articles/realtime_proposal.html
  29. Time or rclcpp::Duration in custom messages or in header.stamp · Issue #1371 - GitHub, https://github.com/ros2/ros2/issues/1371
  30. ROS2 Code Sample - Ruixiang’s Notes, https://notes.rdu.im/robotics/ros/ros2_code_sample/
  31. Documentation for ROS2 Humble : r/ROS - Reddit, https://www.reddit.com/r/ROS/comments/1iky83t/documentation_for_ros2_humble/