AEGLOSLAB ROS 2 Jazzy 대전

제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 2 이행 예 ROS 1 유지 필요 벤더 드라이버 아니오 관성 ROS 1 자산이 있다 장기 계획? 한 번에 가능? 직접 이행 — 브리지 불필요 브리지로 점진 이행 왜? 브리지 · 장기 운용 ★ 이행 재검토 권장
상황접근
소규모·전면 이행 가능직접 이행이 단순
대규모·단계적 필요브리지로 점진
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 2 토픽 ROS 1 ROS 1 토픽 ROS 2 ROS 1 노드 ros1_bridge 양쪽 다 연결 ROS 2 노드 메시지 타입 매핑
특성설명
양쪽 환경 필요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 컨테이너에서 실행 확인):

UbuntuROS 1ROS 2ros1_bridge
20.04 FocalNoeticFoxy·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 1ROS 2
디스커버리중앙 마스터(roscore)분산(3.3)
QoS없음(TCP/UDP 선택)정책 체계(1.6)
파라미터중앙 서버노드 소유(1.9)
액션actionlib내장(1.8)
생애주기없음있음(1.12)
launchXMLPython·XML·YAML(14.3)
빌드catkinament·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 1 1. 주변부부터 독립적인 노드 2. 브리지로 연결 혼합 운영 3. 핵심 이행 4. 브리지 제거 전체 ROS 2
순서 원칙이유
의존이 적은 것부터영향 범위 작음
새로 만드는 것은 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)
launchXML → 새 형식(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대용량을 브리지로병목한쪽에서 처리
6QoS 미확인데이터 안 흐름설정 확인
7/tf 양방향 브리지트리 충돌주인 지정
8환경 소싱 충돌빌드·실행 오류컨테이너 격리
9브리지 단일 실패 지점전체 단절감시·재시작
10안전 경로를 브리지에위험독립 경로
11노드 하나씩 이행경계 다수파이프라인 단위
12새 기능을 ROS 1로이행 안 끝남규칙 수립
13이행과 개선 혼재원인 불명분리
14동기 서비스 호출 이식교착(1.7.3)비동기·그룹
15QoS 미설정 방치센서 문제의식적 설정
16브리지 지연 미고려예산 초과재측정
17Jazzy 호스트에 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_bridge YAML에서 QoS를 오버라이드해야 하는 대표 사례(/tf_static)와 그 이유는?

다음 장 15.4은 반대 방향의 확장 — ROS 2 자체를 확장하는 지점들을 다룬다.