로봇 코드를 노드로 나누는 이유와 ROS 2 분산 구조

단일 무한 루프로 작성한 로봇 소프트웨어가 센서 지연과 장애 전파에서 겪는 한계를 짚고, ROS 2가 인지·판단·제어 기능을 독립 노드와 발행-구독 구조로 분리하는 이유를 초보자 눈높이에서 설명합니다.

무료 공개 · 최근 수정

단일 무한 루프로 짠 로봇 코드가 부딪히는 세 가지 한계

파이썬이나 C++를 처음 배운 개발자가 소형 자율주행 로봇을 만든다고 가정해 보겠습니다. 가장 직관적인 접근은 main.py 파일 하나에 while True: 무한 루프를 만들고, 그 안에서 카메라 프레임을 읽고(인지), 장애물을 피할 방향을 계산한 뒤(판단), 바퀴 모터에 속도 명령을 보내는(제어) 코드를 순서대로 적는 것입니다.

책상 위에서 간단한 테스트를 할 때는 이 단일 프로그램도 잘 돌아갑니다. 하지만 카메라 해상도를 높이거나 딥러닝 객체 인식 모델을 넣는 순간 문제가 터집니다. 영상 한 장을 분석하는 데 180ms가 걸리면, 그 180ms 동안 바퀴 모터를 제어하는 코드도 함께 멈춰 로봇이 덜컥거리거나 벽에 부딪힙니다. ROS 2(Robot Operating System 2)는 바로 이 구조적 병목을 해결하기 위해 등장한 로봇 소프트웨어 프레임워크입니다.

모든 기능을 하나의 소스 파일과 단일 실행 흐름에 몰아넣은 모놀리식(Monolithic) 로봇 프로그램은 하드웨어가 복잡해질수록 세 가지 벽에 부딪힙니다.

while True:
    frame = camera.read()                 # 1. 카메라 읽기 (약 33ms)
    obstacles = heavy_ai_detector(frame)  # 2. 무거운 AI 추론 (180ms 소요 -> 전체 루프 정지!)
    cmd = compute_wheel_command(obstacles)
    motor_driver.set_velocity(cmd)        # 3. 모터 제어 주기가 180ms 이상으로 밀림

첫째는 실행 주기(Hz)의 충돌입니다. 로봇 안의 부품들은 각자 일하는 속도가 다릅니다. 카메라는 보통 초당 30번(30Hz, 약 33.3ms 간격) 영상을 찍고, 자율주행 경로나 시각-언어-행동(VLA) 정책 모델은 초당 510번(510Hz) 추론하며, 바퀴나 로봇 팔 관절 모터 제어기는 초당 1001,000번(1001,000Hz) 미세하게 전류와 속도를 보정해야 합니다. 하나의 순차 루프 안에서는 가장 느린 작업(AI 추론)이 끝날 때까지 가장 빠른 작업(모터 제어)이 기다려야 하므로 제어 주기가 무너집니다.

둘째는 연쇄 장애입니다. 로봇에 장착된 USB 카메라 케이블이 진동 때문에 잠깐 흔들려 드라이버에서 예외(Exception)가 발생했다고 해 보겠습니다. 모든 기능이 한 프로세스에 묶여 있으면 카메라 오류 하나 때문에 프로그램 전체가 종료되고, 달리던 로봇의 모터 제어기와 안전 브레이크 로직까지 동시에 꺼져 버립니다.

셋째는 부분 교체와 협업의 어려움입니다. 실제 로봇 하드웨어가 아직 준비되지 않아 3D 물리 시뮬레이터에서 주행 알고리즘을 테스트하고 싶어도, 센서 읽기 코드와 판단 코드가 한 덩어리로 얽혀 있으면 시뮬레이터용 코드로 바꿀 때마다 프로그램 전체를 뜯어고쳐야 합니다.

기능 단위를 독립 노드로 쪼개는 노드 그래프 구조

ROS 2는 이 문제를 풀기 위해 로봇 소프트웨어를 단일 목적의 작은 실행 단위로 쪼갭니다. 이렇게 분리된 최소 기능 단위를 노드(Node)라고 부릅니다. 예를 들어 레이저 거리 센서(LiDAR) 값을 읽는 /lidar_driver 노드, 장애물 회피 경로를 계산하는 /nav_planner 노드, 바퀴 모터를 돌리는 /wheel_ctrl 노드를 각각 별도의 노드로 만듭니다.

이 독립 노드들이 토픽이라는 데이터 통로로 연결되어 주행 파이프라인과 안전 감시 체계를 이루는 모습은 다음과 같습니다.

ROS 2 분산 노드 그래프와 장애 격리 구조

그림: 서로 다른 주기의 독립 노드와 안전 감시 노드가 토픽으로 연결된 ROS 2 노드 그래프

여러 노드가 데이터를 주고받으며 형성하는 전체 연결망을 노드 그래프(Node Graph)라고 합니다. 각 노드를 독립된 운영체제 프로세스로 띄우면 앞서 살펴본 단일 루프의 한계가 자연스럽게 사라집니다.

  1. 서로 다른 주기의 독립 실행: /lidar_driver는 30Hz로 거리 데이터를 내보내고, /nav_planner는 10Hz로 경로를 갱신하며, /wheel_ctrl은 마지막으로 받은 목표 속도를 기준으로 500Hz 고속 제어 루프를 멈춤 없이 유지합니다.
  2. 프로세스 수준의 분리: 각 노드가 별도의 프로세스 주소 공간을 쓰기 때문에 장애 격리(Fault Isolation)가 확보됩니다. 경로 계획 AI 노드가 메모리 부족이나 예외로 비정상 종료되더라도, 독립 프로세스인 /safety_watchdog(안전 감시 노드)과 /wheel_ctrl은 살아남아 목표 속도를 0.0 m/s로 낮추고 로봇을 안전하게 세울 수 있습니다.

[참고] ROS 2는 운영체제(OS)인가요? 이름에 Operating System이 들어가지만 Windows나 Ubuntu 같은 커널 운영체제가 아닙니다. 리눅스(주로 Ubuntu LTS) 같은 기존 운영체제 위에서 실행되면서, 여러 로봇 프로세스 사이의 통신·실행 스케줄링·빌드 도구(colcon)·시각화 도구(RViz2)를 제공하는 로봇용 미들웨어 및 소프트웨어 개발 프레임워크입니다.

발행-구독 모델이 만드는 느슨한 결합과 시뮬레이터 교체

노드를 나눴다면 이제 노드끼리 데이터를 주고받을 방법이 필요합니다. 만약 센서 노드가 데이터를 받을 상대 노드의 IP 주소나 함수 이름을 코드 안에 직접 적어 호출한다면, 새 기능을 추가할 때마다 센서 노드 코드를 다시 수정해야 합니다.

ROS 2는 이를 피하기 위해 이름 붙은 데이터 통로인 토픽(Topic)을 중심으로 한 발행-구독 모델(Publish-Subscribe Model)을 기본 통신 방식으로 사용합니다(ROS 2 토픽·서비스·액션 비교와 QoS 선택 기준 참조).

구분강하게 결합된 직접 호출ROS 2 발행-구독 모델 (느슨한 결합)
데이터 전달 방식수신 대상을 직접 지정해 함수/소켓 호출정해진 토픽 이름(/scan)에 데이터 발행
기록·시각화 도구 추가센서 코드에 저장 로직을 추가하고 재빌드센서 노드 수정 없이 /scan 구독자만 추가 실행
시뮬레이터 전환실제 센서 API 호출부를 시뮬레이터 API로 수정실제 센서 노드 대신 시뮬레이터 브리지 노드가 /scan 발행

이처럼 데이터를 내보내는 발행자(Publisher)와 데이터를 받는 구독자(Subscriber)가 서로의 존재나 개수를 몰라도 토픽 이름과 데이터 형식만 맞으면 연결되는 성질을 느슨한 결합(Loose Coupling)이라고 합니다.

실무에서 이 구조가 가장 큰 위력을 발휘하는 순간은 시뮬레이터와 실제 로봇을 오갈 때입니다. 경로 계획 노드(/nav_planner)는 오직 /scan 토픽으로 들어오는 거리 배열만 읽고 /cmd_vel 토픽으로 목표 속도만 내보냅니다. 따라서 낮에는 물리 시뮬레이터(Gazebo, Isaac Sim, MuJoCo 등)가 /scan을 발행하게 해 알고리즘을 검증하고, 저녁에는 판단 노드 코드를 단 한 줄도 고치지 않은 채 실제 로봇의 라이다 드라이버 노드에 그대로 연결할 수 있습니다.

두 개의 시계: AI 판단 주기와 실시간 모터 제어 주기 분리

최신 피지컬 AI 로봇에서도 이 분산 구조는 핵심 설계 원칙으로 쓰입니다(피지컬 AI 엔지니어링 로드맵 참조). 카메라 영상을 보고 로봇 팔의 다음 행동을 추론하는 인공지능 모델은 연산량이 커서 추론 주기가 수십 밀리초 단위로 흔들리기(Jitter) 쉽습니다.

반면 로봇 관절의 모터 드라이버는 정해진 주기(예: 1~2ms)마다 정확히 명령을 받지 못하면 진동이 생기거나 하드웨어 보호 회로가 작동해 멈춥니다. ROS 2에서는 무거운 AI 추론 노드를 상위 판단 주기로 분리하고, 하위 모터 제어기는 ros2_control 같은 실시간 제어 노드에서 고속 주기로 따로 돌립니다. 상위 AI 노드가 새 목표 궤적을 조금 늦게 보내더라도, 하위 제어 노드가 그사이를 부드럽게 보간(Interpolation)하며 모터를 안정적으로 잡아 주는 것입니다.

터미널에서 현재 실행 중인 모든 노드 목록과 특정 노드의 토픽 발행·구독 연결 상태는 ros2 node 명령으로 언제든 조회할 수 있습니다.

ros2 node list
ros2 node info /nav_planner

ros2 node list를 실행하면 현재 그래프에 참여 중인 노드 이름들이 한 줄씩 출력됩니다.

/lidar_driver
/nav_planner
/wheel_ctrl
/safety_watchdog

로봇 소프트웨어를 노드로 나누는 이유를 이해했다면, 다음 단계는 "각 터미널에서 따로 실행한 노드들이 중앙 서버도 없이 어떻게 서로를 찾아 연결하는가?"입니다. 이어지는 ROS 2 계층 구조와 DDS 무마스터 자동 디스커버리 문서에서 ROS 2의 내부 계층과 자동 탐색 원리를 살펴봅니다.

참고 문서

설명이 어렵거나 잘못된 부분을 발견하셨나요?

문서 수정 의견 보내기