로봇 발행 항목

관제 운영에 필요한 로봇 발행 데이터의 채널·페이로드 규약입니다. 기존 채널(상태·미션·입찰·자원)의 기본 규격은 연동 API 문서에 있으며, 본 문서는 그 위에 추가로 발행해야 할 항목을 정의합니다.

공통 envelope·인증·토픽 네임스페이스는 연동 API 문서 §1·§3을 따릅니다. 본 문서의 JSON 본문은 예시 값이며, 값 자체에 의미를 두지 않습니다.

1. 개요

로봇은 관측한 사실을 발행하고, 임계 판정·집계·경보는 관제가 수행합니다. 로봇은 다음 값을 싣지 않습니다.

구분채널내용
상태 확장r2s/{robot_id}/state위경도·방위각 · 속도 · 내부 온도 · 센서 상태 · 통신 품질 · 맵 버전 · 누적 주행 (§2)
접속 유지r2s/{robot_id}/stateMQTT keepalive · LWT 두절 판정 (§3)
보행자 안전r2s/{robot_id}/safety인지 · 회피 · 정지 · 재가동 (§4)
충전r2s/{robot_id}/state도킹 충전기 식별 · 배터리 상세 (§5)
미션 예외r2s/{robot_id}/goals실패 · 거절 · 중단 사유 (§6)
미션 거리r2s/{robot_id}/goals단계별 이동 거리 (§7)
소프트웨어r2s/{robot_id}/software컴포넌트별 설치 버전 (§8)

2. 상태 확장 — r2s/{robot_id}/state

기존 상태 발행(API 문서 §4.1) 페이로드에 아래 필드를 추가합니다. 발행 방식은 기존과 동일하게 변동 필드 이벤트 단위이며 retain=true입니다.

{
  "occurredAt": "2026-06-04T10:00:00+09:00",
  "payload": {
    "location": {
      "x": 0.0, "y": 0.0, "th": 0.0, "map_id": "",
      "lat": 37.5004000, "lng": 126.8556000, "heading": 273.4
    },
    "speed": 0.0,
    "internal_temp": 0.0,
    "sensors": {
      "lidar":  { "status": "ok", "errors_1h": 0 },
      "camera": { "status": "ok", "errors_1h": 0 },
      "imu":    { "status": "ok", "errors_1h": 0 }
    },
    "comm": {
      "uplink": "cellular",
      "wifi_rssi": -58, "wifi_ssid": "ZWS-AP-01",
      "net_type": "5G", "cellular_rsrp": -95, "cellular_level": "good"
    },
    "map": { "version": "", "synced_at": "2026-06-04T09:00:00+09:00" },
    "odometer": 0,
    "uptime": 0
  }
}
필드타입단위설명
payload.location.latnumberdegWGS84 위도. 소수점 7자리 권장
payload.location.lngnumberdegWGS84 경도
payload.location.headingnumberdeg로봇 전면이 향하는 방위. 진북 0°, 시계방향 증가(동 90 · 남 180 · 서 270), 0 이상 360 미만
payload.speednumberkm/h주행 속도
payload.internal_tempnumber°C본체 내부 온도
payload.sensors.{lidar,camera,imu}.statusenum-ok · degraded · fault
payload.sensors.{lidar,camera,imu}.errors_1hinteger건직전 1시간 오류 발생 수
payload.comm.uplinkenum-실제 트래픽이 나가는 경로. wifi · cellular
payload.comm.wifi_rssiintegerdBmWi-Fi 수신 세기 (미연결 시 null)
payload.comm.wifi_ssidstring-접속 중인 Wi-Fi SSID (미연결 시 null)
payload.comm.net_typestring-셀룰러 접속 방식. 5G · LTE
payload.comm.cellular_rsrpnumberdBm셀룰러 수신 전력. 로봇이 측정한 LTE RSRP 또는 NR RSRP 를 그대로 싣습니다. 미등록 시 null
payload.comm.cellular_levelenum-excellent · good · fair · poor · none
payload.map.versionstring-로봇이 현재 보유·사용 중인 맵 버전
payload.map.synced_atstringISO8601해당 맵을 마지막으로 반영한 시각
payload.odometerintegerm누적 주행 거리
payload.uptimeintegers부팅 후 누적 가동 시간

위경도 · 방위각 — location 확장

기존 location(API 문서 §4.1의 x·y·th·map_id)에 위경도와 방위각을 같은 객체로 싣습니다. 같은 시각·같은 자세를 서로 다른 기준계로 표현한 값입니다.

둘은 같은 발행에 함께 싣습니다. location을 발행할 때 한쪽만 갱신하면 같은 로봇이 화면마다 다른 위치·방향을 가리키게 됩니다.

heading 규약

위성 신호를 확보하지 못한 구간(실내·지하·음영)에서는 lat·lng·heading을 생략합니다. 직전 값을 유지해 보내지 마십시오 — 멈춘 좌표를 계속 받으면 관제는 그것이 현재 위치인지 갱신이 끊긴 값인지 구분할 수 없어, 로봇이 그 자리에 계속 있는 것으로 오표시됩니다. 이 구간에서도 x·y·th·map_id는 평소대로 싣습니다.

측지계는 WGS84(EPSG:4326)로 고정합니다. 관제 지도(카카오)가 같은 측지계를 쓰므로 별도 변환 없이 표시합니다.

기존 location_score(위치 신뢰도)와 ems(비상정지 소스)는 관제가 저장·표시하므로 값이 변할 때마다 발행합니다.

통신 품질 — comm 확장

comm은 로봇의 통신 상태를 실제 트래픽 경로·Wi-Fi 세기·셀룰러 신호로 나눠 싣습니다.

셀룰러 권외(미등록) 구간은 cellular_level: "none" · cellular_rsrp: null · net_type: null로 싣습니다. 직전 값을 유지해 보내지 마십시오 — 관제가 현재 상태인지 갱신이 끊긴 값인지 구분할 수 없습니다(location과 같은 규약).

3. 접속 유지 · 두절 판정

관제는 아래 메커니즘으로 통신 두절을 판정합니다.

로봇은 상태 변동 시에만 발행합니다. 주기 발행은 요구하지 않습니다. 관제는 브로커가 keepalive 만료 시 발행하는 LWT(online: false) 수신으로 통신 두절을 판정합니다.

4. 보행자 안전 이벤트 — r2s/{robot_id}/safety

보행자 인지·회피 상황을 이벤트로 발행합니다. 임계 초과 판정과 위험 영역 진입 판정은 관제가 수행하므로 로봇은 발행하지 않습니다.

4.1 토픽 · 발행 규약

4.2 페이로드

{
  "occurredAt": "2026-06-04T10:12:03.412+09:00",
  "payload": {
    "events": [
      {
        "eventId": "9f1c2a7e-3b41-4d0e-a7c5-1e2f9b6d8c04",
        "type": "avoid",
        "at": "2026-06-04T10:12:03.400+09:00",
        "location": { "x": 0.0, "y": 0.0, "th": 0.0, "map_id": "" },
        "floor": "B1",
        "trackId": "p-8821",
        "distanceCm": 42,
        "stopReason": null,
        "detail": { "sensor": "fusion", "targets": 2, "class": "person" }
      }
    ]
  }
}
필드타입단위설명
events[].eventIdstring (UUID)-이벤트 고유 식별자. 재전송 시 동일 값을 유지합니다(관제 중복 제거 키).
events[].typeenum-detect · avoid · stop · resume
events[].atstringISO8601이벤트 발생 시각(밀리초 권장). 묶음 발행 시 항목별로 싣습니다.
events[].locationobject-x · y · th · map_id. 상태 발행의 location과 동일 좌표계입니다.
events[].floorstring-층 식별자. 실내 이벤트에 싣습니다.
events[].trackIdstring-인지 대상 추적 식별자. 동일 대상인 동안 같은 값을 유지합니다.
events[].distanceCmintegercm보행자와의 이격 거리. 정의는 §4.4를 따릅니다.
events[].stopReasonenum-정지 사유. type=stop에만 싣습니다.
events[].triggerenum-재가동 주체. type=resume에만 싣습니다.
events[].detailobject-부가 정보(센서·대상 수·분류 등). 스키마를 강제하지 않습니다.

4.3 이벤트 타입별 필드

○ 필수 · △ 선택 · — 미사용

필드detectavoidstopresume
eventId · type · at · location○○○○
floor실내 이벤트 ○ · 실외 —
trackId○○△—
distanceCm△○△—
stopReason——○—
trigger———○
detail△△△△

4.4 값 규약

이격 거리 distanceCm

정지 사유 stopReason

값설명
pedestrian-near보행자 근접
crossing보행자 교차 진입
congestion통로 정체
emergency-stop비상 정지
obstacle장애물

위 5종 외의 사유는 obstacle로 싣고 원문을 detail.msg에 함께 전달합니다.

재가동 주체 trigger — auto(로봇 자체 판단) · operator(운영자 지시)

추적 식별자 trackId — 동일 대상을 추적하는 동안 같은 값을 유지하고, 추적이 끊긴 뒤 재획득하면 새 값을 부여합니다. 로봇 내부에서만 유일하면 되며 전역 유일성은 요구하지 않습니다.

4.5 발행 조건

타입발행 시점억제 규칙
detect보행자를 반경 3m 이내에서 인지한 시점동일 trackId는 30초 내 재발행하지 않습니다.
avoid회피 기동 종료 시점(최소 이격 확정 후) — 기동 1회당 1건-
stop보행자 요인으로 정지한 즉시-
resume정지 상태에서 재가동한 즉시-

주행 중 상시 인지되는 보행자를 모두 발행하지 않습니다. 위 조건에 해당하는 이벤트만 발행합니다.

4.6 발행 예시

10:12:01  detect   trackId p-8821   distanceCm 280
10:12:03  avoid    trackId p-8821   distanceCm 42
10:12:09  avoid    trackId p-8830   distanceCm 95
10:12:31  avoid    trackId p-8830   distanceCm 88
10:12:48  stop     stopReason pedestrian-near
10:13:20  resume   trigger auto

5. 충전 도킹 · 배터리

충전기 점유 현황을 관제가 파악할 수 있도록, 도킹한 충전기를 state에 싣습니다.

{
  "occurredAt": "2026-06-04T10:00:00+09:00",
  "payload": {
    "mode": "charging",
    "charging": { "charger_code": "CS-001", "docked": true, "docked_at": "2026-06-04T10:00:00+09:00" }
  }
}
필드타입설명
payload.charging.charger_codestring도킹한 충전기 식별자. 관제가 부여한 충전기 코드를 사용합니다.
payload.charging.dockedboolean도킹 여부. 해제 시 false로 발행합니다.
payload.charging.docked_atstring도킹·해제 시각(ISO8601)

배터리 필수 전송 필드

API 문서 §4.1의 battery 필드 중 아래는 반드시 전송합니다.

필드전송 시점
soc상시 (변동 시)
voltage · current · temperature상시 (변동 시)
est_charge_complete충전 중
soh상시 (변동 시)

6. 미션 수행 예외 — r2s/{robot_id}/goals

goal·mission이 canceled로 전이할 때 사유를 함께 발행합니다. 관제는 이 사유로 재배차 여부를 판단합니다.

{
  "occurredAt": "2026-06-04T10:00:00+09:00",
  "payload": {
    "goals": [
      {
        "id": "goal-1",
        "type": "customer",
        "status": "canceled",
        "reason": "path-blocked",
        "reasonMsg": "",
        "mission": [
          { "id": "m-1", "type": "dropoff", "status": "canceled", "reason": "door-not-opened" }
        ]
      }
    ]
  }
}
값설명
path-blocked경로 차단 — 우회 불가
robot-fault로봇 이상 — 수행 불가
battery-low배터리 부족
elevator-unavailable엘리베이터 사용 불가
door-not-opened출입문·박스 개방 실패
timeout대기 시간 초과
operator-cancel운영자 지시로 중단
unknown그 외 — reasonMsg에 원문을 함께 전달

reason은 goal·mission 양쪽에 실을 수 있습니다. mission 사유가 있으면 그 값이 goal 사유보다 구체적인 원인입니다.

7. 미션 수행 거리 — r2s/{robot_id}/goals

단계(mission[])가 done으로 전이할 때 그 단계에서 이동한 거리를 함께 발행합니다. 관제는 이 값을 단계의 orderId로 묶어 주문별 배달 거리를 산출합니다.

{
  "occurredAt": "2026-06-04T10:00:00+09:00",
  "payload": {
    "goals": [
      {
        "id": "goal-1",
        "type": "customer",
        "status": "done",
        "mission": [
          { "id": "goal-1_00_move",         "type": "move",         "orderId": "order-1", "status": "done", "distance": 137 },
          { "id": "goal-1_01_elevatorCall", "type": "elevatorCall", "orderId": "order-1", "status": "done" },
          { "id": "goal-1_02_move",         "type": "move",         "orderId": "order-1", "status": "done", "distance": 24 },
          { "id": "m-1",                    "type": "dropoff",      "orderId": "order-1", "status": "done", "distance": 0 }
        ]
      }
    ]
  }
}
필드타입단위설명
payload.goals[].mission[].distanceintegerm그 단계에서 이동한 거리. 누적이 아닌 구간 값입니다

값 규약

주문 귀속

로봇은 여러 주문의 goal 을 한 메시지에 묶어 발행하며(API 문서 §5.2), 주문 식별자는 goal 이 아니라 단계에 실립니다(mission[].orderId). 거리를 단계에 싣는 이유가 이것입니다 — goal 단위 값으로는 한 goal 안에 여러 주문의 단계가 섞였을 때 나눌 수 없습니다. 로봇은 지금처럼 각 단계에 orderId를 채워 보내면 됩니다.

두 주문을 함께 싣고 이동하는 구간은 그 시점에 수행 중인 단계의 주문으로 귀속됩니다. 주문 간 분배 규칙은 관제가 정하며 로봇은 관여하지 않습니다. 단계 id는 한 goal 안에서 서로 달라야 합니다 — 같으면 관제가 두 단계를 한 단계로 읽어 거리가 누락됩니다.

8. 소프트웨어 인벤토리 — r2s/{robot_id}/software

로봇이 탑재한 컴포넌트별 버전을 발행합니다. MQTT 접속 직후 1회와 버전 변경 시 발행하며, retain=true입니다.

{
  "occurredAt": "2026-06-04T10:00:00+09:00",
  "payload": {
    "components": [
      { "name": "navi",   "version": "1.4.2" },
      { "name": "gui",    "version": "2.0.1" },
      { "name": "robots", "version": "3.1.0" },
      { "name": "sub",    "version": "0.9.7" }
    ]
  }
}
필드타입설명
payload.components[].namestring컴포넌트 식별자 (navi · gui · robots · sub 등)
payload.components[].versionstring설치 버전 문자열

9. 지오펜스 — state.geofence · r2s/{robot_id}/geofence

관제가 배포한 영역(지오펜스)을 로봇이 내려받아 보유하고, 주행 중 스스로 판정합니다. 로봇이 발행하는 것은 두 가지입니다 — 적용 버전 회신(§9.1)과 우회·차단 이벤트(§9.2). 영역 진입·이탈 판정은 관제가 수행하므로 로봇은 발행하지 않습니다.

영역을 내려받는 절차(버전 알림 · 조회 · 적용 시점)는 시나리오 문서의 지오펜스 절을 따릅니다.

9.1 적용 버전 회신 — state.geofence

새 토픽을 만들지 않고 기존 상태 발행(r2s/{robot_id}/state)에 geofence 객체를 얹습니다. map과 같은 자리입니다.

{
  "geofence": {
    "version": 42,
    "applied_at": "2026-09-22T10:00:05+09:00"
  }
}
필드타입단위설명
geofence.versioninteger-로봇이 현재 적용 중인 단지 지오펜스 버전. 내려받기만 하고 아직 적용하지 않았다면 이전 값을 유지합니다.
geofence.applied_atstringISO8601그 버전을 실제로 적용한 시각.

9.2 우회 · 차단 이벤트 — r2s/{robot_id}/geofence

영역 때문에 경로를 바꾸거나 진행할 수 없게 된 경우에 발행합니다. 관제는 로봇의 위치만으로는 "영역 때문에 돌아간 것"과 "원래 그 경로"를 구분할 수 없으므로, 이 판단은 로봇만 보고할 수 있습니다.

토픽 · 발행 규약

페이로드 — 우회(detour)

{
  "occurredAt": "2026-09-22T10:04:45.120+09:00",
  "payload": {
    "eventId": "3f9a1c02-77d5-4a11-9c3e-5b0e1f7a2d48",
    "type": "detour",
    "geofenceId": "GF-01a0c31f",
    "missionId": "MSN-20260922-0031",
    "at": "2026-09-22T10:04:45.100+09:00",
    "extraDistanceM": 340,
    "extraSeconds": 95
  }
}

페이로드 — 차단(blocked)

{
  "occurredAt": "2026-09-22T10:06:11.480+09:00",
  "payload": {
    "eventId": "b1d77c54-2e08-4f93-8a6d-0c4e91f3b5a7",
    "type": "blocked",
    "geofenceId": "GF-01a0c318",
    "missionId": "MSN-20260922-0031",
    "at": "2026-09-22T10:06:11.450+09:00",
    "hasAlternative": false,
    "location": { "x": 61.0, "y": 171.5, "th": 0.0, "map_id": "ground" }
  }
}
필드타입단위설명
eventIdstring (UUID)-이벤트 고유 식별자. 재전송 시 동일 값을 유지합니다.
typeenum-detour · blocked
geofenceIdstring-원인이 된 영역 식별자. 조회 응답의 id와 같은 값입니다.
missionIdstring-수행 중이던 미션. 미션 밖 주행이면 null.
atstringISO8601판단이 성립한 시각(밀리초 권장).
extraDistanceMnumbermdetour 필수. 영역을 피하지 않았을 경우의 경로 대비 추가 이동 거리.
extraSecondsnumber초detour 필수. 같은 기준의 추가 소요 시간(추정값 허용).
hasAlternativeboolean-blocked 필수. 목적지까지 다른 경로가 존재하는지. 존재하나 손실이 과도해 포기한 경우 true.
locationobject-blocked 필수. 진행 불가로 멈춘 지점. 상태 발행의 location과 동일 좌표계입니다.

9.3 값 규약

9.4 발행 조건

10. 추후 범위 추후 추가 예정

아래 항목은 채널·담당 경계를 확정한 뒤 본 문서에 규격을 추가합니다.

항목내용선행 조건
경로 이탈 · 리플래닝 보고영역 외 사유(장애물·통행 불가 등)의 우회 발생·사유·지연 예상. 영역에 의한 우회는 §9.2보고 단위(이벤트 / goals 확장) 확정
펌웨어 OTA 진행 보고수락·거절·진행률·성공·실패·롤백OTA 담당 경계(관제 배포 / 제로웍스 자체 갱신) 확정
원격 명령 응답긴급정지·복귀·속도제한 명령의 수신·수락·적용 회신원격 명령 채널(s2r) 확정
적재함 재실 감지칸별 물품 유무·잠금 상태감지 수단 확정 (현재 door[] 개폐만 수신)
자가진단 결과부팅 점검 항목·통과 여부점검 항목 목록 확정
라이브 영상원격 제어 영상 스트림스트리밍 방식 확정