# 시간 지연(Latency) 문제 해결 방안

#### 1. 시간 지연의 원인

ROS2에서는 노드 간 통신이 네트워크를 통해 이루어지므로, 다양한 요소로 인해 시간 지연(latency)이 발생할 수 있다. 이러한 지연은 시스템의 실시간성을 저하시킬 수 있으며, 특히 분산 시스템에서 큰 문제로 작용할 수 있다. 주요 원인으로는 다음과 같은 것들이 있다:

* **네트워크 트래픽**: 네트워크 상의 데이터 전송이 지연되는 경우.
* **큐 대기 시간**: 메시지가 큐에서 처리되기까지 대기하는 시간.
* **노드 간 시간 동기화 문제**: 노드 간 시간이 일치하지 않으면 시간 오차가 발생할 수 있다.
* **QoS 설정의 부적절한 구성**: 품질 서비스(QoS) 설정이 적절하지 않으면 성능 저하를 유발할 수 있다.

#### 2. 시간 지연 측정 방법

시간 지연을 해결하기 위해서는 먼저 지연을 정확하게 측정해야 한다. 이를 위해 두 노드 간의 통신 시간 차이를 측정하는 것이 일반적이다. ROS2에서는 `rclcpp::Clock`과 같은 내장된 시간 추적 도구를 사용할 수 있다.

**송신 측정**

송신 노드에서 메시지를 보낼 때의 시간을 기록한다. 이 시간을 $t\_s$라고 하자.

$$
t\_s = \text{clock.now()}
$$

**수신 측정**

수신 노드에서 메시지를 받을 때의 시간을 $t\_r$라고 하자.

$$
t\_r = \text{clock.now()}
$$

**지연 계산**

시간 지연 $\Delta t$는 단순히 송신 시간과 수신 시간의 차이로 계산할 수 있다.

$$
\Delta t = t\_r - t\_s
$$

여기서 $\Delta t$는 네트워크, 처리 시간, 스케줄링 지연 등을 모두 포함한 총 시간 지연을 나타낸다.

#### 3. 지연 해결을 위한 방법

**네트워크 최적화**

네트워크 트래픽을 줄이는 방법 중 하나는 데이터의 양을 최소화하는 것이다. 이를 위해 토픽 메시지의 크기를 줄이는 것이 필요할 수 있다. 또한, 네트워크가 병목이 되지 않도록 QoS 설정을 최적화할 수 있다.

* **QoS 설정 조정**: 특히, `reliability`와 `history` 설정을 조정하여 데이터를 불필요하게 다시 전송하거나 대기하는 상황을 방지할 수 있다.

```cpp
rclcpp::QoS qos_profile(10);
qos_profile.reliability(RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT);
qos_profile.history(RMW_QOS_POLICY_HISTORY_KEEP_LAST);
```

**큐 대기 시간 최적화**

메시지가 큐에 너무 오래 머물러 있지 않도록 적절한 스레드 설정과 자원 할당을 통해 대기 시간을 줄이는 방법이 있다. 이를 위해 메시지 큐의 크기를 조정하고, 필요에 따라 멀티스레딩을 도입할 수 있다.

**네임스페이스와 리매핑 사용**

노드의 네임스페이스 및 리매핑을 통해 통신 경로를 단순화할 수 있다. 이를 통해 복잡한 통신 구조로 인한 지연을 줄이는 것이 가능하다. 네임스페이스 및 리매핑 설정은 주로 노드의 통신 경로를 명확히 하고, 필요하지 않은 경로를 제거함으로써 통신 지연을 줄이는 데 기여할 수 있다.

#### 4. 시간 동기화

분산 시스템에서는 각 노드의 시간이 일관되게 유지되는 것이 중요하다. 이를 위해 시간 동기화를 수행하여 시간 지연을 최소화할 수 있다. ROS2에서는 `Time Synchronizer`와 같은 기능을 통해 노드 간 시간을 일치시킬 수 있다.

**Time Synchronizer**는 다수의 센서 데이터에서 시간 동기화된 데이터를 제공하기 위한 도구이다. 이를 사용하면 다중 노드 간 통신 시 시간 차이를 보정할 수 있다.

```cpp
#include <message_filters/subscriber.h>
#include <message_filters/time_synchronizer.h>

message_filters::Subscriber<sensor_msgs::Image> image_sub(node, "camera/image", 1);
message_filters::Subscriber<sensor_msgs::Imu> imu_sub(node, "imu/data", 1);

message_filters::TimeSynchronizer<sensor_msgs::Image, sensor_msgs::Imu> sync(image_sub, imu_sub, 10);
sync.registerCallback(boost::bind(&callback, _1, _2));
```

#### 5. 시간 동기화 기법

**네트워크 시간 프로토콜(NTP)**

NTP는 네트워크 상에서 노드 간의 시간을 동기화하는 데 사용되는 표준 프로토콜이다. 분산 시스템에서 각 노드의 시간을 정밀하게 동기화하기 위해 활용할 수 있다.

NTP는 전 세계 표준 시간 서버와 연결하여 각 노드의 시간을 맞추는 방식으로, 각 노드의 시간 차이를 최소화하여 시간 지연 문제를 완화할 수 있다.

**PTP(정밀 시간 프로토콜)**

보다 높은 정확도를 요구하는 경우, PTP(정밀 시간 프로토콜)를 사용할 수 있다. PTP는 하드웨어 기반의 시간 동기화로, NTP보다 더 정밀한 시간 동기화를 제공한다. PTP는 주로 산업용 네트워크에서 사용되며, ROS2 환경에서도 적용 가능하다.

{% @mermaid/diagram content="sequenceDiagram
participant A as 송신 노드
participant B as 수신 노드
Note over A,B: 시간 동기화 과정
A->>B: 데이터 전송 (t\_s)
B->>A: 응답 (t\_r)
A->>B: 시간 차이 계산 및 보정" %}

#### 6. 시간 지연 보정

**타임스탬프 보정**

노드 간의 시간 지연을 줄이기 위해서는 수신된 메시지의 타임스탬프를 적절히 보정해야 한다. 송신 노드에서의 타임스탬프와 수신 노드에서의 타임스탬프를 비교하여 지연 시간을 계산하고, 해당 값을 보정하는 방식으로 시간 동기화가 가능하다.

$$
t\_{\text{corrected}} = t\_r - \Delta t
$$

여기서 $t\_{\text{corrected}}$는 보정된 시간, $t\_r$는 수신 노드에서 측정된 시간, $\Delta t$는 앞서 정의된 지연 시간이다. 이를 통해 데이터 수신 시간과 실제 발생 시간 간의 차이를 줄일 수 있다.

**동적 QoS 설정**

ROS2의 QoS 정책은 기본적으로 정적이지만, 특정 상황에서는 동적으로 QoS를 변경하여 네트워크 상태에 맞춰 성능을 조정할 수 있다. 예를 들어, 네트워크 부하가 심할 때는 `best effort` 방식으로 QoS 설정을 변경하여 지연을 줄일 수 있고, 중요한 데이터는 `reliable` 설정을 유지할 수 있다.

```cpp
// QoS 설정을 동적으로 변경하는 코드 예제
auto qos_profile = rclcpp::QoS(10);
qos_profile.reliability(RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT);
qos_profile.history(RMW_QOS_POLICY_HISTORY_KEEP_LAST);

publisher->set_qos(qos_profile);
```

**시간 기반 필터 적용**

데이터의 타임스탬프를 기준으로 시간 지연을 감지하고, 그에 따라 데이터 필터링을 적용할 수 있다. 오래된 데이터를 무시하고 최신 데이터만 처리하는 방식이다. 이를 통해 시간 지연이 발생하더라도 실시간 데이터 처리가 가능하도록 보장할 수 있다.

```cpp
// 오래된 데이터를 무시하는 시간 필터링 예제
if (current_time - message_time > time_threshold) {
    // 오래된 데이터는 처리하지 않음
    return;
}
```

**예측 기반 보정**

시간 지연을 최소화하는 또 다른 방법은 예측 기반의 보정이다. 노드 간 통신 시 발생하는 지연을 예측하여 데이터를 미리 보정할 수 있다. 이를 위해서는 Kalman Filter와 같은 예측 알고리즘을 사용할 수 있으며, 센서 데이터의 변화 추이를 기반으로 시간 지연을 보정하는 방식이다.

**Kalman Filter 기반 보정**

Kalman Filter는 센서 데이터의 변동성을 예측하여 지연된 데이터를 실시간으로 보정하는 데 유용하다. 기본적인 Kalman Filter의 상태 업데이트 방정식은 다음과 같다:

$$
\mathbf{x}*{k} = \mathbf{F} \mathbf{x}*{k-1} + \mathbf{B} \mathbf{u}\_k + \mathbf{w}\_k
$$

여기서:

* $\mathbf{x}\_k$: 현재 상태 벡터
* $\mathbf{F}$: 상태 전이 행렬
* $\mathbf{B}$: 제어 입력 행렬
* $\mathbf{u}\_k$: 제어 입력 벡터
* $\mathbf{w}\_k$: 시스템 노이즈

Kalman Filter는 상태 벡터의 예측과 함께 실제 센서 데이터와의 차이를 보정하는 방식으로 시간 지연을 보완할 수 있다.

**타이머 기반 주기적 작업**

ROS2에서는 주기적인 작업을 타이머로 처리할 수 있으며, 주기적 작업의 시간 지연을 방지하기 위해 타이머의 정확한 주기를 설정하는 것이 중요하다. 타이머 주기를 줄이면 데이터 갱신 속도가 빨라져 지연을 최소화할 수 있다. 타이머의 주기는 응용 프로그램의 요구 사항에 따라 적절하게 조정해야 한다.

```cpp
// 타이머 설정 예제
auto timer = node->create_wall_timer(
    std::chrono::milliseconds(100),  // 100ms 주기
    std::bind(&NodeClass::timer_callback, this)
);
```

#### 7. 멀티스레딩 및 멀티프로세싱을 통한 지연 문제 해결

**멀티스레딩 적용**

노드 간 통신에서 발생하는 시간 지연을 줄이기 위한 방법 중 하나는 멀티스레딩을 사용하는 것이다. 멀티스레딩을 통해 여러 작업을 동시에 처리함으로써 대기 시간을 줄일 수 있다. ROS2는 `rclcpp::executors::MultiThreadedExecutor`를 통해 멀티스레딩을 지원하며, 이를 사용하면 각 콜백이 별도의 스레드에서 실행되어 지연 시간을 최소화할 수 있다.

```cpp
// 멀티스레딩 적용 예제
rclcpp::executors::MultiThreadedExecutor executor;
auto node = std::make_shared<MyNode>();
executor.add_node(node);
executor.spin();
```

**멀티프로세싱 적용**

특정 작업이 CPU 자원을 많이 사용하는 경우, 멀티스레딩보다는 멀티프로세싱을 사용하는 것이 유리할 수 있다. 멀티프로세싱을 통해 각 프로세스가 독립적으로 실행되기 때문에 서로의 작업에 영향을 주지 않으며, 작업 병렬성을 더욱 극대화할 수 있다.

ROS2는 노드를 별도의 프로세스로 실행할 수 있는 구조이므로, 멀티프로세싱을 활용하여 네트워크 병목이나 CPU 부하로 인한 시간 지연을 줄일 수 있다. 예를 들어, 각 노드를 개별 프로세스로 분리하고, 서로 독립적으로 동작하게 하여 실시간성을 유지할 수 있다.

**실시간 운영 체제(RTOS) 사용**

만약 하드웨어 환경에서 ROS2를 운영하고 있다면, 리눅스 기반의 실시간 운영 체제(RTOS)를 사용하는 것도 시간 지연을 줄이는 좋은 방법이다. ROS2는 실시간 기능을 지원하므로, 노드의 실행을 실시간으로 처리하여 시간 지연을 최소화할 수 있다. 이때 ROS2의 `Lifecycle` 기능을 활용하여 노드의 상태를 관리하고, 노드의 활성화와 비활성화를 실시간으로 제어할 수 있다.

#### 8. 네트워크 트래픽 제어

**DDS 설정 최적화**

ROS2는 DDS(데이터 분산 서비스)를 기반으로 통신을 수행하므로, DDS 설정을 최적화하여 네트워크 트래픽을 제어할 수 있다. 특히, QoS 설정에서 `reliability`와 `history`를 적절하게 설정함으로써 네트워크 트래픽을 줄일 수 있다.

* **`reliability`:** 데이터 손실을 허용할 수 있는 경우, `best effort`로 설정하여 지연을 줄일 수 있다.
* **`history`:** 토픽의 메시지를 쌓아두는 개수를 조정하여, 필요 이상으로 많은 데이터를 보유하지 않도록 한다.

```cpp
// DDS 설정 최적화 예제
rclcpp::QoS qos_profile(10);
qos_profile.reliability(RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT);
qos_profile.history(RMW_QOS_POLICY_HISTORY_KEEP_LAST);

auto publisher = node->create_publisher<std_msgs::msg::String>("topic", qos_profile);
```

**메시지 압축**

네트워크를 통해 전송되는 데이터의 크기를 줄이면 시간 지연을 줄일 수 있다. ROS2에서는 메시지의 크기를 줄이기 위해 압축을 사용하는 방법도 있다. 메시지를 전송할 때 압축하여 네트워크 트래픽을 줄이고, 수신한 후에는 다시 압축을 해제하는 방식으로 지연을 줄일 수 있다.

* **Zstandard (Zstd) 또는 LZ4**: 압축 알고리즘을 적용하여 데이터 크기를 줄일 수 있다.

#### 9. 성능 모니터링 및 문제 해결

**네트워크 상태 모니터링**

지연 문제가 발생할 경우, 먼저 네트워크 상태를 모니터링하는 것이 중요하다. 네트워크 대역폭, 트래픽 양, 패킷 손실 등을 실시간으로 모니터링하여 병목이 발생하는 구간을 파악할 수 있다. ROS2의 네트워크 트래픽을 모니터링하는 도구로는 Wireshark와 같은 네트워크 분석 툴을 사용할 수 있다.

**rqt\_console와 rqt\_logger\_level**

ROS2 시스템의 로그를 실시간으로 확인할 수 있는 도구인 `rqt_console`과 `rqt_logger_level`을 통해 시간 지연 문제를 추적할 수 있다. 특정 노드에서 발생하는 지연 문제를 로그로 확인하고, 문제의 원인을 분석하여 해결할 수 있다.

```bash
# rqt_console 사용 예제
ros2 run rqt_console rqt_console
```

```bash
# rqt_logger_level 사용 예제
ros2 run rqt_logger_level rqt_logger_level
```

**실시간 모니터링 및 성능 최적화 도구**

ROS2에서는 실시간으로 시스템 성능을 모니터링할 수 있는 도구들이 있다. 특히, `ros2 topic`과 `ros2 service` 명령어를 통해 각 토픽과 서비스의 상태를 모니터링할 수 있으며, 지연이 발생하는 구간을 파악할 수 있다.

```bash
# ros2 topic 명령어 예제
ros2 topic echo /topic_name
```

```bash
# ros2 service 명령어 예제
ros2 service call /service_name
```
