# 타이밍 관련 디버그 팁

#### 디버그 출력을 통한 시간 정보 확인하기

ROS2에서는 노드 내에서 시계(Clock)가 올바른 시간값을 제공하는지 점검하는 것이 중요하다. 예를 들어, ROS2가 제공하는 `rclcpp::Node` 내부에서 타임스탬프를 출력하고, 이를 다른 노드 혹은 토픽에서 수신해 확인하는 방식으로 시간 동기 과정을 디버그할 수 있다.

* 먼저, 타임스탬프를 얻어오는 코드를 간단히 작성할 수 있다:

```cpp
rclcpp::Clock clock(RCL_ROS_TIME);
auto now = clock.now();
RCLCPP_INFO(this->get_logger(), "현재 시각: %ld.%09ld", now.nanoseconds() / 1000000000, now.nanoseconds() % 1000000000);
```

* 이렇게 출력되는 시각이 실제 물리 시각과 정확히 일치하는지, 또는 시뮬레이션에서의 논리 시각과 일치하는지 비교해볼 수 있다.

#### 토픽 간 전파 지연 측정하기

ROS2에서 토픽 기반 통신을 할 때, 발행 시각과 수신 시각 사이의 지연이 얼마나 발생하는지 측정하여 디버그할 수 있다. 발행 노드의 타임스탬프와 수신 노드에서의 수신 타임스탬프를 비교해보면 된다. 예컨대, 다음과 같은 절차를 활용할 수 있다.

1. 발행 노드에서 메시지를 생성한 시각을 메시지 자체에 넣어 발행한다.
2. 수신 노드에서는 수신과 동시에 Clock을 통해 현재 시각을 얻는다.
3. 두 시각을 비교하여 지연 시간을 로깅한다.

```cpp
// 발행 노드
auto time_now = clock_.now();
custom_msg.stamp = time_now;
publisher_->publish(custom_msg);

// 수신 노드
void subscriberCallback(const CustomMsg::SharedPtr msg)
{
  auto receive_time = clock_.now();
  auto diff = receive_time - msg->stamp;
  RCLCPP_INFO(this->get_logger(), "전파 지연(ns): %ld", diff.nanoseconds());
}
```

이를 통해 네트워크 지연, DDS 설정 등 다양한 요인에 의해 발생할 수 있는 전송 지연 문제를 가시화할 수 있다.

#### Monotonic Clock과 ROS Clock 비교 점검

ROS2에서는 두 가지 주요 시계 소스를 활용할 수 있다. 하나는 ROS Time(`RCL_ROS_TIME`)이고, 다른 하나는 System Time(`RCL_SYSTEM_TIME`) 또는 Steady Clock이다. 시뮬레이션 혹은 기록 재생(rosbag playback) 과정에서 `RCL_ROS_TIME` 사용 시에, 시스템 클록과의 차이를 디버깅하기 위해 아래와 같은 사항들을 점검한다.

1. 실제 물리 시계가 사용되는지, 시뮬레이션 시간(시뮬레이터 클록)인지 확인한다.
2. Steady Clock(`std::chrono::steady_clock`)을 사용해 일정 구간의 시간 경과를 측정하고, ROS Clock 시간과의 차이를 비교한다. 이때 단순 로그 출력 외에도 별도 로그 파일이나 ros2 bag 기록을 통해 후처리로 비교할 수 있다.

> 예를 들어, 특정 콜백 함수가 호출되는 간격이 정상적인지 확인할 때, steady\_clock을 활용해 이전 호출과 현재 호출 간 간격을 측정하고, 동시에 ROS Time을 사용해 같은 간격을 측정한다. 만약 둘 간에 오차가 크면 시뮬레이션 타임 스케일이 달라졌을 가능성을 의심해볼 수 있다.

#### 노드 그래프 구조 시각화로 디버그하기

정확한 시간 흐름이 필요한 복잡한 네트워크 구조에서는 노드 간 종속 관계를 먼저 확인하고, 시간 정보를 어떻게 주고받는지 파악이 필요하다. 예를 들어 다음과 같은 다이어그램을 통해 노드 간 관계 및 시간값 사용 흐름을 시각화할 수 있다:

{% @mermaid/diagram content="flowchart LR
A\[Sensor Node] --> B\[Filter Node]
B --> C\[Time Sync Node]
C --> D\[Controller Node]" %}

* Sensor Node에서 시간 스탬프를 부여해 Filter Node로 보내는 구조인지,
* 중간에 Time Sync Node가 별도의 시간 동기화를 거치는지,
* Controller Node에서 최종적으로 어떤 시간 정보를 참조하는지 시각적으로 파악하면 디버그가 수월해진다.

#### ROS Clock 점검 시 수식 활용

ROS Clock과 System Clock 간의 시간 차이를 $\Delta T$라 하면, 특정 시점 $t$에서 다음과 같은 단계를 거쳐 측정할 수 있다.

1. $t$ 시점에서의 ROS Clock 시간: $T\_{\mathrm{ROS}}(t)$
2. $t$ 시점에서의 System Clock 시간: $T\_{\mathrm{SYS}}(t)$
3. 두 값의 차이:

$$
\Delta T(t) = T\_{\mathrm{ROS}}(t) - T\_{\mathrm{SYS}}(t)
$$

이 $\Delta T(t)$의 추이를 기록해보면, 시뮬레이션 시간과 시스템 시간이 얼마나 동기되어 있는지 혹은 드리프트가 있는지 가늠할 수 있다.

#### 타임 점프(Time Jump) 발생 케이스 검사

ROS2에서는 Time Source가 바뀌거나 시뮬레이션 환경에서 Time Jump가 발생할 수 있다. 예를 들어 rosbag 재생 시에 타임 점프가 잘못 처리가 되면 이후의 노드가 값을 잘못 계산하게 된다. 이를 방지하기 위해 `jump_callbacks`를 설정해 놓고, 점프가 발생했을 때 관련 로직(예: 타이머 재설정)을 디버깅할 수 있다.

```cpp
rclcpp::Clock::SharedPtr ros_clock_ = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
ros_clock_->on_time_jump(
  [](const rcl_time_jump_t & jump_info) {
    RCLCPP_WARN(rclcpp::get_logger("TimeJump"), "Time jump detected!");
    // 여기서 재설정 혹은 로깅 등 필요한 디버그 동작
  }
);
```

이 콜백을 통해 실제 시각이 점프했는지, 아니면 시뮬레이션 타임이 바뀐 것인지 확인하고 문제를 해결할 수 있다.

#### Timer 콜백 주기 검증

ROS2에서 Timer 콜백 함수가 일정한 주기로 정상 작동하는지 확인하는 것은 매우 중요하다. 특히, 제어 시스템처럼 주기가 엄격히 요구되는 경우 더욱 세밀한 점검이 필요하다. 일반적으로 다음과 같은 방법을 활용할 수 있다.

1. Timer 콜백 내부에서 Clock을 통해 현재 시각을 기록한다.
2. 이전 콜백 호출 시점과의 차이를 계산하여 기대 주기와 실제 주기가 일치하는지 비교한다.

```cpp
void timerCallback()
{
  static auto prev_time = clock_.now();
  auto current_time = clock_.now();
  auto diff = current_time - prev_time;
  double diff_sec = (double)diff.nanoseconds() / 1e9;

  RCLCPP_INFO(this->get_logger(), "Timer 콜백 간격(초): %f", diff_sec);

  // 주기 오차가 허용 범위를 벗어나면 디버그
  if (fabs(diff_sec - expected_period_) > tolerance_) {
    RCLCPP_WARN(this->get_logger(), "Timer 주기가 기대치에서 벗어났습니다.");
  }

  prev_time = current_time;
}
```

* 위와 같이 측정한 주기가 $t\_1, t\_2, \dots, t\_n$이라 할 때, 평균값은

$$
\bar{t} = \frac{1}{n} \sum\_{i=1}^{n} t\_i
$$

로 구할 수 있다.

* 분산도 간단히 계산해볼 수 있는데, 이는

$$
\sigma^2 = \frac{1}{n} \sum\_{i=1}^{n} (t\_i - \bar{t})^2
$$

와 같이 확인할 수 있다.

* 이를 통해 Timer 콜백 주기가 의도대로 유지되는지, 혹은 지연이나 불규칙이 발생하는지 진단할 수 있다.

#### 고속 데이터 처리 노드의 시간 검증

카메라, LiDAR 같은 센서에서 초당 수십\~수백 Hz의 토픽이 발생할 경우, 노드가 이를 적절히 처리하는지 검증해야 한다. 이때 주요 디버그 포인트는 아래와 같다.

1. 콜백 함수가 목표 데이터 처리율을 따라잡을 수 있는지 확인한다.
2. 각 콜백 내에서 `clock().now()` 혹은 고정된 Steady Clock을 통해 연산 소요 시간을 측정한다.
3. 시스템 리소스(CPU, 메모리)가 충분한지, 쓰레드 풀(thread pool) 설정이 적절한지 점검한다.

특히 고속 센서 데이터의 경우, 메시지 유실이 발생하는지 모니터링해야 한다. 로깅과 병행하여 ros2 topic echo 등을 사용해 실제 발행 속도와 수신 속도를 비교하면 문제 파악에 도움이 된다.

#### 멀티 스레드 상황에서 시간 레이스 컨디션(Race Condition) 점검

콜백 함수나 타이머 함수가 멀티 스레드로 동작할 경우, 시간정보 사용에 대한 동기화 문제가 발생할 수 있다. 다음과 같은 현상이 대표적이다.

* 콜백 함수마다 사용하는 Clock 인스턴스가 다르거나, 시점이 달라서 타임스탬프가 불일치하는 문제
* 데이터가 들어오기 전에 이미 Clock 기반 계산을 수행하여, 알 수 없는 에러나 경고 발생
* 메시지 큐에서 오래 지연된 후 처리되는 메시지 때문에, 최신 시간 정보와 맞지 않는 문제

이를 해결하기 위해서는 다음과 같은 방법을 고려한다.

1. Node 내에서 Clock 인스턴스를 단일화하거나, 싱글톤 패턴처럼 한 군데에서만 Clock을 관리한다.
2. 큐에 오래 머무른 메시지를 필터링하거나, 타임스탬프 기반으로 메시지 유효 여부를 판별한다.
3. 멀티 스레드 콜백 그룹(Callback Group)을 적절히 설정하고, 쓰레드 풀을 구성함으로써 예측 가능성을 높인다.

#### TF(Transform) 프레임 시간 불일치 디버깅

ROS2에서 TF 변환(geometry\_msgs/msg/TransformStamped 등)을 다루는 경우, 각 변환에 기록된 시간 스탬프가 다르면 TF 쿼리가 실패하거나 보간이 비정상적으로 이뤄질 수 있다.

* tf2 패키지를 통해 다음과 같은 오류를 자주 접할 수 있다: "Lookup would require extrapolation into the past/future" 이는 변환 시간 정보가 실제 시스템 시간과 불일치하거나, 너무 오래된 변환 정보를 사용하고 있을 때 발생한다.
* TF 브로드캐스터와 리스너 각각에서 시계 소스(ROS Time vs System Time)를 어떻게 쓰고 있는지 체크한다.
* TF를 시뮬레이션 시간에 맞추려면, 시뮬레이터가 제공하는 ROS Time으로 clock을 설정하고, TF 변환 메시지에 이를 확실히 반영해야 한다.

예시:

```cpp
geometry_msgs::msg::TransformStamped transform_stamped;
transform_stamped.header.stamp = clock_.now();
transform_stamped.header.frame_id = "world";
transform_stamped.child_frame_id = "robot_base";
// 변환 정보 설정
broadcaster_->sendTransform(transform_stamped);
```

* 이렇게 올바르게 stamp를 넣었는지, 브로드캐스터와 리스너가 같은 타입의 Clock을 사용하는지 확인함으로써 TF 시간 불일치 문제를 디버그할 수 있다.

#### 로깅/프로파일링 기반 디버그 기법

ROS2는 다양한 로깅 옵션과 프로파일링 툴을 제공하므로, 시간과 관련한 문제를 추적할 때 적극 활용하는 것이 좋다.

1. **ROS2 로깅 옵션**
   * ROS2에서 표준 로그 레벨(TRACE, DEBUG, INFO, WARN, ERROR, FATAL)을 적절히 배치하여, 시간 관련 로그를 세분화해 남길 수 있다.
   * `RCLCPP_INFO` 등 로깅 매크로에서 각 콜백이나 타이머가 실행된 시점, 메인 루프에서 처리된 시점 등을 기록한다.
2. **ros2\_tracing 활용**
   * ros2\_tracing 패키지를 이용하면 콜백 함수가 호출되는 시점과 실제 실행 사이의 지연, 시스템 자원 사용률 등을 비교적 정밀하게 측정할 수 있다.
   * LTTng(Linux Trace Toolkit Next Generation) 기반으로 동작하기 때문에, 콜백 간 간격, 스레드 스케줄링 등 시스템 레벨에서 발생하는 지연도 추적 가능하다.
   * 예컨대, 아래와 같이 설치하고 사용할 수 있다:

     ```bash
     sudo apt-get update
     sudo apt-get install ros-humble-ros2-tracing
     ```

     그리고 Launch 파일에서 tracing을 활성화하거나, 별도의 CLI 명령어를 통해 추적 세션을 시작할 수 있다.
3. **프로파일러(Perf, eBPF 등) 연동**
   * 시스템 차원의 프로파일러인 Perf, eBPF 등을 사용하여 CPU에서 어떤 함수나 스레드가 가장 오래 실행되고 있는지, 어떤 지점에서 지연이 발생하는지 파악할 수 있다.
   * 콜백이 많이 몰리는 특정 시점에 CPU 점유가 치솟아서 시간계산이 틀어지는 문제 등도 추적 가능하다.

#### ROS2 QoS 설정에 따른 시간 거동 분석

ROS2의 QoS(Quality of Service) 프로파일을 어떻게 설정하느냐에 따라, 토픽이 수신되는 타이밍이나 일관성이 크게 달라진다. 예를 들어:

1. **Reliability**:
   * `Reliable` 설정 시, 패킷 유실 없이 메시지를 꼭 전달하지만 네트워크 상태에 따라 지연이 발생할 수 있다.
   * `Best Effort` 설정 시, 지연보다는 처리량 유지가 가능하나 패킷 유실이 발생할 수 있다.
2. **Durability**:
   * `Transient Local`로 설정하면 발행 노드에서 보관한 최근 메시지를 새 구독자에게도 보낼 수 있는데, 이 과정에서 시간 차이가 생길 수 있다.
   * `Volatile`로 설정하면 최신 메시지만 전달한다.
3. **History와 Depth**:
   * 수신 측에서 지나치게 큰 버퍼(depth)를 두면 메시지가 오래 큐에서 대기하다가 처리가 늦어질 수 있다.
   * 너무 작은 버퍼(depth)로 설정하면 메시지가 소실되어 주기 분석이 어려워질 수 있다.

각 QoS 설정이 노드 간 시간 동기 및 데이터 전송 지연에 미치는 영향을 체계적으로 실험하고, 결과를 로그로 남겨 비교해야 한다.

#### Simulation vs. Real Hardware에서의 Clock 차이 디버그

Gazebo, Ignition, Webots 등 시뮬레이터에서 ROS Time(`RCL_ROS_TIME`)을 사용할 때와 실제 로봇 하드웨어에서 System Time(`RCL_SYSTEM_TIME`)을 사용할 때의 차이를 적절히 파악해야 한다.

* 시뮬레이터 환경에서 $\mathrm{Clock}$이 멈춰 있거나 느려질 수 있으며, Pause/Resume 기능을 통해 시계가 건너뛰는(시간점프) 일이 생길 수 있다.
* 실제 하드웨어 환경에서는 NTP(Network Time Protocol)나 Chrony 등을 통해 시간 동기화를 수행하지만, 네트워크 불안정이나 GPS 신호 이상으로 인한 오차가 발생할 수 있다.

따라서, 시뮬레이터와 실제 장비에서 모두 동일한 코드가 동작하더라도, 시계(Clock)와 타이밍 관련 문제는 전혀 다른 양상으로 나타날 수 있으므로 주의해야 한다.

#### 타이밍 디버그 시 주의사항

* **전역 변수나 정적 변수로 시간 정보를 저장할 경우** 여러 콜백이나 쓰레드에서 동시에 참조할 때 동기화 이슈가 발생할 수 있다. 예: 하나의 타임스탬프를 여러 스레드에서 업데이트하고 읽어들이면, 레이스 컨디션에 의해 일관성 없는 데이터가 발생할 수 있다.
* **ROS2 Lifecycle 노드 사용 시** 노드가 “inactive” 상태에서는 타이머나 콜백이 동작하지 않는다. 상태 전환 과정에서 일시적으로 시간 정보가 누락되는 경우가 있을 수 있다.
* **정확한 시간 확인을 위해 외부 장치(예: 하드웨어 타이머, 고정밀 시계 모듈) 사용** 경우에 따라서는 CPU 내부의 TSC(Time Stamp Counter)나 고정밀 타이머 모듈을 활용해야 할 수도 있다.
