파일을 모으는 단위와 실행 중 역할
워크스페이스(workspace)는 ROS 2 패키지를 담아 개발·빌드하는 디렉터리다. 패키지(package)는 이름·의존성·빌드 방식 같은 메타데이터와 코드/설정을 묶는 배포·빌드의 단위다. 한 패키지에 여러 실행 파일이나 노드 구현이 있을 수 있다. 폴더 하나가 있다고 자동으로 ROS 패키지가 되는 것도 아니며 패키지 이름이 실행 파일 이름과 반드시 같지도 않다. 노드(node)는 실행 중 ROS 그래프에서 통신하는 논리적 참여자다. 그래프는 이런 참여자와 통신 관계를 표현한다. 디스크의 패키지와 실행 중 노드를 같은 것으로 세지 않는다.
프로세스는 운영체제가 실행 중인 프로그램에 부여하는 실행 단위다. 노드는 한 프로세스에 하나일 수도, 여러 개일 수도 있으며 서로 다른 컴퓨터에 분산될 수도 있다. 예를 들어 가상 패키지 sensor_demo에 관측 발행 노드와 상태 검사 노드의 구현을 넣고, 두 노드를 하나의 프로세스에 구성할 수 있다. ‘노드 두 개가 보이므로 프로세스도 두 개’라는 추론은 성립하지 않는다. 프로그램이 시작됐다는 사실만으로 기대 노드가 올바르게 등록되고 콜백이 실행 중임을 입증하지도 못한다.
소스가 발견되는 실행물로 바뀌는 경로
일반적인 개발 레이아웃은 workspace/src에 패키지 소스를 두고 루트에서 colcon으로 빌드한다. 빌드 뒤 build에는 중간 산출물, install에는 설치 공간의 실행물/라이브러리/메타데이터 등이, log에는 빌드 기록이 생긴다. 세 디렉터리의 존재는 모든 패키지의 최신 빌드 성공을 뜻하지 않는다. 앞선 실행의 산출물이 남아 있을 수 있어 패키지별 결과와 실제 사용하는 설치 경로를 확인한다.
underlay는 의존 패키지를 제공하는 기반 환경이고 overlay는 그 위에 추가하거나 덮어 개발하는 환경이다. Ubuntu 24.04의 Jazzy 바이너리 설치를 사용하는 예에서는 /opt/ros/jazzy의 환경을 먼저 읽고 개발 워크스페이스의 설치 환경을 뒤에 읽는다. 셸의 source 명령은 스크립트가 설정하는 환경 변수를 현재 터미널에 반영한다. 다른 터미널에 자동으로 같은 변경이 전파되는 것은 아니다. overlay의 같은 이름 패키지가 기반 패키지보다 우선할 수 있으므로 서로 다른 작업 폴더를 번갈아 읽으면 예상하지 못한 구현을 찾을 수 있다.
다음은 이미 설치와 의존성이 준비된 별도 Ubuntu 24.04/Jazzy 환경의 절차를 읽기 위한 예다. 이 단위에서는 실행하지 않는다. 빌드용 터미널에서는 기반 환경을 사용하고, 현재 빌드할 overlay를 이미 읽은 터미널을 재사용하지 않는 공식 튜토리얼의 주의를 따른다. 실행용은 새 터미널에서 기반과 성공한 설치 공간을 읽는다.
# 빌드용 터미널: 의존성이 준비된 가상 ~/study_ws
source /opt/ros/jazzy/setup.bash
cd ~/study_ws
colcon build --packages-select sensor_demo
# 별도 실행용 터미널에서, 위 빌드 성공을 확인한 뒤
source /opt/ros/jazzy/setup.bash
source ~/study_ws/install/local_setup.bash
ros2 pkg prefix sensor_demo
마지막 명령은 패키지의 발견 경로를 확인하려는 읽기 명령이다. 기대 경로가 ~/study_ws/install 쪽이라면 다른 overlay의 경로가 나오는 것은 다음 조사 단서다. 소스 파일을 편집했어도 재빌드/설치·환경·실행 프로세스가 예전 상태면 변화가 보이지 않을 수 있다. symlink 설치가 일부 개발 파일 반영을 돕더라도 모든 변경에 재빌드가 불필요하다는 뜻은 아니다. 기존 1.6의 실제 패키지 예제는 이 개발 경로를 이해한 뒤 선택해서 읽는다.
메시지 도착과 사용자 코드 실행 사이
콜백(callback)은 메시지·타이머 같은 사건을 처리하도록 등록한 함수다. Executor는 준비된 사건에 대응하여 콜백 실행을 관리한다. C++ rclcpp의 단순 spin(node) 경로는 단일 스레드 Executor로 콜백을 처리한다. 노드를 만들기만 하고 필요한 실행 루프를 돌리지 않으면 기대 콜백이 처리되지 않을 수 있다. 통신 발견이나 미들웨어의 메시지 수신과 사용자 콜백 실행은 구별해야 한다.
그림 M05.01-1. 각 칸은 서로 다른 역할/근거다. 아래 방향은 자료/처리의 연결이며 실제 성공을 자동으로 보장하지 않는다. 직접 제작한 개념도이며 본문의 조건과 함께 읽는다.
가상 단일 스레드 Executor가 콜백 A를 0~30 ms 동안 중단 없이 실행하고, B의 메시지는 5 ms에 처리 가능해졌다고 하자. 다른 작업과 스케줄링 비용을 생략하고 A 뒤에 B를 실행한다고 이번 예에서 정하면 B의 가장 이른 시작은 30 ms, 준비 뒤 대기는 25 ms다. B의 처리가 4 ms 걸리면 종료는 34 ms다. 준비 시각부터 종료까지는 29 ms이고, 메시지 획득 시각이 따로 있다면 전체 관측 나이는 이 29 ms와 다를 수 있다. A가 길어서 B가 늦어진 기록을 곧바로 네트워크 손실이라고 판정하면 계층을 혼동한다.
이 숫자는 일반 Executor가 항상 모든 준비 사건을 도착 순서로 처리한다는 규칙이 아니다. 여러 콜백이 동시에 준비되면 실제 스케줄링 정책·콜백 그룹·스레드·대기 자료의 상태에 따라 달라진다. MultiThreadedExecutor는 여러 실행 스레드를 쓸 수 있지만 콜백 그룹의 상호 배제와 공유 데이터 보호가 동시성을 제한한다. 스레드 수를 늘렸다는 이유만으로 지연 상한이나 실시간 기한이 보장되지 않는다. 먼저 사건 준비/콜백 시작/종료를 구분하여 병목을 관측한 뒤 설계를 판단한다.