ROS2 기반 멀티 컴포넌트 시스템 성능 측정

hyoin·2026년 2월 8일

ROS2 부트캠프

목록 보기
6/11
post-thumbnail

1. 성능 측정 배경

1.1 성능 측정의 이유

초기 우려사항:

이 시스템은 여러 컴포넌트가 HTTP로 연결된 분산 아키텍처입니다:

Robot → (ROS2) → malle_service → (HTTP) → AI Service → (HTTP) → Web Service

HTTP 통신의 잠재적 문제:

  • TCP 기반: 3-way handshake, 연결 설정 오버헤드
  • 요청-응답 모델: 동기적 대기 시간
  • 다중 홉: 여러 서비스를 거치며 지연 누적

대안으로 고려했던 것:

  • UDP 통신: 연결 설정 없이 빠른 전송
  • gRPC: HTTP/2 기반 고성능 RPC

1.2 핵심 질문

"HTTP 통신이 병목이 될까? UDP를 필수적으로 적용해야 할까?"

이를 확인하기 위해 성능 측정을 진행했습니다.

1.3 측정 목표

  1. 전체 파이프라인 지연 시간 측정

    • 로봇 메시지 발행 → 웹 UI 표시까지 총 시간
  2. 구간별 병목 지점 파악

    • AI 처리 vs HTTP 통신 vs 오버헤드
  3. 궁극적으론, UDP 적용 필요성 판단

    • HTTP 통신 오버헤드가 허용 가능한 수준인가?
    • 실시간성이 보장되는가?

1.4 허용 가능한 지연 시간

요구사항:

  • 로봇 센서 데이터: 1초에 1회 발행
  • AI 처리 시간: ~1초 (모델 추론)
  • 목표 전체 지연: 2초 이내
  • 허용 통신 오버헤드: 100ms 이내

가설:
아래 2개 상황일 경우, UDP 전환 없이도 충분히 실시간성을 확보할 수 있음:

  • AI 처리가 주요 병목일 것 (1초)
  • HTTP 통신 오버헤드는 수십 ms 수준일 것

검증 계획:

1분간 메시지를 연속 전송하며 다음 요소를 측정:

  • 평균 지연 시간
  • 최대 지연 시간 (P99)
  • 안정성 (표준편차)
  • 각 구간별 비율

결과를 바탕으로 UDP 전환 필요 여부를 결정하고자 했습니다.


2. 시스템 아키텍처

2.1 개요

이번에 진행한 Malle 프로젝트는 다중 로봇 제어 시스템으로, 여러 대의 자율주행 로봇을 동시에 관리하고 모니터링합니다. 각 로봇의 센서 데이터를 수집하여 AI로 분석하고, 그 결과를 웹 대시보드에서 실시간으로 확인할 수 있습니다.

프로젝트 배경:

  • 복수의 로봇이 동일한 작업 공간에서 협업
  • 각 로봇을 개별적으로 식별하고 제어해야 함
  • 중앙 서버에서 모든 로봇의 상태를 통합 관리
  • 관리자가 웹 UI에서 실시간 모니터링

2.2 통신 흐름

통신 흐름은 Sequence Diagram으로 미리 설계했습니다.
업로드중..

2.3 컴포넌트별 역할

컴포넌트기술 스택포트역할
Robot Publisher (다이어그램 내 bot)ROS2 Humble-센서 데이터 발행 (다중 로봇)
malle_service (다이어그램 내 malle server)ROS2 + FastAPI8000ROS Subscriber + 중앙 조정
malle_ai_service (다이어그램 내 malle AI server)Flask5000AI 데이터 분석
malle_web_service (다이어그램 내 malle web server)FastAPI8001WebSocket 브로드캐스트
Web UI (다이어그램 내 malle UI server)HTML/JS3000실시간 모니터링

2.4 메시지 포맷

설계 배경:

다중 로봇 시스템에서는 각 메시지의 출처를 명확히 식별하는 것이 필수입니다. 단순히 센서 데이터만 보내면:

  • 어떤 로봇에서 온 데이터인지 알 수 없음
  • 메시지 순서 보장 불가 (네트워크 지연 시)
  • 중복 메시지 감지 불가
  • 디버깅 및 추적 곤란

따라서 풍부한 메타데이터를 포함한 Header 구조를 설계했습니다:

# RobotMessage (ROS2 Custom Message)
Header header
  string message_id        # UUID - 메시지 고유 식별자 (중복 감지용)
  int64 timestamp_sec      # Unix timestamp (초) - 발행 시간
  int64 timestamp_nsec     # 나노초 - 고정밀 타임스탬프
  string robot_id          # 로봇 식별자 (예: "robot_001", "robot_002")
  string message_type      # 메시지 타입 (예: "status", "alert", "command")
  int32 priority           # 우선순위 (긴급 메시지 처리용)
  int32 sequence           # 시퀀스 번호 (메시지 순서 보장)

# Body (실제 센서 데이터)
float64 battery            # 배터리 잔량 (%)
string robot_status        # 상태 ("running", "idle", "error")
string command             # 제어 명령
string error_message       # 에러 메시지

각 필드의 역할:

필드용도예시
message_id메시지 중복 제거, 추적"7f939-3a0a-4742-91cc"
robot_id로봇 식별"robot_001", "robot_A"
timestamp_*메시지 발행 시간 (성능 측정)1770365590.123456789
message_type메시지 분류"status", "alert"
priority긴급도 (높을수록 우선 처리)1 (일반), 5 (긴급)
sequence순서 보장 (패킷 손실 감지)0, 1, 2, 3...

3. 성능 측정 관련 트러블 슈팅

3.1 분산 시스템의 시간 동기화

문제 상황:

# 로봇에서 타임스탬프 설정
publish_time = time.time()
msg.header.timestamp_sec = int(publish_time)

# malle_service에서 수신
receive_time = datetime.now()
ros_latency = (receive_time - publish_time).total_seconds()
# 결과: 1.684초 (비정상적으로 큼)

원인:

  • time.time()datetime.now()의 미묘한 차이가 있었음.
  • 메시지 타임스탬프가 과거 시점으로 잘못 설정될 가능성 존재.
  • 서로 다른 기준점(발행 시간 vs 수신 시간)으로 측정됨.

해결:

# 수신 시간을 기준점으로 통일
def listener_callback(self, msg):
    start_time = datetime.now()
    asyncio.run(self.process_message(msg, start_time))

4. QoS 최적화

4.1 ROS2 QoS 개념

ROS2는 DDS 기반으로, QoS 설정을 통해 통신 특성을 조정할 수 있습니다.

주요 QoS 파라미터:

파라미터옵션의미
ReliabilityRELIABLE모든 메시지 보장 (느림)
BEST_EFFORT최선 노력, 손실 가능 (빠름)
HistoryKEEP_ALL모든 메시지 보관
KEEP_LAST최신 N개만 보관
Depth1~N보관할 메시지 개수
DurabilityTRANSIENT_LOCAL구독자 연결 전 메시지 보관
VOLATILE현재 구독자에게만 전송

4.2 RELIABLE vs BEST_EFFORT

RELIABLE (신뢰성 우선):

qos_profile = QoSProfile(
    reliability=ReliabilityPolicy.RELIABLE,
    history=HistoryPolicy.KEEP_LAST,
    depth=10
)

특징:

  • 메시지 손실 없음
  • 재전송 오버헤드로 느림
  • 사용 사례: 중요한 제어 명령

BEST_EFFORT (속도 우선):

qos_profile = QoSProfile(
    reliability=ReliabilityPolicy.BEST_EFFORT,
    history=HistoryPolicy.KEEP_LAST,
    depth=1
)

특징:

  • 낮은 지연시간
  • 네트워크 혼잡 시 메시지 손실 가능
  • 사용 사례: 센서 데이터, 모니터링

4.3 적용 결과

Publisher (test_publisher.py):

from rclpy.qos import QoSProfile, ReliabilityPolicy, HistoryPolicy, DurabilityPolicy

qos_profile = QoSProfile(
    reliability=ReliabilityPolicy.BEST_EFFORT,
    history=HistoryPolicy.KEEP_LAST,
    depth=1,
    durability=DurabilityPolicy.VOLATILE
)

self.publisher_ = self.create_publisher(
    RobotMessage,
    'robot_test_topic',
    qos_profile
)

Subscriber (malle_service):

# Publisher와 동일한 QoS 설정 필요
qos_profile = QoSProfile(
    reliability=ReliabilityPolicy.BEST_EFFORT,
    history=HistoryPolicy.KEEP_LAST,
    depth=1,
    durability=DurabilityPolicy.VOLATILE
)

self.subscription = self.create_subscription(
    RobotMessage,
    'robot_test_topic',
    self.listener_callback,
    qos_profile
)

효과:

  • 안정적인 latency
  • 표준편차 감소 (=변동성이 낮아짐)

5. 결과 및 분석

5.1 최종 성능 지표

1분간 측정 (58개 메시지):

============================================================
📊 통계 분석 결과 (수신 시간 기준)
============================================================
총 메시지 수: 58개
수집 기간: 60초
초당 평균 메시지: 0.97개/초

------------------------------------------------------------
단계                 평균        최소        최대      중앙값
------------------------------------------------------------
AI 처리             1.006초     1.003초     1.007초     1.006초
웹 전송             0.003초     0.003초     0.003초     0.003초
오버헤드            0.028초     0.022초     0.067초     0.025초
전체                1.037초     1.029초     1.077초     1.034초
------------------------------------------------------------

📈 백분위수 분석 (전체 처리 시간)
------------------------------------------------------------
P50: 1.034초 (50%의 요청이 이 시간 이내 처리)
P75: 1.037초 (75%의 요청이 이 시간 이내 처리)
P90: 1.051초 (90%의 요청이 이 시간 이내 처리)
P95: 1.057초 (95%의 요청이 이 시간 이내 처리)
P99: 1.077초 (99%의 요청이 이 시간 이내 처리)

📊 각 단계별 시간 비율
------------------------------------------------------------
AI 처리:       97.00% (1.000초)
웹 전송:        0.29% (0.003초)
오버헤드:       2.71% (0.028초)
총합:         100.00% (1.037초)

✓ 합계 검증 통과 (차이: 0.000000초)

📉 표준편차 (안정성 지표)
------------------------------------------------------------
AI 처리           0.001초
웹 전송            0.000초
오버헤드            0.008초
전체              0.009초
============================================================

5.2 병목 분석

병목 순위:

  1. AI 처리: 97.00% (1.000초)

    • 의도된 병목 (AI 모델 추론 시간)
  2. 오버헤드: 2.71% (0.028초)

    • Python asyncio 오버헤드
    • HTTP 클라이언트 생성/소멸
    • 개선 방법: 연결 풀링, 코드 최적화
  3. 웹 전송: 0.29% (0.003초)

    • 로컬 네트워크 (localhost) 사용 중
    • 이미 최적화됨

결론: AI 처리 외 통신 오버헤드는 3% 미만으로 매우 효율적입니다.

5.3 HTTP vs UDP 판단

측정 결과 기반 의사결정:

전체 통신 오버헤드 (웹 전송 + 오버헤드): 
  0.003초 + 0.028초 = 0.031초 (31ms)

판단:

항목측정값목표값평가
총 지연 시간1.037초< 2초✅ 통과
통신 오버헤드31ms< 100ms✅ 통과
안정성 (σ)0.009초-✅ 매우 안정적
P99 지연1.077초< 2초✅ 통과

결론:

HTTP 통신으로 충분하다. UDP 전환은 불필요.

나중에 리팩토링해도 되겠지만, 현재의 스펙에선 굳이 UDP로 바꾸지 않아도 실시간성이 유지된다.

의사결정:

현재 시스템에선 HTTP로 충분히 실시간성을 만족하므로, 불필요한 복잡도를 추가하지 않기로 결정했습니다.

📚 참고 자료

profile
배워야 할 게 많은 개발자... 하지만 공부를 포기하지 않지!!

0개의 댓글