제15부 이행·확장
15.3 ROS 1 상호운용 — ros1_bridge
15.3.1 이 장에서 배우는 것 / 선수 지식
ROS 1은 공식 지원이 끝났지만(확인 필요: 정확한 시점·상태), 현장에는 여전히 ROS 1 시스템이 있다. 검증된 드라이버가 ROS 1에만 있거나, 오래 운영된 시스템이 있거나, 장비 벤더가 아직 ROS 2를 지원하지 않는 경우다.
그래서 두 가지 실무 문제가 생긴다 — 한동안 함께 쓰기(브리지)와 옮기기(마이그레이션). 이 장은 둘 다 다룬다.
배우는 것:
- 판단 — 브리지인가 이행인가.
ros1_bridge— 구조와 한계.- 브리지 운용 — 실무 문제들.
- 개념 차이 — 무엇이 다른가.
- 이행 전략 — 점진적으로 옮기기.
- 코드 이행 — 무엇을 어떻게 고치는가.
선수 지식: 1.1(노드·토픽), 1.6(QoS), 1.7·1.8(서비스·액션), 1.12(생애주기), 5.1(tf), 14.1(경계 설계).
정직:
ros1_bridge의 Jazzy 지원은 이 책이 직접 확인한 결과 사실상 없다.ros2/ros1_bridge저장소의 README(2026년 확인)는 "ros1_bridge는 ROS 1을 요구하는데, ROS 1(Noetic)은 2025년 5월에 EOL을 맞았고 Ubuntu 24.04(Noble)는 애초에 ROS 1을 지원하지 않는다"고 명시하며, Jazzy·Kilted가 설치되는 Ubuntu Noble에서는ros1_bridge가 지원되지 않는다고 명시한다. 이 책도ros:jazzy-ros-base컨테이너에서apt-cache search ros1-bridge를 실행해 패키지 자체가 apt 인덱스에 없음을 확인했다(실행 확인). 즉 이 장의 실전 구도는 "Jazzy 노드 안에서 브리지 하나만 추가로 실행하는" 그림이 아니라, ROS 1이 아직 운용되는 별도의(대개 Ubuntu 20.04) 호스트나 컨테이너에서 브리지를 실행하고 네트워크로 Jazzy와 마주 보게 하는 그림이다 — 15.3.2·15.3.5에서 이어서 다룬다. 그 밖의 세부(빌드 절차의 최신 변형, 커스텀 메시지 등록의 정확한 파일 경로 등)는 여전히 현재 문서로 재확인해야 한다(확인 필요). ROS 1 자체의 사용법은 다루지 않는다.
15.3.2 판단 — 브리지인가 이행인가
| 상황 | 접근 |
|---|---|
| 소규모·전면 이행 가능 | 직접 이행이 단순 |
| 대규모·단계적 필요 | 브리지로 점진 |
| ROS 1 전용 드라이버 필수 | 브리지 장기 운용 |
| 외부 시스템 연동 | 브리지 또는 커스텀 어댑터 |
견해: "브리지를 영구 구조로 두는 것"은 신중해야 한다. ROS 1의 지원이 끝났다면 보안 패치와 OS 지원이 없고(13.4.6), 브리지 자체의 유지 상태도 불확실하다
(확인 필요). 부득이하면 그 부분만 격리하고, 장기적으로 대체 경로를 찾는 것이 안전하다.
견해: 작은 시스템은 브리지 없이 직접 이행하는 것이 대체로 빠르다. 브리지 설정·빌드·디버깅에 드는 시간이 코드 이행 시간과 비슷할 수 있다. 노드가 열 개 안팎이면 직접 이행을 먼저 검토하라.
견해: "ROS 1 드라이버만 있어서"라면 대안을 확인하라. 그 사이 ROS 2 드라이버가 나왔을 수 있고, 없으면 간단한 드라이버를 직접 만드는 것이 브리지를 영구 운용하는 것보다 나을 수 있다 — 특히 장비가 단순한 프로토콜을 쓴다면(6.1).
정직: 판단에 앞서 물리적 배치부터 정해야 한다. 15.3.1에서 확인했듯 Jazzy가 실행되는 Ubuntu Noble 위에는 애초에 ROS 1을 설치할 수 없으므로, "브리지를 쓴다"는 선택은 자동으로 "ROS 1이 운용되는 별도 머신(또는 Ubuntu 20.04 컨테이너)을 하나 더 운영한다"는 선택이기도 하다. 이 비용(별도 OS 이미지 유지, 네트워크 경로 하나 추가, 15.3.5의 브리지 단일 실패 지점 문제)을 "브리지인가 이행인가" 판단에 처음부터 포함시켜야 실제 견적이 나온다 — 노드 몇 개를 위해 브리지를 쓴다면 그 몇 개가 별도 머신 유지 비용을 정당화하는지 물어야 한다.
15.3.3 ros1_bridge 구조
| 특성 | 설명 |
|---|---|
| 양쪽 환경 필요 | ROS 1과 ROS 2를 모두 소싱 |
| 타입 매핑 | 같은 이름·구조의 메시지를 연결 |
| 동적/정적 브리지 | 자동 탐색 vs 명시 지정 |
| 컴파일 시 타입 등록 | 커스텀 메시지는 재빌드 필요 |
| 브리지 모드 | 특성 |
|---|---|
동적(dynamic_bridge) | 양쪽 토픽을 탐색해 양쪽에 매칭되는 발행자·구독자 쌍이 모두 활성 상태일 때만 연결 |
정적/파라미터 지정(parameter_bridge) | YAML로 명시한 목록만 연결 |
동적 브리지의 실제 동작과 그 부작용. 공식 README(2026년 확인)에 따르면 동적 브리지는 효율을 위해 "발행-구독 쌍이 활성화될 때만" 브리지를 만든다. 그 결과 ros2 topic echo <토픽>을 아무 구독자도 없는 상태에서 실행하면 Could not determine the type for the passed topic 오류가 난다 — echo 자신이 그 구독자가 되어야 브리지가 트리거되는데, 타입을 모르면 echo조차 구독을 못 걸기 때문이다. 우회법은 타입을 명시(ros2 topic echo <토픽> <타입>)하거나, 모든 ROS 2→ROS 1 토픽을 무조건 연결하는 --bridge-all-2to1-topics 옵션을 쓰는 것이다.
# ROS 1 쪽: roscore + 소싱
source /opt/ros/noetic/setup.bash
roscore
# ROS 2 쪽 셸: 양쪽을 함께 소싱하고 동적 브리지 실행
source /opt/ros/noetic/setup.bash
source /opt/ros/jazzy/setup.bash # ROS 1 다음에 ROS 2를 소싱
export ROS_MASTER_URI=http://localhost:11311
ros2 run ros1_bridge dynamic_bridge --bridge-all-2to1-topics
# 연결되면 이런 로그가 출력된다(README 인용)
# created 1to2 bridge for topic '/chatter' with ROS 1 type 'std_msgs/String' and ROS 2 type 'std_msgs/String'
빌드는 세 단계이며 소싱 순서가 결과를 가른다. 커스텀 메시지를 브리지에 태우려면 ROS 1·ROS 2 양쪽 워크스페이스에 그 메시지 패키지를 먼저 빌드해 둔 뒤, 아래 순서로 브리지 자체를 빌드해야 한다(공식 README, 2026년 확인).
# 1) 브리지를 제외한 나머지를 먼저 빌드 — 이 시점엔 ROS 1을 소싱하지 않는다
colcon build --symlink-install --packages-skip ros1_bridge
# 2) ROS 1을 먼저 소싱, 그다음 ROS 2(+커스텀 메시지 오버레이)를 소싱
source /opt/ros/noetic/setup.bash
source install/setup.bash # 커스텀 메시지가 포함된 ROS 2 오버레이
# 3) 브리지만 다시 빌드 — CMake가 그 시점에 소싱된 양쪽 패키지를 모두 찾아 매핑을 만든다
colcon build --symlink-install --packages-select ros1_bridge --cmake-force-configure
함정:
ros1_bridge는 C++로 구현되어 있어 "빌드 시점에 경로에 있던 메시지·서비스 타입만" 지원한다(README 인용: "its support is limited to only the message/service types available at compile time"). 배포되는 바이너리 브리지에는common_interfaces·tf2_msgs같은 표준 타입만 매핑이 미리 들어 있으므로, 커스텀 메시지를 쓰려면 위 3단계처럼 브리지를 직접 재빌드해야 한다는 것이 README가 명시하는 흔한 좌절 지점이다.CMAKE_PREFIX_PATH에 ROS 1 경로가 남아 ROS 2 패키지를 덮어쓰지 않도록 주의해야 한다는 것도 README가 별도로 경고하는 함정이다.
Ubuntu·ROS 1·ROS 2 조합별 지원 범위(README, 2026년 확인 + 이 책이 Jazzy 컨테이너에서 실행 확인):
| Ubuntu | ROS 1 | ROS 2 | ros1_bridge |
|---|---|---|---|
| 20.04 Focal | Noetic | Foxy·Galactic(둘 다 EOL)·Humble | 완전 지원 |
| 22.04 Jammy | 공식 미지원(부분) | Humble·Iron | 소스 빌드 필요 |
| 24.04 Noble | 설치 불가 | Jazzy·Kilted | 미지원 — 실행 확인: apt 패키지 없음 |
견해: 동적 브리지는 편리하지만 예측 가능성이 낮다. 어떤 토픽이 언제 연결되는지가 탐색 결과와 위
echo함정에 달렸고,--bridge-all-2to1-topics없이는 툴링조차 일관되게 안 보일 수 있다. 운영에서는parameter_bridge+ YAML로 필요한 토픽만 명시하는 방식(15.3.5)이 안전하다.
함정: 브리지가 대역과 CPU를 소비한다. 모든 메시지가 브리지를 거쳐 두 번 직렬화·역직렬화되므로, 대용량 토픽(이미지·포인트클라우드 — 7.8)을 브리지로 넘기면 병목이 된다. 가능하면 대용량은 한쪽에서 처리하고 결과만 넘긴다.
15.3.4 개념 차이
브리지가 자동으로 해결하지 못하는 것들이다.
| 개념 | ROS 1 | ROS 2 |
|---|---|---|
| 디스커버리 | 중앙 마스터(roscore) | 분산(3.3) |
| QoS | 없음(TCP/UDP 선택) | 정책 체계(1.6) |
| 파라미터 | 중앙 서버 | 노드 소유(1.9) |
| 액션 | actionlib | 내장(1.8) |
| 생애주기 | 없음 | 있음(1.12) |
| launch | XML | Python·XML·YAML(14.3) |
| 빌드 | catkin | ament·colcon(4.1) |
| 시간 | /clock·sim time | 유사하나 세부 차이(1.10) |
| 네임스페이스 | 유사 | 유사·세부 차이(1.1.6) |
견해: QoS의 부재/존재가 브리지에서 가장 미묘한 문제다. ROS 1 쪽은 QoS 개념이 없으므로, 브리지가 ROS 2 쪽에 어떤 QoS로 발행/구독할지 결정해야 한다. 기본값이 맞지 않으면 데이터가 흐르지 않거나(1.5.4) 예상과 다른 신뢰성이 된다.
parameter_bridge는 토픽별 YAML로 QoS를 오버라이드할 수 있다:topics: - topic: /scan type: sensor_msgs/msg/LaserScan queue_size: 5 qos: history: keep_last depth: 5 reliability: best_effort # 센서 데이터는 RELIABLE 기본값이 아니라 BEST_EFFORT로(1.6.3.1) - topic: /tf_static type: tf2_msgs/msg/TFMessage qos: history: keep_all # ROS 1에서 /tf_static은 래칭(latching) 토픽 — ROS 2 쪽에선 durability: transient_local # history·durability를 명시하지 않으면 늦게 참여하는 구독자가 못 받는다
/tf_static처럼 ROS 1의 래칭 토픽은 ROS 2 쪽에서transient_local로 명시하지 않으면 동작이 달라진다는 것이 실전에서 특히 자주 걸리는 함정이다 — 센서 QoS 불일치(1.6)와 정확히 같은 메커니즘이 여기서도 반복된다.
함정: 파이썬 구현이 없어 컴파일 타임에 고정된 QoS 기본값을 바꾸려면 YAML로 명시하는 수밖에 없다. "일단 붙여 보고 안 되면 나중에 QoS를 본다"는 순서로 접근하면, 데이터가 조용히 하나도 안 흐르는데 원인을 브리지가 아니라 노드 코드에서 찾느라 시간을 쓰게 된다. 브리지를 새로 놓을 때마다 관련 토픽의 QoS를 먼저 YAML에 적어 두는 습관이 이 함정을 없앤다.
견해: 파라미터 모델의 차이가 이행에서 큰 작업이 된다. ROS 1의 중앙 파라미터 서버에 의존한 코드는 구조를 바꿔야 한다. 각 노드가 자기 파라미터를 소유하는 모델로 옮기면서(1.9), 공유가 필요한 값은 토픽·서비스로 명시적으로 주고받게 바꾼다.
함정:
/tf는 브리지에서 특별히 주의해야 한다. 양쪽에서 tf가 발행되면 트리가 충돌하거나 중복된다(5.1.4). 어느 쪽이 tf의 주인인지 정하고, 필요한 방향으로만 브리지하는 것이 안전하다.
15.3.5 브리지 운용
| 문제 | 대처 |
|---|---|
| 환경 소싱 충돌 | 브리지 전용 셸·컨테이너(14.8.5) |
| 커스텀 메시지 | 양쪽 정의·재빌드 |
| 대역·CPU | 필요한 토픽만 |
| QoS 불일치 | 설정 확인 |
| 시간 동기 | 양쪽 시계·sim time(1.10) |
| 이름 매핑 | 리매핑 필요 시 |
| 장애 | 브리지가 종료되면 단절 |
견해: 브리지를 컨테이너로 격리하는 것이 환경 문제를 크게 줄인다(14.8.5). ROS 1과 ROS 2를 같은 셸에 소싱하는 것은 환경 변수 충돌을 만들기 쉬운데, 컨테이너 안에서 브리지만 실행하면 나머지 환경이 오염되지 않는다 — 그리고 15.3.1의 실행 확인대로 Jazzy·Kilted가 실행되는 Ubuntu Noble 자체에는 ROS 1을 설치할 수 없으므로, 이 컨테이너 격리는 사실상 선택이 아니라 유일한 경로다. 브리지 이미지는 최소한 다음 ROS 1 패키지를 담아야 빌드·실행이 된다(README 인용):
catkin·roscpp·roslaunch(roscore실행용)·rosmsg·std_msgs, 그리고 파이썬 패키지rospkg. 예제를 그대로 실행해 보려면rosbash(rosrun)·rostopic·rqt_image_view도 필요하다.
함정: 브리지가 단일 실패 지점이 된다(14.2.5). 브리지가 종료되면 양쪽이 완전히 단절되므로, 감시와 자동 재시작(14.8.3)이 필요하다. 안전에 관련된 경로를 브리지에 의존시키지 말라(14.10.5).
견해: 브리지를 지나는 경계를 최소화하도록 시스템을 나누라(14.1). 브리지를 넘나드는 통신이 많으면 성능·복잡도·장애 위험이 모두 는다. 경계를 통신이 적은 곳에 긋는 원칙이 여기서 특히 중요하다.
15.3.6 이행 전략
점진적으로 옮기는 방법이다.
| 순서 원칙 | 이유 |
|---|---|
| 의존이 적은 것부터 | 영향 범위 작음 |
| 새로 만드는 것은 ROS 2로 | 이행 대상이 안 늘어남 |
| 인터페이스가 안정된 것부터 | 브리지 설정이 안정 |
| 대용량 데이터 경로는 함께 | 브리지 병목 회피 |
| 안전 관련은 신중히 | 검증 부담(14.10) |
견해: "새 기능은 ROS 2로 만든다"는 규칙을 먼저 세우라. 그렇지 않으면 이행하는 동안 ROS 1 코드가 계속 늘어나 끝나지 않는다. 이행 시작 시점에 이 규칙을 정하는 것이 가장 효과적인 조치 중 하나다.
견해: 파이프라인 단위로 옮기는 것이 브리지 경계를 줄인다. 노드 하나씩 옮기면 브리지를 지나는 통신이 많아지므로, 센서→인지→출력 같은 한 흐름 전체를 옮기면 경계가 적어진다.
견해: 이행 중에 기능을 개선하지 말라. 이행과 개선을 섞으면 문제가 생겼을 때 원인이 이행인지 개선인지 모른다(11.1.7의 원칙). 먼저 동등하게 옮기고, 그다음 개선한다.
15.3.7 코드 이행
무엇을 고치는가.
| 영역 | 작업 |
|---|---|
| 빌드 | catkin → ament·colcon(4.1) |
| 노드 구조 | 콜백·스핀 모델(2.2) |
| 파라미터 | 중앙 → 노드 소유(1.9) |
| QoS | 명시적 설정 추가(1.6) |
| 서비스 | 비동기 모델 주의(1.7.5) |
| 액션 | actionlib → ROS 2 액션(1.8) |
| launch | XML → 새 형식(14.3) |
| 시간 | API 차이(1.10) |
| 메시지 | 대체로 유사·세부 확인 |
| 로깅 | 매크로 차이(1.11) |
견해: QoS 설정이 이행에서 새로 생기는 작업이다. ROS 1에는 없던 개념이므로 "기본값으로 두면 되겠지"라고 넘어가기 쉬운데, 센서 데이터에 기본
RELIABLE을 쓰면 문제가 생긴다(1.6.3.1). 이행 시 각 토픽의 QoS를 의식적으로 정하는 것이 필요하다.
견해: 서비스 호출의 동기/비동기 차이가 함정이다(1.7.3). ROS 1에서 콜백 안에서 서비스를 동기 호출하던 코드를 그대로 옮기면 교착에 빠질 수 있다. 이행 시 이 패턴을 찾아 비동기로 바꾸거나 콜백 그룹을 분리해야 한다(2.3).
견해: 이행을 기회로 구조를 개선하고 싶은 유혹을 관리하라. 15.3.6에서 "이행 중 개선하지 말라"고 했지만, 명백히 잘못된 구조는 옮길 가치가 없다. 판단 기준은 "이것을 그대로 옮기면 곧 다시 고쳐야 하는가"이며, 그렇다면 별도 작업으로 분리해 순서를 정한다.
15.3.8 검증
| 확인 | 방법 |
|---|---|
| 기능 동등성 | 같은 입력·같은 출력 |
| 인터페이스 | 토픽·타입·주기 |
| 규약 | tf·프레임·단위(14.6.3) |
| 성능 | 브리지 지연 포함 측정(11.7) |
| 이상 대응 | 브리지 단절 시 |
| 장시간 | 안정성 |
견해: ROS 1 시스템의 bag을 검증에 쓸 수 있다면 강력하다(14.6.5). 형식이 다르므로 변환이 필요하지만
(확인 필요: 변환 도구), 기존 데이터로 새 시스템을 검증하면 동등성을 확인하기 좋다.
견해: 브리지 지연을 측정에 포함하라(12.4). 브리지를 지나는 경로는 지연이 추가되므로, 지연 예산을 다시 계산해야 한다. 이것이 브리지를 영구 구조로 두지 말라는 또 하나의 이유다.
15.3.9 함정 카탈로그
| # | 함정 | 증상 | 대처 |
|---|---|---|---|
| 1 | 브리지를 영구 구조로 | 보안·유지 위험 | 격리·대체 계획 |
| 2 | 작은 시스템에 브리지 | 오히려 느림 | 직접 이행 검토 |
| 3 | 커스텀 메시지 미지원 | 브리지 안 됨 | 양쪽 정의·재빌드 |
| 4 | 동적 브리지 운영 사용 | 예측 불가·대역 | 명시 지정 |
| 5 | 대용량을 브리지로 | 병목 | 한쪽에서 처리 |
| 6 | QoS 미확인 | 데이터 안 흐름 | 설정 확인 |
| 7 | /tf 양방향 브리지 | 트리 충돌 | 주인 지정 |
| 8 | 환경 소싱 충돌 | 빌드·실행 오류 | 컨테이너 격리 |
| 9 | 브리지 단일 실패 지점 | 전체 단절 | 감시·재시작 |
| 10 | 안전 경로를 브리지에 | 위험 | 독립 경로 |
| 11 | 노드 하나씩 이행 | 경계 다수 | 파이프라인 단위 |
| 12 | 새 기능을 ROS 1로 | 이행 안 끝남 | 규칙 수립 |
| 13 | 이행과 개선 혼재 | 원인 불명 | 분리 |
| 14 | 동기 서비스 호출 이식 | 교착(1.7.3) | 비동기·그룹 |
| 15 | QoS 미설정 방치 | 센서 문제 | 의식적 설정 |
| 16 | 브리지 지연 미고려 | 예산 초과 | 재측정 |
| 17 | Jazzy 호스트에 ros1_bridge를 직접 설치 시도 | apt 패키지 없음(Ubuntu Noble 미지원) | 별도 Ubuntu 20.04/22.04 호스트·컨테이너 |
| 18 | 아무 구독자 없이 ros2 topic echo | "타입을 못 정함" 오류 | 타입 명시 또는 --bridge-all-2to1-topics |
| 19 | /tf_static QoS 미지정 | 늦게 참여하는 구독자가 못 받음 | transient_local·keep_all 명시 |
15.3.10 체크포인트
- 브리지와 직접 이행의 판단 기준 네 가지는?
- 브리지를 영구 구조로 두는 것의 위험 셋은?
- 작은 시스템에 직접 이행을 권하는 근거는?
ros1_bridge의 네 특성과 두 모드는?- 커스텀 메시지에서 흔한 좌절 지점과 그 이유는?
- 동적 브리지를 운영에 권하지 않는 이유는?
- ROS 1과 ROS 2의 개념 차이 아홉 가지를 말할 수 있는가.
- QoS 부재/존재가 브리지에서 만드는 문제는?
- 파라미터 모델 차이가 이행에서 어떤 작업이 되는가.
/tf브리지의 주의점은?- 브리지 운용의 일곱 문제와, 컨테이너 격리의 이점은?
- 브리지가 단일 실패 지점일 때의 대비와, 하지 말아야 할 것은?
- 이행 순서의 다섯 원칙과, "새 기능은 ROS 2로"가 왜 중요한가.
- 파이프라인 단위 이행의 이점은?
- 코드 이행의 열 영역과, 새로 생기는 작업·함정은?
- 이행 중 개선에 대한 판단 기준은?
- 브리지 지연을 측정에 포함해야 하는 이유는?
- Jazzy·Kilted가 설치되는 Ubuntu 버전에서
ros1_bridge가 지원되지 않는 이유와, 그것이 물리적 배치에 강제하는 것은? - 동적 브리지에서
ros2 topic echo가 실패하는 정확한 이유와 두 가지 우회법은? parameter_bridgeYAML에서 QoS를 오버라이드해야 하는 대표 사례(/tf_static)와 그 이유는?
다음 장 15.4은 반대 방향의 확장 — ROS 2 자체를 확장하는 지점들을 다룬다.