주황색 발행자 상자에서 나온 구슬 모양 메시지가 레일을 따라가고, 긴 제공 deadline 막대 아래 더 짧은 요청 deadline 막대가 놓인 채 초록색 구독자 상자와의 연결 고리가 맞물리지 않은 개념 삽화

ROS 2 센서 토픽에 deadline QoS를 걸 때: 구독자가 더 짧은 deadline을 요청하면 왜 연결이 끊길까


카메라 드라이버 노드가 20 Hz로 이미지를 발행하고, 발행자 QoS에는 deadline 100 ms가 들어 있다. 이 토픽을 받는 인식 노드가 “조금 더 엄격하게” 50 ms를 요청하면 무슨 일이 생길까. 「ROS 2 센서 토픽에 deadline QoS를 걸 때: 구독자가 더 짧은 deadline을 요청하면 왜 연결이 끊길까」라는 질문에 답하려면 표현 하나부터 바로잡아야 한다. 공식 문서 기준으로 이 짝은 연결됐다가 끊기는 것이 아니라 처음부터 맺어지지 않는다. 구독자 요청 50에서 발행자 제공 100을 빼면 −50이고, 이 음수가 곧 비호환이라는 판정이다.

계산은 뺄셈 두 번으로 끝난다. 연결 시점에는 “구독자 요청 − 발행자 제공”의 부호로 짝이 맺어지는지를 보고, 연결된 뒤에는 “발행자 제공 − 실제 발행 간격”의 부호로 발행자가 약속을 지킬 수 있는지를 본다. 첫 번째가 통과해도 센서가 느려지면 두 번째는 음수가 될 수 있다. 두 부호를 나눠 두면 메시지가 오지 않을 때 구독자 요청값을 고칠지, 발행자 제공값이나 발행 빈도를 고칠지 고를 수 있다.

토픽, 발행자(publisher), 구독자(subscription)와 reliability·durability 호환성은 안다고 가정한다. 이 부분이 낯설다면 ROS 2 토픽 이름이 같은데 메시지가 안 올 때: QoS 호환성을 먼저 계산하기를 먼저 읽으면 된다. 판정 규칙은 2026년 9월 25일에 ROS 2 공식 QoS 문서(Rolling 고정 커밋과 Jazzy 판), ROS 2 설계 문서, Fast DDS 문서, rclcpp·rclpy와 미들웨어 구현 소스로 대조했고, 직접 실행한 것은 여덟 사례를 계산하는 Python 3.12.3 산술 예제뿐이다. ROS 2 노드, DDS, 실제 카메라나 시뮬레이터는 실행하지 않았으므로 복사해 돌릴 노드 코드는 싣지 않는다. 결론은 ROS 2의 기본 통신 계층인 Fast DDS처럼 DDS(Data Distribution Service, ROS 2가 기본으로 쓰는 발행·구독 통신 표준) 계열 미들웨어를 전제로 한다.

deadline 값이 작을수록 더 엄격한 조건이 된다

QoS(Quality of Service, 서비스 품질)는 발행자와 구독자가 각자 정하는 전달 방식 설정 묶음이다. reliability(신뢰성), durability(지속성) 같은 여러 정책(policy)으로 이루어지고 deadline도 그중 하나다.

ROS 2 문서는 deadline(데드라인, 기한)을 “토픽에 연속으로 발행되는 메시지 사이의 예상 최대 시간”으로 정의한다(ROS 2 QoS 문서 L53-L55). 100 ms라면 다음 메시지가 100 ms보다 늦지 않아야 한다는 뜻이다. 값이 작을수록 더 자주 보내야 하므로 더 엄격한 조건이 된다. 그러니 50 ms를 요청하는 쪽은 상대에게 더 많은 것을 요구하고 있는 셈이다.

같은 deadline이라도 어느 쪽에 거느냐에 따라 뜻이 갈린다.

  • 제공(offered): 발행자에 건 값이다. “이 간격 안에는 다음 메시지를 내겠다”는 약속이며, 문서 표현으로는 발행자가 줄 수 있는 최대 품질이다.
  • 요청(requested): 구독자에 건 값이다. “적어도 이 간격 안에는 받아야 한다”는 요구이며, 구독자가 받아들일 수 있는 최소 품질이다(L133-L139).

센서 사양은 보통 빈도(Hz)로 적혀 있으므로 먼저 주기(period)로 바꾼다. 1초가 1000 ms이니 주기(ms) = 1000 / 빈도(Hz)다. 20 Hz 카메라는 1000 / 20 = 50 ms마다 한 장을 낸다. 이 50 ms는 센서가 실제로 내는 간격이고 deadline 설정값과는 다른 숫자다. 두 숫자를 섞으면 뒤의 두 뺄셈이 한데 엉킨다.

식자재 배송 계약에 빗대 보자. 업체가 계약서에 “늦어도 100분마다 한 번은 배송한다”고 적었는데 식당이 “50분마다 오지 않으면 곤란하다”는 조건을 건다. 업체는 그 조건을 약속한 적이 없고, DDS 계열 ROS 2는 이런 짝을 거래 상대로 묶지 않는다. 비유가 어긋나는 지점도 분명하다. 실제 거래라면 협상하거나 가능한 만큼만 배송할 수 있지만 QoS 판정에는 협상도, 부분 연결도 없다. 업체가 실제로는 50분마다 배송하고 있더라도 판정은 계약서에 적힌 설정값끼리만 비교한다.

20 Hz 카메라 예제를 뺄셈 두 번으로 푼다

아래 숫자는 설명을 위해 고른 값이며 장애 보고나 로봇 실험에서 가져오지 않았다.

연결 판정은 요청 − 제공을 계산해 0 이상이면 호환(compatible), 음수면 비호환(incompatible)으로 읽는다. 위반 판정은 센서가 흔들림 없이 일정한 주기로 발행한다고 가정하고 제공 − 실제 발행 간격을 계산한다. 음수면 발행자가 약속한 간격 안에 다음 메시지를 내지 못한다.

# 상황 식 (ms) 결과 문서 규칙으로 읽으면
1 20 Hz 센서의 발행 간격 1000 / 20 50.0 주기 변환
2 요청 50, 제공 100 50 − 100 −50 음수 → 비호환, 연결 없음
3 요청 100, 제공 50 100 − 50 50 양수 → 호환
4 요청 80, 제공 80 80 − 80 0 0 → 호환
5 20 Hz 발행자, 제공 40 40 − 1000/20 −10.0 음수 → Offered deadline missed 조건
6 10 Hz 발행자, 제공 150 150 − 1000/10 50.0 양수 → 약속 이행 가능
7 사례 6과 같은 짝, 요청 200 200 − 150 50 연결 시점 호환
8 사례 7의 센서가 5 Hz로 느려짐 150 − 1000/5 −50.0 호환이어도 발행자 쪽 위반 조건

독자 질문의 답은 사례 2다. 요청 50 − 제공 100 = −50이므로 연결되지 않는다. 사례 3과 4는 요청이 제공보다 길거나 같으면 통과한다는 것을 보여 주며, 결과가 정확히 0인 경계도 연결 쪽에서는 호환이다.

사례 5는 성격이 다르다. 제공 40 ms를 20 Hz(50 ms) 센서에 걸었으니 구독자와 무관하게 발행자가 스스로 한 약속을 지키지 못하는 조건이다. 사례 7과 8은 한 짝의 시간 흐름이다. 센서가 5 Hz로 느려져도 설정값 200과 150은 그대로이므로 첫 번째 뺄셈은 50에 머문다. 음수로 바뀌는 것은 두 번째 뺄셈, 150 − 200 = −50이다.

여덟 사례는 공개된 산술 예제 example.py에 assert로 들어 있고, 기록한 출력은 검산 기록 verification.json에 있다. 2026년 9월 25일 Linux의 Python 3.12.3(표준 라이브러리 math·json만 사용)에서 python3 -I example.py를 다시 실행해 종료 코드 0, assert 여덟 개 통과, 기록과 같은 출력을 확인했다. 스크립트가 확인하는 것은 뺄셈과 부호이고, 그 부호를 “연결 없음”이나 “위반 조건”으로 읽는 근거는 다음 절의 공식 문서다.

부호 하나가 연결과 위반을 가르는 이유

요청이 제공보다 엄격하면 짝을 만들지 않는다

ROS 2 QoS 문서는 요청과 제공을 맞대는 Request vs Offered 모델에서 “요청이 제공보다 더 엄격하지 않을 때만” 연결이 만들어진다고 적는다(L133-L139). deadline에서 더 엄격하다는 말은 더 짧다는 뜻이므로 조건은 요청 ≥ 제공, 즉 요청 − 제공 ≥ 0이다. 같은 문서의 deadline 호환성 표도 발행자 x, 구독자 y일 때 y = x와 y > x는 호환, y < x는 비호환으로 적는다(L193-L220).

다른 자료도 방향이 같다. Fast DDS 문서는 부등식을 반대쪽에서 써서 “제공 deadline 주기 ≤ 요청 deadline 주기”가 아니면 비호환이라고 한다(Fast DDS DeadlineQosPolicy). ROS 2 설계 문서는 구독자의 요청 deadline이 발행자 deadline 이상이어야 구독할 수 있다고 설계 의도를 밝히고(설계 문서 L51-L60), 설계 문서가 인용한 DDS 사양 정의는 이 조건이 맞지 않으면 통신이 일어나지 않는다고 적는다(설계 문서 L195-L209).

호환성 표가 비교하는 것은 설정값 x와 y뿐이고, 실제 발행 속도는 표 어디에도 없다. 표대로 읽으면 카메라가 실제로 20 Hz로 보내고 있어도 설정값이 비호환인 짝은 연결되지 않는다.

deadline이 맞는다고 끝나지도 않는다. reliability, durability, liveliness 같은 다른 호환성 정책 중 하나라도 어긋나면 연결은 만들어지지 않고, 연결이 없으면 메시지도 전달되지 않는다(L272-L276). 판정은 발행자와 구독자 한 쌍마다 따로 이뤄진다. 50 ms를 요청한 인식 노드만 빠지고, 같은 토픽에서 100 ms 이상을 요청한 다른 구독자는 그대로 연결될 수 있다.

비호환은 상대를 발견하는 순간의 경고로 드러난다

문서는 이 상황을 “조건이 맞을 때만 연결이 만들어지고, 연결이 만들어지지 않으면 메시지가 전달되지 않는다”고 표현한다. 비호환을 알리는 QoS 이벤트(QoS event)도 발행자 쪽 Offered incompatible QoS, 구독자 쪽 Requested incompatible QoS로 나뉘며, 둘 다 결과를 “연결 없음”으로 설명한다(L292-L318). 이미 연결된 상대가 사라지는 경우는 같은 문서의 매칭(matched) 이벤트 설명에 따로 나온다.

그래서 원인을 찾을 때는 상대를 발견하는 시점에 남는 비호환 경고부터 본다. rclcpp(ROS 2의 C++ 클라이언트 라이브러리) 소스에 정의된 구독자 쪽 기본 경고 문구는 다음과 같다(subscription_base.cpp L384-L390).

New publisher discovered on topic '%s', offering incompatible QoS. No messages will be sent to it. Last incompatible policy: %s

발행자 쪽 기본 경고는 “New subscription discovered on topic ‘%s’, requesting incompatible QoS.”로 시작한다(publisher_base.cpp L366-L373). deadline이 원인이면 정책 이름 자리에 DEADLINE_QOS_POLICY가 들어간다(qos.cpp L33-L34). 문구 이름대로 “Last incompatible policy”는 어긋난 정책 가운데 마지막 하나만 보여 주므로, deadline을 고친 뒤에도 연결되지 않으면 다른 정책을 다시 봐야 한다.

발행자에 deadline이 없고 구독자에만 있으면 막힌다

Duration 형태 정책의 기본값(“default”)은 값을 지정하지 않은 상태이며 미들웨어가 보통 무한대로 해석한다(L70-L71). ROS 2 기본 프로필의 deadline도 이 기본값이고(L96-L98), Fast DDS의 기본 주기는 무한(c_TimeInfinite)이다. 2019년 설계 제안 문서에는 0을 “추적하지 않음”으로 적은 대목이 있지만 현행 문서의 기본값 설명은 미지정과 무한대 해석이므로, deadline을 끄려고 0을 넣는 방식은 권하지 않는다.

호환성 표는 발행자가 기본값이고 구독자만 x를 걸면 비호환, 발행자가 x이고 구독자가 기본값이면 호환으로 적는다(L193-L220). 뺄셈으로 옮기면 요청 x − 제공 무한대가 음수이기 때문이다. 약속을 하지 않은 발행자에게 약속을 요구하는 조합이다. 잘 돌던 센서 토픽에 모니터링 노드를 새로 붙이면서 구독자 쪽에만 deadline을 넣으면, 그 새 구독자만 연결되지 않을 수 있다.

연결 뒤에는 실제 간격이 비교 대상이 된다

연결된 뒤의 판정은 별도의 이벤트로 정의돼 있다. 발행자가 제공 deadline 안에 다음 메시지를 발행하지 못하면 Offered deadline missed, 구독자가 요청 deadline 안에 메시지를 받지 못하면 Requested deadline missed다(L292-L318). 연결 판정이 설정값 두 개를 연결 시점에 한 번 비교한다면, 위반 판정은 실행 중 실제로 흐른 시간을 설정값과 비교한다.

일정한 주기를 가정하면 제공 − 실제 발행 간격 < 0이 발행자 쪽 조건이고, 사례 5(−10)와 사례 8(−50)이 여기에 해당한다. 계산이 가리키는 것은 “Offered deadline missed 조건에 해당한다”까지이며, 이벤트가 몇 번 나는지는 식에 들어 있지 않다. 사례 8의 구독자 쪽은 요청 200과 실제 간격 200이 정확히 같은 경계라서 예제에서 판정하지 않았다.

rclpy(ROS 2의 Python 클라이언트 라이브러리)에서는 QoSProfile의 deadline 인자에 Duration을 넘겨 값을 정하고(qos.py L88-L117), 위반은 SubscriptionEventCallbacks나 PublisherEventCallbacks의 deadline 콜백으로 받는다. 콜백에 전달되는 정보 타입은 QoSRequestedDeadlineMissedInfo와 QoSOfferedDeadlineMissedInfo다(event_handler.py L64-L80).

무엇이 “받았다”로 세어지는지도 알아 둘 만하다. ROS 2에는 DDS나 Zenoh 같은 실제 통신 구현을 바꿔 끼울 수 있게 해 주는 미들웨어 인터페이스 층, RMW(ROS Middleware interface, 소문자 rmw로도 쓴다)가 있다(Different middleware vendors L85). ROS 2 설계 문서는 deadline 추적을 이 rmw 계층까지로 두고, 역직렬화나 콜백 같은 ROS 상위 처리 지연은 계산에 넣지 않는다고 설계 의도를 밝힌다(설계 문서 L51-L60, L150-L151). 인식 노드의 콜백이 무거워 처리가 밀리는 문제는 deadline missed로 드러나지 않을 수 있다는 뜻이다.

연결 시점에는 요청 50 ms − 제공 100 ms = −50으로 비호환, 연결 뒤에는 제공 150 ms − 실제 간격 200 ms = −50으로 Offered deadline missed 조건임을 비교한 시간축 개념도

확인 문제: 세 가지 설정에서 고칠 쪽을 고른다

문제 1. 발행자 제공 100 ms, 인식 노드 요청 50 ms라서 메시지가 오지 않는다. 인식 노드의 요청을 100 ms로 늘리면 연결될까?

연결된다. 100 − 100 = 0이고 호환성 표에서 같은 값은 호환이다. 다만 요청을 늘리면 “100 ms 안에만 받으면 된다”로 요구가 느슨해진다. 인식 노드가 정말 50 ms 안에 새 이미지를 받아야 한다면 요청을 늘리는 것은 요구를 포기하는 선택이고, 발행자 제공값을 줄이는 쪽을 검토해야 한다. 그러면 두 번째 뺄셈이 문제가 된다.

문제 2. 그래서 발행자 제공값을 50 ms로 줄였다. 센서는 20 Hz다. 괜찮을까?

연결 쪽은 50 − 50 = 0으로 호환이다. 그런데 발행자 쪽 제공 − 실제 간격도 50 − 50 = 0으로 경계에 걸린다. 문서가 같은 값에서 위반 이벤트가 나는지 정하지 않으므로 이 설정을 안전하다고 볼 근거가 없다. 제공값을 40 ms로 더 줄이면 사례 5처럼 −10이 된다. 제공값을 실제 주기보다 짧게 잡으면 발행자가 그 약속을 지킬 수 없고(Offered deadline missed 조건), 주기와 똑같이 두면 경계에 선다. 여유를 얼마나 둘지는 공식 문서도, 흔들림(지터)을 넣지 않은 이 예제도 수치를 주지 않는다. 50 ms 요구가 진짜라면 센서 빈도를 올리는 것도 선택지다.

문제 3. 카메라 드라이버 발행자에는 deadline을 설정하지 않았고, 새로 붙인 로깅 노드에만 요청 100 ms를 넣었다. 연결될까?

연결되지 않는다. 발행자 기본값은 보통 무한대로 해석되므로 100 − 무한대는 음수이고 표에서도 이 조합은 비호환이다. 고칠 쪽은 둘 중 하나다. 로깅 노드에서 deadline을 빼거나, 발행자에 실제 주기(50 ms)보다 긴 제공값을 명시하고 로깅 노드의 요청을 그 이상으로 맞춘다.

rmw_zenoh, 흔들리는 주기, 실물 정지에는 이 계산을 그대로 쓰지 않는다

사용하는 RMW부터 확인한다. ROS 2 문서 기준 기본 RMW는 Fast DDS이고(Different middleware vendors L93-L105), rmw_cyclonedds_cpp도 deadline을 DDS QoS로 넘긴다(rmw_node.cpp L2308). 반면 rmw_zenoh의 설계 문서는 DEADLINE을 “현재 미구현”으로 적고 비호환 QoS 설정이 사실상 없다고 설명한다(rmw_zenoh design.md L664-L668). rmw_zenoh를 쓴다면 이 글의 “연결 없음” 결론을 그대로 적용하면 안 된다.

예제는 주기가 전혀 흔들리지 않는다고 가정한 산술이라 지터, 네트워크 지연, 콜백을 호출하는 실행기(executor)의 처리 시간이 들어 있지 않다(Executors L15-L16). 실제 센서 간격은 매번 조금씩 달라지므로 두 번째 뺄셈이 0 근처라면 계산상 통과를 그대로 믿기 어렵다.

근거 문서의 기준은 계속 바뀌는 Rolling 판(고정 커밋 3352771, 2026-06-26)이다. Jazzy 판(커밋 d148c7b)에서도 정의, “더 엄격하지 않을 때만” 문장, 표의 y > x와 y < x 행이 같다(Jazzy 문서). 문서와 산술만으로는 답하지 못한 질문은 아래와 같으며, 실제 노드로 확인할 때 이 항목들이 결론을 바꿀 수 있다.

  • 요청값과 실제 간격이 정확히 같을 때(사례 8의 구독자 쪽 200 = 200) Requested deadline missed가 나는지. 문서는 이 경계를 명시하지 않는다.
  • deadline missed 이벤트가 몇 번, 어떤 간격으로 나는지.
  • deadline을 벽시계와 시뮬레이션 시간 중 무엇으로 재는지(use_sim_time과의 관계).
  • deadline에 0을 넣었을 때 현행 rclcpp·rclpy가 어떻게 해석하는지.
  • rclpy가 비호환을 만났을 때 남기는 기본 경고 문구.
  • 특정 배포판의 rmw_zenoh 바이너리가 실제로 어떻게 동작하는지와 향후 구현 여부.
  • Humble 등 Rolling·Jazzy 밖 배포판의 문서 내용.

실물 로봇에서 deadline missed 이벤트를 센서 고장 대응의 안전장치로 쓰면 안 된다. 앞에서 본 대로 설계 문서상 추적 지점은 rmw 계층의 수신 여부이며 애플리케이션이 데이터를 처리했는지는 보지 않는다. 센서가 끊겼을 때 로봇을 멈추는 정지 로직, 워치독(watchdog, 일정 시간 신호가 없으면 개입하는 감시 장치), 비상정지는 별도로 설계하고 검증해야 한다.

내 센서 토픽 하나로 두 뺄셈을 적어 본다

deadline QoS는 센서가 멈추거나 느려진 것을 통신 계층에서 알아차리고 싶은 DDS 계열 환경에 맞는 도구다. 콜백 처리 지연을 잡거나 실물 안전을 보장하는 용도로는 맞지 않는다. 적용할 때는 다음 순서로 숫자를 적는다.

  1. 센서 빈도를 주기로 바꾼다. 주기(ms) = 1000 / Hz.
  2. 발행자 제공값이 주기보다 긴지 본다. 제공 − 주기가 음수면 위반 조건이고, 0이면 경계다.
  3. 각 구독자의 요청값이 제공값 이상인지 본다. 요청 − 제공이 음수면 그 구독자는 연결되지 않는다. 발행자에 deadline이 없다면 구독자에도 넣지 않는다.
  4. 메시지가 오지 않으면 incompatible QoS 경고와 DEADLINE_QOS_POLICY를 먼저 찾고, deadline을 고친 뒤에도 안 되면 다른 정책을 본다.
  5. 연결된 뒤 느려짐을 알고 싶다면 deadline missed 이벤트를 받는다. incompatible QoS 이벤트는 짝이 맺어지지 않을 때의 신호다.

이벤트를 직접 관측해 보려면 공식 데모가 다음 단계다. quality_of_service_demo README는 ros2 run quality_of_service_demo_cpp deadline 600 --publish-for 5000 --pause-for 2000을 실행하면 발행이 멈춘 동안 양쪽에서 deadline 이벤트가 난다고 설명한다. 규칙 원문은 ROS 2 QoS 문서의 deadline 호환성 표와 직접 대조할 수 있다.

제휴·협찬 링크는 없다.

참고 링크

비슷한 글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다