제4장: 1단계 - 데이터 형식

弘益人間 (홍익인간) - 널리 인간을 이롭게 하라

4.1 1단계의 목적

1단계 명세(PHASE-1-DATA-FORMAT, v1.0.0)는 청소 로봇이 주고받는 데이터를 표현하는 공통 형식을 정한다. 대상은 로봇 식별 정보, 로봇 상태, 지도, 청소 세션 기록, 센서 데이터, 이벤트, 명령이다. 명세는 모든 데이터가 의미적 상호운용성을 위해 JSON-LD 형식을 사용해야 한다(MUST)고 규정한다.

JSON-LD는 일반 JSON 문서에 @context와 @type 같은 키워드를 더해, 각 필드가 무엇을 뜻하는지 기계가 해석할 수 있게 하는 W3C 표준 형식이다. WIA-ROB-011 문서의 @context는 https://wiastandards.com/rob-011/v1이며, @type은 문서가 어떤 종류의 데이터인지 나타낸다. 이 장에서 다루는 주요 타입은 다음과 같다.

@type내용명세 절
CleaningRobot로봇 식별 정보2.1
RobotState배터리·위치·동작 모드·센서 상태2.2
OccupancyGrid점유 격자 지도, 방, 진입 금지 구역, 도크 위치2.3
CleaningSession청소 세션 기록과 통계2.4
LidarScan, CameraImage센서 데이터3.1, 3.2
RobotEvent정보·경고·오류 이벤트4
RobotCommand로봇에 보내는 명령5

4.2 로봇 식별 정보 (CleaningRobot)

로봇 식별 정보는 제조사, 모델, 일련번호, 펌웨어·하드웨어 버전, 제조일, 지원 기능 목록을 담는다. 펌웨어 버전은 유의적 버전(semver) 형식으로 적는다.

{
  "@context": "https://wiastandards.com/rob-011/v1",
  "@type": "CleaningRobot",
  "robotId": "ROB-2025-XXXX-YYYY",
  "manufacturer": "string",
  "model": "string",
  "serialNumber": "string",
  "firmwareVersion": "semver",
  "hardwareVersion": "string",
  "manufactureDate": "ISO8601 date",
  "capabilities": ["array", "of", "capability", "strings"]
}

capabilities는 문자열 배열이다. 명세는 기능 이름의 목록을 따로 고정하지 않지만, 3단계의 mDNS 광고 예시에서는 slam, camera, mopping 같은 값을 사용한다(제6장 참조).

4.3 로봇 상태 (RobotState)

로봇 상태는 특정 시각의 배터리, 자세(pose), 동작 모드, 청소 설정, 센서 값을 하나의 문서로 표현한다.

{
  "@type": "RobotState",
  "timestamp": "ISO8601 datetime",
  "battery": {
    "percentage": 0-100,
    "voltage": "float (V)",
    "current": "float (A)",
    "temperature": "float (°C)",
    "health": 0-100,
    "cycleCount": "integer"
  },
  "pose": {
    "x": "float (meters)",
    "y": "float (meters)",
    "theta": "float (radians)",
    "floor": "integer",
    "confidence": 0-1
  },
  "mode": "idle|cleaning|charging|returning|error",
  "cleaningMode": "auto|spot|edge|zigzag|spiral",
  "suctionPower": 0-100,
  "brushSpeed": 0-100,
  "waterFlow": 0-100,
  "sensors": {
    "lidar": "SensorReading",
    "camera": "SensorReading",
    "cliff": ["boolean", "array"],
    "bumper": "boolean",
    "dirtLevel": 0-100
  }
}

동작 모드와 청소 모드

mode는 로봇이 지금 무엇을 하고 있는지를, cleaningMode는 청소 중일 때 어떤 방식으로 청소하는지를 나타낸다. 두 필드 모두 열거형(enum)이며, 명세는 열거값이 명세와 정확히 일치해야 한다고 규정한다.

필드값의미
modeidle대기
cleaning청소 중
charging충전 중
returning도크로 복귀 중
error오류 상태
cleaningModeauto자동 청소
spot국소 청소
edge가장자리 청소
zigzag지그재그 경로
spiral나선형 경로

suctionPower, brushSpeed, waterFlow, battery.percentage, battery.health, sensors.dirtLevel은 0~100 범위의 값이고, pose.confidence는 0~1 범위의 위치 추정 신뢰도다. 위치 좌표는 미터, 방향 theta는 라디안 단위를 쓴다.

4.4 지도 (OccupancyGrid)

지도는 점유 격자(occupancy grid) 방식으로 표현한다. 공간을 일정한 크기의 칸으로 나누고 각 칸이 비어 있는지, 장애물로 막혀 있는지를 값으로 기록하는 방식이다. 격자 데이터 자체는 uint8 배열을 base64로 인코딩해 data 필드에 담는다.

{
  "@type": "OccupancyGrid",
  "resolution": 0.05,
  "width": "integer (cells)",
  "height": "integer (cells)",
  "origin": {
    "x": "float (meters)",
    "y": "float (meters)",
    "theta": "float (radians)"
  },
  "data": "base64 encoded uint8 array",
  "rooms": [
    {
      "id": "UUID",
      "name": "string",
      "polygon": [[x, y], ...],
      "surfaceType": "hardwood|tile|carpet|vinyl|marble",
      "lastCleaned": "ISO8601 datetime",
      "cleanCount": "integer"
    }
  ],
  "noGoZones": [
    {
      "id": "UUID",
      "polygon": [[x, y], ...],
      "type": "no_go|restricted"
    }
  ],
  "dockLocation": { "x": "float", "y": "float", "theta": "float" }
}

resolution은 한 칸의 크기(미터)로, 명세 예시의 0.05는 한 칸이 5cm라는 뜻이다. 따라서 지도의 실제 크기는 width × resolution, height × resolution미터가 된다. 예를 들어 폭 200칸, 높이 160칸이면 10m × 8m 공간이다. 방(rooms)과 진입 금지 구역(noGoZones)은 다각형 꼭짓점 목록으로 표현하며, 이 좌표는 지도 범위 안에 있어야 한다. 방마다 바닥 재질(surfaceType)을 기록해 로봇이 재질에 맞게 흡입력이나 물 공급량을 조절할 수 있게 한다.

작성 시 주의: 다각형 좌표와 도크 위치는 격자 칸 번호가 아니라 미터 단위 좌표다. 칸 번호로 적으면 다른 시스템이 지도를 가져올 때 방 경계가 크게 어긋난다. 명세의 유효성 규칙 2(SI 단위 사용)가 이를 요구한다.

4.5 청소 세션 기록 (CleaningSession)

청소 세션 기록은 한 번의 청소 작업에 대한 시작·종료 시각, 소요 시간, 청소 면적, 이동 거리, 소비 에너지, 청소한 방, 중단 내역, 통계를 담는다.

{
  "@type": "CleaningSession",
  "sessionId": "UUID",
  "startTime": "ISO8601 datetime",
  "endTime": "ISO8601 datetime",
  "duration": "integer (seconds)",
  "areaCleaned": "float (m²)",
  "distanceTraveled": "float (m)",
  "batteryConsumed": "float (Wh)",
  "cleaningMode": "string",
  "rooms": ["room_id", "array"],
  "interruptions": [
    {
      "timestamp": "ISO8601 datetime",
      "reason": "stuck|battery_low|manual_stop|error",
      "duration": "integer (seconds)"
    }
  ],
  "statistics": {
    "avgSpeed": "float (m/s)",
    "avgSuction": "float (%)",
    "dirtCollected": "float (g)",
    "coverage": "float (%)"
  }
}

중단 사유(reason)는 끼임(stuck), 배터리 부족(battery_low), 사용자 정지(manual_stop), 오류(error) 네 가지다. 세션 기록은 2단계 API의 청소 이력 조회(GET /robot/history)가 돌려주는 기본 단위이기도 하다.

4.6 센서 데이터

LiDAR 스캔 (LidarScan)

LiDAR 스캔은 측정 각도 범위와 간격, 측정 가능 거리 범위, 거리 측정값 배열로 이루어진다. 반사 강도(intensities)는 선택 항목이다.

{
  "@type": "LidarScan",
  "timestamp": "ISO8601 datetime",
  "angleMin": "float (radians)",
  "angleMax": "float (radians)",
  "angleIncrement": "float (radians)",
  "rangeMin": "float (meters)",
  "rangeMax": "float (meters)",
  "ranges": ["float", "array"],
  "intensities": ["float", "array (optional)"]
}

카메라 이미지 (CameraImage)

카메라 이미지는 크기, 인코딩(rgb8, bgr8, jpeg, png), base64 이미지 데이터와 함께, 로봇이 인식한 물체 목록(detectedObjects)을 분류명·신뢰도·경계 상자로 담는다. 카메라 이미지는 개인정보에 민감한 데이터이므로 전송과 저장에 3단계의 보안 요구 사항을 적용해야 한다.

4.7 이벤트와 명령

이벤트 (RobotEvent)

{
  "@type": "RobotEvent",
  "eventId": "UUID",
  "timestamp": "ISO8601 datetime",
  "type": "info|warning|error|critical",
  "category": "navigation|cleaning|battery|sensor|communication",
  "message": "string",
  "details": { "arbitrary": "key-value pairs" }
}

이벤트는 심각도(type) 네 단계와 분류(category) 다섯 가지로 구분한다. details에는 이벤트별 부가 정보를 키-값 쌍으로 자유롭게 넣는다.

명령 (RobotCommand)

{
  "@type": "RobotCommand",
  "commandId": "UUID",
  "timestamp": "ISO8601 datetime",
  "action": "start|stop|pause|resume|dock|set_mode",
  "parameters": {
    "mode": "string (optional)",
    "rooms": ["array (optional)"],
    "powerLevel": "integer (optional)"
  }
}

명령의 동작(action)은 시작, 정지, 일시 정지, 재개, 도크 복귀, 모드 설정 여섯 가지다. 같은 명령을 2단계 REST API로 보낼 때는 POST /robot/clean, POST /robot/stop, POST /robot/dock 같은 엔드포인트를 사용하고, MQTT로 보낼 때는 wia/rob/{robot_id}/command 토픽을 사용한다(제5장, 제6장 참조).

4.8 유효성 규칙

명세 6절은 모든 1단계 데이터가 지켜야 할 규칙 여섯 가지를 정한다.

  1. 모든 시각은 시간대를 포함한 ISO 8601 형식을 사용해야 한다(예: 2025-12-26T14:30:00Z).
  2. 모든 측정값은 SI 단위를 사용해야 한다(미터, 초, 라디안 등).
  3. 모든 배열의 원소는 같은 타입이어야 한다.
  4. 모든 UUID는 버전 4여야 한다.
  5. 필수 필드는 null이어서는 안 된다.
  6. 열거값은 명세와 정확히 일치해야 한다.

제3장에서 본 적합성 등급 Tier 1은 JSON Schema 검증 보고서를 필수 산출물로 요구한다. 따라서 구현팀은 위 규칙을 JSON Schema로 옮겨 자동 검증에 사용하는 것이 실무적으로 유리하다.

4.9 확장 필드와 버전 관리

확장 필드 (x_)

구현체는 x_ 접두사를 붙인 사용자 정의 필드를 추가할 수 있다(MAY). 접두사 덕분에 제조사 고유 필드가 향후 표준에 추가될 필드와 충돌하지 않는다. 바닥 청소 이외의 로봇 유형이 고유 정보를 표현할 때도 이 방식을 쓴다(제3장 참조).

{
  "@type": "RobotState",
  "battery": 75,
  "x_customField": "vendor-specific data"
}

위 예시는 명세 7절의 원문을 그대로 옮긴 것으로, 확장 필드의 형태만 보여 준다. 실제 문서에서 battery는 4.3절의 객체 구조를 따라야 한다.

데이터 버전 관리

모든 데이터 형식에는 하위 호환성을 위한 버전 정보가 들어간다.

{
  "@context": "https://wiastandards.com/rob-011/v1",
  "@version": "1.0.0",
  "schemaVersion": "1.0.0"
}

명세의 마이그레이션 지침은 다음과 같다. 부 버전 사이에서는 하위 호환성을 유지하고, 주 버전을 바꿀 때는 마이그레이션 도구를 제공하며, 호환성을 깨는 변경은 모두 문서화하고, 전환 기간에는 여러 버전을 함께 지원한다.

4.10 전체 예시

다음은 명세 9절의 로봇 상태 예시다. 청소 중인 로봇의 배터리가 75%, 0번 층(floor: 0)의 (2.5m, 3.1m) 위치에서 약 90도(1.57rad) 방향을 향하고 있다.

{
  "@context": "https://wiastandards.com/rob-011/v1",
  "@type": "RobotState",
  "timestamp": "2025-12-26T14:30:00Z",
  "battery": {
    "percentage": 75,
    "voltage": 14.8,
    "current": 2.5,
    "temperature": 35.2,
    "health": 95,
    "cycleCount": 245,
    "charging": false,
    "timeToEmpty": 3600
  },
  "pose": { "x": 2.5, "y": 3.1, "theta": 1.57, "floor": 0, "confidence": 0.95 },
  "mode": "cleaning",
  "cleaningMode": "auto",
  "suctionPower": 80,
  "brushSpeed": 100,
  "sensors": {
    "lidar": { "timestamp": "2025-12-26T14:30:00Z", "ranges": [1.2, 1.5, 2.0] },
    "cliff": [false, false, false, false],
    "bumper": false,
    "dirtLevel": 45
  }
}

예시의 charging과 timeToEmpty(남은 사용 시간, 초)는 4.3절의 기본 구조에는 없는 필드로, 명세 예시가 배터리 객체에 추가 정보를 담을 수 있음을 보여 준다.

핵심 요약

복습 문제

  1. WIA-ROB-011이 일반 JSON이 아니라 JSON-LD를 요구하는 이유는 무엇인가?
  2. mode와 cleaningMode는 각각 무엇을 나타내는가?
  3. 해상도 0.05, 폭 300칸, 높이 200칸인 점유 격자 지도는 실제로 몇 미터 크기의 공간인가?
  4. 청소 세션의 중단 사유 네 가지를 들어라.
  5. 명세의 유효성 규칙 여섯 가지를 설명하라.
  6. x_ 확장 필드는 어떤 문제를 해결하는가?
  7. 부 버전과 주 버전이 바뀔 때 요구되는 조치는 각각 무엇인가?

📐 시뮬레이터 패널 3