🚚 WIA 배송 로봇 표준 전자책 | 제2장 (총 8장)
WIA ROB-010 로봇이 플릿 플랫폼에 보내는 모든 메시지는 물리 세계에 대한 하나의 주장에서 출발한다. 나는 지금 여기에 있고, 이쪽을 향하고 있으며, 주변에는 이런 것들이 있다. 1단계는 이 주장에 형식을 부여한다. RobotState의 position·heading·speed 필드와 SensorReadings 형식의 선택 필드 sensors가 그것이다. 3단계는 §2와 §3에서 로봇이 이 숫자에 도달하는 방법, 곧 전역 측위, SLAM, 다중 센서 융합, 장애물 분류를 기술한다. 이 장은 두 단계의 밑바탕에 있는 공학을 설명한다. 목표는 페이로드에 담은 값이 API 반대편의 소비자가 가정하는 바로 그 의미를 갖도록 하는 것이다.
다루는 범위는 의도적으로 좁다. 이 장은 감지와 상태 추정, 즉 좌표계, 위치 출처, 지도 작성, 센서 특성, 필터링, 그리고 원시 검출에서 추적 장애물에 이르는 경로를 다룬다. 로봇이 그 정보로 무엇을 하는지(경로 계획, 안전 여유 거리, 행동 상태 기계, 비상 정지)는 제5장의 몫이다. 전체 RobotState 예시와 검증 코드를 포함한 스키마 해설은 제3장에 있으며, 여기서는 감지 방식이 값을 결정하는 필드만 언급한다.
이 장 전체에서 표준에서 가져온 수치는 출처를 밝힌다. 일반적인 하드웨어 범위는 "일반적"이라고 표시하며 요구사항이 아니다. 계산 예시는 예시 값임을 명시한다.
좌표계가 없는 위치는 의미가 없다. 로봇 텔레메트리의 통합 결함은 대부분 산술 오류가 아니라 좌표계 오류다. 배송 로봇은 동시에 최소 세 개의 좌표계를 쓰며, 표준은 그중 두 개와 직접 맞닿는다.
1단계 §2.2는 Position을 latitude(−90~90), longitude(−180~180), "해수면 기준 미터"로 설명된 선택 필드 altitude, 미터 단위의 선택 필드 accuracy, 선택 필드 source로 정의한다. 스키마는 측지 기준계를 명시하지 않는다. 그러나 GNSS 수신기, 지도 서비스, 플릿 대시보드 사이에서 주고받는 위도·경도는 관례상 거의 예외 없이 GPS 자체가 쓰는 WGS84로 표현된다. 구현체는 이를 문서에 명시해야 한다. 국가 측량 좌표계나 건물 CAD 도면을 기준으로 측위하는 로봇이라면 게시하기 전에 반드시 변환해야 하기 때문이다.
고도는 한 번 더 따져 볼 필요가 있다. GNSS 수신기가 본래 계산하는 값은 WGS84 타원체로부터의 타원체고다. "해수면 기준"은 지오이드로부터 잰 정표고다. 둘의 차이인 지오이드고는 지역마다 다르며 여러 곳에서 수십 미터에 이른다. 어떤 수신기는 전자를, 어떤 수신기는 후자를, 어떤 수신기는 둘 다 출력한다. 1단계가 "해수면 기준"이라고 적은 이상, 적합한 생산자는 정표고를 게시하거나 그렇지 않다는 사실을 분명히 문서화해야 한다. 보도 로봇에게 절대 고도가 안전과 직결되는 경우는 드물다. 중요한 것은 건물 몇 층에 있는지 판단할 때의 상대 높이이며, 그래서 실내 배치에서는 고도에만 의존하지 않고 자체 지도 데이터에 층 식별자를 둔다.
수치 정밀도도 중요하다. 위도 1도는 약 111 km이므로 소수점 다섯째 자리는 약 1.1 m, 일곱째 자리는 약 1.1 cm에 해당한다. JSON 숫자는 보통 64비트 배정밀도로 파싱되므로 정밀도가 충분하다. 함정은 중간 저장소나 메시지 정의가 32비트 부동소수점을 쓰는 경우다. float32는 유효숫자가 십진수 약 일곱 자리이므로 126.97801 같은 경도는 대략 미터 수준에서 해상도를 잃는다. 텔레메트리 파이프라인이 RTK로 센티미터 정확도를 주장한다면, 수신기에서 데이터베이스까지 모든 구간에 float32가 끼어 있지 않은지 확인해야 한다.
인지와 제어는 위도·경도가 아니라 미터 단위의 직교 좌표에서 이루어진다. 제1장에서 공통 미들웨어 계층으로 짚은 ROS 생태계는 대부분의 배송 로봇 스택이 따르는 두 가지 규약을 정해 두었다.
map → odom → base_link 사슬을 정의한다. base_link는 로봇에 고정된다. odom은 세계 고정 좌표계로, 자세가 시간이 지나며 표류하지만 갑자기 튀지 않으므로 지역 제어에 적합하다. map은 전역적으로 일관되지만 위치 추정이 보정될 때 튈 수 있는 세계 고정 좌표계로, 전역 계획과 보고에 적합하다.3단계 §3.2는 장애물 위치를 좌표계 명시 없이 {"x", "y", "z"}로 게시한다. 2.6절에서 다시 다루겠지만, "x = 5.2"는 base_link에서는 로봇 전방 5.2 m를, map에서는 임의의 지도 원점에서 동쪽으로 5.2 m를 뜻한다는 점만 기억해 두자.
배송 로봇이 다니는 수 킬로미터 범위에서 전역 좌표와 로컬 좌표를 오갈 때는 흔히 로컬 원점(위도 φ0, 경도 λ0)을 정하고 국소 접평면 근사를 쓴다.
east_m ≈ R · (λ − λ0) · cos(φ0) (각도는 라디안)
north_m ≈ R · (φ − φ0)
R = 6,378,137 m (WGS84 장반경)
이 등장방형 근사는 원점 주변의 짧은 거리에서는 충분하다. 더 넓은 지역이나 측량 수준의 작업에서는 근사의 오차를 키우지 말고, UTM 투영이나 완전한 측지-ENU 변환 같은 정식 투영 라이브러리를 써야 한다.
RobotState의 heading은 0~360 범위의 도 단위 값이며, 1단계 §2.1은 이를 "0 = 북쪽, 90 = 동쪽"으로 설명한다. 이는 북쪽에서 시계 방향으로 재는 나침반(항법) 규약이다. ENU 좌표계의 ROS 요는 수학 규약으로, 0이 동쪽이고 반시계 방향으로 증가하며 단위는 라디안이다. 둘은 반사와 90° 이동으로 연결된다.
heading_deg = (90 − yaw_deg) mod 360
yaw_deg = (90 − heading_deg) mod 360 (같은 식이며, 자기 자신이 역변환이다)
| 속성 | 1단계 heading | ROS 요(REP-103, ENU) |
|---|---|---|
| 0의 방향 | 북쪽 | 동쪽 |
| 양의 회전 | 시계 방향(위에서 볼 때) | 반시계 방향(위에서 볼 때) |
| 단위 | 도 | 라디안 |
| 범위 | 0~360(스키마) | 보통 −π~π |
| 동쪽을 향할 때 | 90 | 0 |
| 북쪽을 향할 때 | 0 | π/2 |
| 남서쪽을 향할 때 | 225 | −3π/4(−135°) |
두 규약을 섞으면 시험에서 잘 드러나지 않는 오류가 생긴다. 북동쪽으로 달리는 로봇은 두 규약 모두에서 45이므로 그 대각선을 따라가는 시험 경로는 통과한다. 그러나 북쪽으로 달리는 로봇은 0 대신 90을 보고하고, 플릿 대시보드는 그 로봇이 동쪽으로 달린다고 그린다. 해법은 내부 상태가 1단계 페이로드로 바뀌는 경계에 변환 함수를 단 하나만 두고, 네 방위에 대한 단위 시험을 붙이는 것이다. 세부 사항이 두 가지 있다. 스키마는 0과 360을 모두 허용하므로 북쪽이 두 가지로 인코딩되지 않도록 [0, 360)으로 정규화한다. 또 ROS는 자세를 쿼터니언으로 저장하므로 먼저 쿼터니언에서 요를 뽑아내야 한다.
import math
def quaternion_to_yaw(qx: float, qy: float, qz: float, qw: float) -> float:
"""ROS 자세 쿼터니언에서 z축 회전(요)을 라디안으로 구한다."""
siny_cosp = 2.0 * (qw * qz + qx * qy)
cosy_cosp = 1.0 - 2.0 * (qy * qy + qz * qz)
return math.atan2(siny_cosp, cosy_cosp)
def ros_yaw_to_phase1_heading(yaw_rad: float) -> float:
"""REP-103 ENU 요(0 = 동쪽, 반시계, 라디안) -> 1단계 heading
(0 = 북쪽, 시계, 도, [0, 360)으로 정규화)."""
heading = (90.0 - math.degrees(yaw_rad)) % 360.0
# 359.9999999나 360.0 같은 부동소수점 결과를 방지한다
return 0.0 if math.isclose(heading, 360.0) else heading
# 네 방위 시험: 좌표계 버그를 막는 가장 싼 보험이다
assert ros_yaw_to_phase1_heading(0.0) == 90.0 # 동쪽
assert ros_yaw_to_phase1_heading(math.pi / 2) == 0.0 # 북쪽
assert ros_yaw_to_phase1_heading(math.pi) == 270.0 # 서쪽
assert ros_yaw_to_phase1_heading(-math.pi / 2) == 180.0 # 남쪽
파이썬의 % 연산자는 제수가 양수이면 음수가 아닌 결과를 돌려주므로, 이 식은 음의 요에서도 별도 분기 없이 동작한다. C나 C++의 fmod는 피제수의 부호를 따르므로 360을 더한 뒤 다시 나머지를 구해야 한다.
3단계 §2.1은 전역 측위를 세 층으로 나눈다. 1차 수단인 GPS/GNSS, 강화 수단인 RTK-GPS, 대체 수단인 추측 항법이다. 셋은 정확도만이 아니라 원리가 다르며, 그 원리를 알면 각각이 언제 실패하는지 알 수 있다.
GNSS 수신기는 여러 위성의 코드 신호로 측정한 의사거리, 즉 신호 도달 시간으로 자기 위치를 추정한다. 최신 수신기는 GPS, Galileo, GLONASS, BeiDou와 지역 시스템을 함께 써서 하늘이 일부 가려진 곳에서도 버틴다. 3단계는 개활지에서 1~5 m의 정확도와 1~10 Hz의 갱신 주기를 일반적인 값으로 제시하고, 도심 협곡·터널·실내에서의 성능 저하를 한계로 적는다.
보도 로봇에게 가장 중요한 것은 도심 협곡 문제다. 높은 건물이 여러 위성으로의 직접 가시선을 막고, 남은 신호도 건물 외벽에 반사되어 도착하는 경우가 많다(다중 경로). 반사 신호는 직접 경로보다 멀리 돌아오므로 수신기는 거리를 길게 계산하고, 위치 해는 개활지 수치보다 훨씬 크게, 흔히 도로 중앙선 쪽이나 그 너머로 밀린다. 더 나쁜 것은 수신기가 스스로 보고하는 정확도 추정치가 이 변위를 반영하지 못할 수 있다는 점이다. 길 이쪽에 있는 로봇이 길 건너편에 있다고 보고될 수 있다. 보도를 벗어나지 않는 것이 안전의 전제인 플랫폼에게 단독 GNSS는 대략적인 전역 기준점일 뿐, 차선 수준의 위치가 아니다.
실시간 이동 측위(RTK)는 코드뿐 아니라 위성 신호의 반송파 위상을 쓴다. 반송파 파장은 약 20 cm 수준이므로 위성과 안테나 사이에 들어 있는 정수 개의 파장 수("정수 모호성")를 결정할 수만 있다면 훨씬 세밀하게 위치를 풀 수 있다. 위치를 아는 기준국이 같은 위성을 관측하고 보정 정보를 보내는데, 흔히 NTRIP 같은 네트워크 프로토콜 위에 RTCM 메시지로 전송한다. 대기와 궤도 오차의 대부분은 가까운 수신기끼리 공유되므로 이동국은 이를 상쇄할 수 있다. 3단계는 일반적인 정확도로 1~10 cm를 제시하고 기준국이 필요하다고 적는다.
RTK에는 텔레메트리가 감춰서는 안 되는 뚜렷한 동작 상태가 있다. 모호성이 풀린 상태("fix")에서는 센티미터 수준이다. 풀리지 않은 상태("float")에서는 데시미터 이상으로 나빠진다. 보정 링크가 끊기면 수신기는 단독 GNSS로 돌아간다. 도심 협곡에서는 GNSS를 괴롭히는 바로 그 다중 경로 때문에 fix를 얻기도, 유지하기도 어렵다. 조용히 float 모드로 떨어진 뒤에도 source: "rtk-gps"와 2 cm 정확도를 보고하는 로봇은 거짓 주장을 게시하는 셈이다. 하드웨어의 능력이 아니라 현재 해의 출처와 정확도를 게시해야 한다.
추측 항법은 로봇 자신의 센서로 움직임을 추정한다. 휠 엔코더는 각 바퀴가 얼마나 돌았는지를, 관성 측정 장치(IMU)는 가속도와 각속도를 잰다. 이를 적분하면 출발점 기준의 자세가 나온다. 실내·지하·어떤 날씨에서도 동작하고 갱신 주기가 높으므로 모든 배송 로봇이 이것을 갖추고 있다. 약점은 구조적이다. 매 단계의 추정이 이전 단계 위에 쌓이므로 오차가 누적된다.
따라서 추측 항법은 목적지가 아니라 다리다. GNSS 공백이나 SLAM 갱신 사이를 건너게 해 줄 뿐이다. 3단계의 고장 모드 표는 GPS를 잃은 뒤 로봇이 얼마나 오래 운행할 수 있는지에 시간 제한을 두며, 그 행동은 제5장에서 다룬다. 감지 관점에서 핵심은, 추측 항법 동안에는 불확실성이 커지고 있으므로 로봇이 보고하는 accuracy도 함께 커져야 한다는 점이다.
| 계층 | 원리 | 일반적 정확도 | 주요 실패 조건 | position.source |
|---|---|---|---|---|
| GNSS(1차) | 여러 위성의 코드 의사거리 | 개활지 1~5 m | 도심 협곡, 터널, 실내, 다중 경로 | gps |
| RTK-GPS(강화) | 반송파 위상 + 기준국 보정 | fix 상태에서 1~10 cm | 보정 링크 단절, fix 상실, 하늘 가림 | rtk-gps |
| 추측 항법(대체) | 휠 오도메트리 + IMU 적분 | 무한정 표류, 거리·시간에 따라 악화 | 바퀴 미끄러짐, 센서 편향, 긴 공백 | 전용 값 없음. 보통 fused 안에서 보고 |
마지막 열은 작은 공백을 드러낸다. source 열거형(gps, rtk-gps, slam, visual-odometry, fused)에는 순수 추측 항법을 위한 값이 없다. 실제로 오도메트리만으로 달리는 로봇도 다른 입력이 빠진 채 융합 필터를 계속 돌리고 있으므로, 정직하게 부풀린 accuracy와 함께 fused로 인코딩하는 것이 가장 방어 가능한 선택이다. 소비자가 fused를 "고품질"로 읽지 않도록 이 선택을 구현 문서에 적어 두어야 한다.
동시적 위치 추정 및 지도 작성(SLAM)은 닭과 달걀의 문제를 푼다. 위치를 알려면 지도가 필요하고, 지도를 만들려면 위치를 알아야 한다. SLAM은 둘을 함께 추정한다. 배송 로봇에게 SLAM은 GNSS가 주지 못하는 것을 준다. 위성이 가려지거나 없는 곳에서도 정확하고 좌표계가 일관된 위치, 그리고 로봇이 자기 위치를 알아볼 수 있는 주변 환경 모델이다.
3단계 §2.2는 SLAM을 여섯 단계로 요약한다. 단계마다 특유의 실패가 있고, 이를 알면 로봇이 보고하는 값을 해석하는 데 도움이 된다.
odom 좌표계와 전역적으로 일관된 map 좌표계를 나눈다.| 지도 유형 | 내용 | 강점 | 배송 로봇에서의 일반적 용도 |
|---|---|---|---|
| 점유 격자 지도 | 점유·비어 있음·미지로 표시한 2D(또는 3D 복셀) 셀. 흔히 확률로 저장 | 충돌 검사와 경로 계획에 바로 쓸 수 있음 | 지역 비용 지도, 실내 평면도, 장애물 팽창 |
| 특징 지도 | 위치가 붙은 두드러진 랜드마크의 희소 집합 | 간결하고 위치 추정·루프 폐합에 효율적 | 알려진 경로를 따라 재측위, 시각 SLAM |
| 의미 지도 | 유형(나무, 건물, 횡단보도)이 붙은 객체와 영역 | 규칙이 활용할 수 있는 의미를 담음 | 횡단보도·경사로·출입문 위치 파악, 구역별 행동 |
실제 배치된 로봇은 보통 이들을 조합한다. 사전 조사로 만든 의미 지도, 위치 추정용 특징 지도, 실시간 장애물 처리를 위해 로봇 주변에서 굴러가는 점유 격자가 함께 쓰인다. 이 지도들은 1단계 페이로드로 교환되지 않는다. 표준이 교환하는 것은 지도 자체가 아니라 위치 추정의 결과다.
SLAM은 map 좌표계에서 자세를 낸다. 이를 position.latitude와 longitude로 채우려면 map 좌표계가 지리 참조되어 있어야 한다. 즉 원점과 방향이 측량 기준점이나 지도 작성 중 수집한 RTK fix와의 정렬을 통해 WGS84에 묶여 있어야 한다. 지리 참조가 부정확한 지도 안에서 완벽하게 위치를 잡는 로봇은 정밀하지만 틀린 전역 위치를 게시한다. accuracy 값에는 SLAM 잔차뿐 아니라 지리 참조 오차도 포함해야 한다.
열거형의 slam과 visual-odometry도 분명히 구분해야 한다. 시각 오도메트리는 연속 카메라 프레임으로 증분 운동을 추정하며, 휠 오도메트리처럼 대조할 지도가 없으므로 표류한다. SLAM은 지도 정합과 루프 폐합으로 표류를 바로잡는다. 시각 오도메트리를 slam으로 보고하면 긴 거리에서의 추정 신뢰도를 과장하게 된다.
3단계 §3.1은 장애물 감지를 위한 네 가지 센서를 비교한다. 아래 수치는 표준의 것이며 일반적인 값으로 표시한다. 실제 값은 제품, 장착 위치, 환경에 크게 좌우되며, 표준은 센서 최소 성능을 정하지 않는다.
| 센서 | 거리 | 시야각 | 날씨·조명 | 갱신 주기 | 상대 비용 | SensorReadings 필드 |
|---|---|---|---|---|---|---|
| LiDAR | 0.1~100 m | 360° | 비·안개에 취약 | 10~20 Hz | 높음 | lidar[](점 x, y, z, intensity) |
| 카메라 | 0.5~50 m | 60~120° | 어둠·눈부심에 취약 | 30~60 Hz | 낮음 | cameras[](image_url, detections) |
| 초음파 | 0.02~5 m | 15~30° | 매우 좋음 | 10~40 Hz | 매우 낮음 | ultrasonic[](distance_cm) |
| 레이더 | 1~200 m | 15~120° | 매우 좋음 | 10~20 Hz | 중간 | 정의 없음 |
LiDAR는 레이저 반사 시간을 재어 거리를 직접 측정하므로 주변 조명과 무관하게 정확한 3차원 형상을 준다. 그래서 대부분의 SLAM과 장애물 감지 파이프라인의 중추가 된다. 약점은 비·안개·눈·먼지에 의한 산란(센서 가까이에 허상 반사를 만들 수 있다), 투명하거나 반사가 강한 표면, 그리고 먼 거리에서의 성긴 표본화다. 먼 곳의 작은 물체는 빔 몇 개에만 맞을 수 있다.
카메라는 조밀한 질감과 색을 제공하며, 이는 분류에 꼭 필요하다. 보행자와 기둥을 구분하고, 횡단보도 신호를 읽고, 안내견을 알아보는 일이 여기에 달려 있다. 단안 카메라는 깊이를 직접 재지 못한다. 깊이는 스테레오 쌍, 움직임, 학습 기반 추정에서 얻으며, 모두 LiDAR 거리 측정보다 간접적이다. 카메라는 밤, 역광과 눈부심, 렌즈가 젖거나 더러울 때 약하다. 얼굴과 번호판을 찍기 때문에 개인정보 보호 의무도 생긴다. 플릿 파이프라인에서 이를 다루는 방법은 제6장에서 설명한다.
초음파 센서는 음파 펄스를 보내고 반향 시간을 잰다. 싸고, 조명의 영향을 받지 않으며, 비에도 거의 영향을 받지 않고, LiDAR가 놓칠 수 있는 유리도 본다. 원뿔 모양의 빔 탓에 각 해상도가 나쁘고, 부드럽거나 비스듬한 표면은 반향을 돌려주지 않을 수 있다. 배송 로봇에서는 대개 차체를 두르는 근거리 안전 링을 이루어, 지붕에 단 LiDAR가 보지 못하는 사각을 메운다.
레이더는 거리와 함께 도플러 효과로 상대 시선 속도를 직접 재며, 비와 안개를 잘 투과한다. 각 해상도는 LiDAR보다 거칠고, 작고 반사가 약한 물체는 잡기 어렵다. 레이더는 도로 차량에서 흔하며 소형 모듈로도 점점 더 구할 수 있다. 1단계 SensorReadings에는 radar 필드가 정의되어 있지 않다는 점에 주의해야 한다. 레이더를 쓰는 로봇은 그 기여를 융합된 장애물 출력으로, 또는 명확히 문서화한 확장 필드로 보고한다.
이 표는 3단계가 최고의 센서 하나가 아니라 융합을 요구하는 이유를 설명한다. 실패 조건이 대체로 상보적이다. LiDAR가 실패하는 안개에서 레이더는 버티고, 카메라가 실패하는 어둠에서 LiDAR는 버티며, 초음파는 다른 센서가 잘 못 보는 근거리를 덮는다. 설계 검토에서는 운행 지역의 환경 조건(밤, 비, 눈부심, 인파, 유리 상점 전면)마다 어떤 센서가 여전히 동작하고 로봇의 인지가 어느 수준으로 떨어지는지 물어야 한다.
융합은 여러 개의 불완전한 추정을 하나의 더 나은 추정으로 합친다. 기준이 되는 방법은 칼만 필터다. 3단계 §3.1은 융합 전략의 넷째 단계로 "칼만 필터 융합"을, §2.2는 SLAM용으로 확장 칼만 필터를 든다. 핵심 아이디어는 1차원 예시 하나에 담긴다.
곧은 보도를 따라가는 로봇의 위치처럼 하나의 상태 x와, 그 추정이 얼마나 불확실한지를 나타내는 분산 P를 생각하자. 매 주기는 두 단계로 이루어진다.
예측 (운동 모델. 예: 휠 오도메트리가 변위 u를 보고)
x⁻ = x + u
P⁻ = P + Q Q = 과정 잡음: 움직임이 더하는 불확실성
갱신 (분산 R을 가진 측정값 z. 예: GNSS나 SLAM 위치)
K = P⁻ / (P⁻ + R) 칼만 이득, 0과 1 사이
x = x⁻ + K · (z − x⁻)
P = (1 − K) · P⁻
이득 K는 누구를 믿을지 정한다. 예측이 불확실하고(P⁻가 크고) 측정이 정밀하면(R이 작으면) K는 1에 가까워지고 추정은 거의 측정값까지 옮겨 간다. 측정에 잡음이 많으면 K는 0에 가까워지고 예측이 우세해진다. 갱신은 언제나 분산을 줄이고 예측은 언제나 늘린다. 시간이 지나면 필터는 센서가 실제로 얼마나 좋은지를 반영하는 리듬에 자리 잡는다.
예시 값으로 계산해 보자. 예측 직후 필터는 로봇이 x⁻ = 10.0 m에 있고 P⁻ = 4.0 m²(표준편차 2.0 m)라고 믿는다. 측정값 z = 11.0 m, R = 1.0 m²(표준편차 1.0 m)가 들어온다. 그러면 K = 4.0 / (4.0 + 1.0) = 0.8이고, 새 추정은 10.0 + 0.8 × 1.0 = 10.8 m, P = 0.2 × 4.0 = 0.8 m²로 표준편차는 약 0.89 m다. 융합 결과는 어느 입력보다도 확실하며, 이것이 융합의 존재 이유다.
이 산술의 품질은 Q와 R이 얼마나 정직한지에 달려 있다. 도심 협곡의 GNSS 수신기에 R을 너무 작게 주면 필터는 다중 경로로 밀린 위치를 믿고 옆으로 튄다. 그래서 견고한 구현은 측정값에 게이트를 건다. 혁신값 z − x⁻를 기대 분산(P⁻ + R)에 비추어 보고, 통계적으로 있을 법하지 않은 측정은 버리거나 가중치를 낮춘다. 다차원에서도 행렬로 같은 구조가 성립하며, 확장 칼만 필터는 회전 운동 같은 비선형 모델을 현재 추정 주변에서 선형화한다.
position.source·position.accuracy로필터가 입력을 합친 이상 정직한 source 값은 fused다. 단일 출처 값은 원시 RTK 출력을 그대로 게시하는 로봇처럼 하나의 입력이 실제로 추정을 결정하는 경우에만 쓴다. 더 까다로운 것은 accuracy다. 1단계는 이를 "미터 단위 위치 정확도"라고만 설명하므로 1시그마 값인지, 95 % 반경인지, 다른 무엇인지가 열려 있다. 이들 사이의 차이는 크다.
| 정의 | 의미 | σ로 나타낸 반경 | σ = 0.2 m일 때 값(예시) |
|---|---|---|---|
| 축별 1σ | 각 좌표의 표준편차 | 1.00 σ | 0.20 m |
| CEP(50 %) | 측위 결과의 절반이 들어가는 반경 | ≈ 1.18 σ | ≈ 0.24 m |
| R95 | 측위 결과의 95 %가 들어가는 반경 | ≈ 2.45 σ | ≈ 0.49 m |
| 2DRMS | 반경 오차 제곱평균제곱근의 두 배 | ≈ 2.83 σ | ≈ 0.57 m |
이 예시는 요점을 구체적으로 보여 주려고 고른 것이다. 3단계 §8은 위치 오차 0.5 m 미만(95 %)을 성능 목표로 둔다. 필터가 축별 σ = 0.2 m를 보고하는 로봇은, 오차가 정말 가우시안이고 σ가 정직하다면, 이 목표를 겨우 충족한다. 한 공급사는 accuracy에 1σ를, 다른 공급사는 R95를 넣는다면 둘을 비교하는 플릿 대시보드는 더 보수적인 공급사를 더 나쁜 쪽으로 순위 매긴다. 스키마로는 이를 해결할 수 없으므로 구현체마다 사용하는 정의를 선언해야 하며, 적합성 검토는 선언이 빠진 것을 결함으로 다뤄야 한다. 배송 로봇에게 합리적인 선택은 3단계 목표와 맞아떨어지는 95 % 수평 반경이다. 무엇을 고르든 적합성 문서에 적고 플릿 전체에 일관되게 적용해야 한다.
실제 필터는 하나의 σ가 아니라 2×2 수평 공분산을 내며, 그 타원은 흔히 길쭉하다. 진행 방향 오차는 작고 횡방향 오차는 크거나, 그 반대다. 보수적으로 줄이는 방법은 큰 고유값의 제곱근을 σ로 삼은 뒤 선언한 정의로 환산하는 것이다. 한 방향으로는 오차를 과장하지만 결코 과소평가하지는 않는다. 하위 시스템이 안전 판단에 쓸 수도 있는 값이라면 이것이 옳은 쪽의 편향이다.
융합 추정기가 만든 위치 블록은 다음과 같을 수 있다(값은 예시이며, 전체 RobotState는 제3장에 있다).
{
"robot_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-03-14T09:21:07.250Z",
"version": "1.0.0",
"position": {
"latitude": 37.5662952,
"longitude": 126.9779451,
"altitude": 38.4,
"accuracy": 0.42,
"source": "fused"
},
"heading": 184.5,
"speed": 1.2,
"battery_level": 76
}
이 페이로드를 바르게 읽으려면 페이로드 밖에 있는 문서 세 가지가 필요하다. 정확도 정의(여기서는 예컨대 95 % 수평 반경), 고도 기준(정표고), 그리고 타임스탬프의 의미(메시지를 보낸 시각이 아니라 그 자세가 유효했던 시각)다. 마지막 항목은 2.7절에서 다시 다룬다.
위치 추정은 "나는 어디에 있는가"에 답한다. 인지는 "주변에 무엇이 있고, 어디로 가고 있는가"에도 답해야 한다. 3단계 §3.1은 그 파이프라인을 여섯 단계로 제시한다. 원시 데이터 전처리, 양식별 객체 검출, 센서 간 검출 연관, 칼만 필터 융합, 분류, 궤적 예측이다.
base_link 사이의 고정 변환)과 타임스탬프가 결정적이 된다. 연관은 흔히 거리 게이트를 둔 할당 문제로 푼다.3단계 §3.2는 장애물 레코드의 예를 제시한다. $id를 가진 1단계 스키마는 아니지만 표준이 장애물에 쓰는 형태이며, 인지 결과를 노출하는 구현체는 이를 따라야 한다.
{
"obstacle_id": "obs-001",
"class": "pedestrian",
"confidence": 0.95,
"position": {"x": 5.2, "y": -1.3, "z": 0.0},
"velocity": {"vx": 1.2, "vy": 0.3},
"bounding_box": {"length": 0.5, "width": 0.4, "height": 1.7},
"predicted_trajectory": [
{"t": 1.0, "x": 6.4, "y": -1.0},
{"t": 2.0, "x": 7.6, "y": -0.7}
]
}
| 필드 | 예시의 내용 | 표준에 없는 것: 선언해야 함 |
|---|---|---|
obstacle_id | 트랙 식별자 | 수명: 트랙이 살아 있는 동안 프레임 사이에서 안정적이며 전역 고유값은 아님. UUID가 아니므로 UUID로 검증하지 말 것. |
class | 의미 클래스 | 허용 어휘(예: pedestrian, wheelchair, stroller, cyclist, vehicle, static). 표준에 열거형이 없음. |
confidence | 0~1 점수 | 보정 여부(2.7절 참조), 그리고 존재에 대한 것인지 클래스에 대한 것인지. |
position | 미터 단위 x, y, z | 좌표계(base_link 또는 odom)와 기준점(상자 중심 또는 최근접점). |
velocity | m/s 단위 vx, vy | 좌표계, 그리고 절대 속도인지 로봇 기준 상대 속도인지. |
bounding_box | 미터 단위 길이·폭·높이 | 상자의 방향(예시에는 요가 없음). |
predicted_trajectory | t초 뒤의 점들 | t가 레코드 타임스탬프로부터의 오프셋이라는 점, 그리고 예측의 불확실성. |
예시로 해 볼 만한 일관성 검사가 있다. (5.2, −1.3)에서 속도 (1.2, 0.3) m/s를 1초 적용하면 (6.4, −1.0), 2초면 (7.6, −0.7)로, 예측점과 정확히 같다. 예시는 등속 예측을 쓰며, x·y는 속도가 위치와 같은 좌표계에서 표현되었다는 가정과 맞는다. 로봇 자신이 움직이고 좌표계가 base_link라면, 그 좌표계에서의 등속 예측은 속도가 로봇 기준 상대 속도일 때에만 옳다. 표가 확정하라고 요구하는 모호함이 바로 이것이다.
이름 충돌 하나는 특히 조심해야 한다. 1단계 카메라 검출의 bounding_box는 정수 x·y·width·height로 된 픽셀 사각형이다. 3단계 장애물의 bounding_box는 length·width·height로 된 미터 단위 3차원 크기다. 둘 다 width가 있지만 하나는 픽셀, 하나는 미터다. JSON 키가 같더라도 코드에서는 두 형식을 따로 두어야 한다.
다음 문제들은 로봇 텔레메트리에서 반복해서 나타난다. 어느 경우든 페이로드 형식은 올바르므로 스키마 검증으로는 막을 수 없다. 설계 규율과 시험만이 잡아낸다.
융합은 측정값을 그것이 측정된 시각에서 비교한다고 가정한다. 카메라 타임스탬프는 드라이버가 프레임을 받은 순간이고 LiDAR 타임스탬프는 스캔 시작 시점이라면, 둘은 수십 밀리초 차이가 날 수 있다. 영향은 속도에 비례한다. 예시로 합성 속도 2.5 m/s(1.2 m/s로 가는 로봇이 1.3 m/s로 다가오는 보행자와 마주하는 상황)에서 40 ms 어긋남은 보행자를 0.1 m 잘못 놓는다. 이는 3단계의 정적 장애물 여유 거리 0.3~0.5 m에서 상당한 비율이며, 센서 간 연관 오류를 부르기에 충분하다. 회전식 LiDAR에는 스캔 내부 어긋남도 있다. 10 Hz에서 한 바퀴는 100 ms가 걸리며, 1.5 m/s로 움직이는 로봇은 그동안 0.15 m를 간다.
완화책은 다음과 같다. 가능하면 하드웨어에서 수집 시점에 타임스탬프를 찍는다. 센서 시계를 로봇 시계에 동기화한다(로컬 네트워크에서는 이를 위해 설계된 PTP, 곧 IEEE 1588이 있다). 센서별 지연을 기록하고 보상한다. 오도메트리로 점군을 디스큐한다. 이와 별개로 RobotState timestamp의 의미를 정해야 한다. 1단계는 이를 "상태 캡처 시각"이라고 부르므로, 메시지를 직렬화한 시각이 아니라 게시한 자세가 유효했던 시각으로 설정한다. 로봇과 클라우드 사이의 플릿 수준 시계 동기화는 제6장에서 다룬다.
SensorReadings는 lidar[].points를 {x, y, z, intensity} 객체의 배열로 허용한다. 디버그 스냅숏에는 유용하지만 텔레메트리 스트림으로는 재앙이다. 예시로 계산하면, 초당 30만 점을 내는 센서를 점당 약 60~80바이트의 JSON 객체로 인코딩하면 부가 비용을 빼고도 센서 하나당 초당 약 20 MB가 나온다. 같은 점을 32비트 부동소수점 네 개로 담으면 점당 16바이트, 초당 약 4.8 MB로, 여전히 플릿이 나눠 쓰는 셀룰러 상향 링크에는 지나치게 많다. 1단계 §6.3이 이미 길을 제시한다. 페이로드를 최소화하고, 고빈도 텔레메트리에는 Protocol Buffers를 쓰고, 증분 갱신을 보내라는 것이다. 카메라 스키마는 의도된 패턴을 보여 준다. 프레임 자체가 아니라 detections와, 저장된 프레임을 가리키는 image_url을 보낸다. LiDAR에도 같은 생각을 적용하면 된다. 평소에는 장애물과 점유 요약을 게시하고, 원시 점군은 요청이 있거나 사고 전후에만 올린다.
스키마는 confidence를 0~1로 제한하므로 확률로 읽고 싶어진다. 그러나 대부분의 검출기 출력은 보정된 확률이 아니다. 신경망은 실제로는 95 %보다 훨씬 드물게 맞는 검출에 0.95를 낼 수 있고, 그 관계는 모델, 소프트웨어 버전, 조명 조건에 따라 달라진다. 두 가지 결과가 따른다. 첫째, 한 모델에 맞춘 임계값은 재학습한 모델로 옮겨 가지 않는다. 둘째, 공급사가 다른 신뢰도는 플릿 대시보드에서 비교하거나 평균할 수 없다. 신뢰도를 노출한다면 대표성 있는 검증 데이터로 보정하고(신뢰도 다이어그램과 온도 스케일링 같은 보정 단계가 표준적인 기법이다), 모델 버전을 기록하고, 값이 보정된 것인지 밝혀야 한다. 3단계 §8은 검출률과 오탐률 목표를 두는데, 이는 신뢰도 점수에서 추론하는 것이 아니라 시험에서 정답 데이터와 대조해 측정한다(제7장).
1단계는 단위를 섞어 쓴다. speed는 m/s, heading은 도, ultrasonic[].distance_cm는 센티미터이며, LiDAR 점 좌표에는 단위가 적혀 있지 않다. IMU 블록에는 단위 없는 acceleration x·y·z와, roll·pitch·yaw라는 필드를 가진 gyroscope 객체가 있다. 자이로스코프는 각속도를 재지만 필드 이름은 각도를 연상시키고 단위도 없다. 소비자는 "yaw: 0.2"가 0.2 rad의 자세인지, 0.2 rad/s의 각속도인지, 0.2 °/s인지 알 수 없다. 이는 구현 프로파일에서 해결해야 한다. 예를 들어 REP-103을 따라 SI 단위(가속도는 m/s², 각속도는 rad/s)를 쓰고 자이로스코프 필드는 차체 축에 대한 각속도를 담는다고 정할 수 있다. 무엇을 고르든 문서화하고, 정지한 로봇으로 시험해야 한다(중력을 포함하면 가속도계 z는 약 9.81 m/s², 중력을 뺐다면 약 0이어야 하며, 문서는 어느 쪽인지 밝혀야 한다).
2.1절에서 다룬 좌표계 관련 오류 세 가지가 현장 문제에서 유난히 큰 몫을 차지하므로 점검 항목으로 다시 적는다. 나침반 규약 대신 ROS 규약으로 게시한 방위각, 파이프라인 어딘가의 float32에서 잘린 위도·경도, 해수면 기준이라고 문서화해 놓고 타원체고로 게시한 고도다. 셋 모두 유효한 JSON을 만들고, 스키마 검증을 통과하며, 하위의 모든 소비자를 오도한다.
heading은 0 = 북쪽, 시계 방향, 도 단위이고, ROS 요는 0 = 동쪽, 반시계 방향, 라디안이다. heading_deg = (90 − yaw_deg) mod 360을 쓰고, [0, 360)으로 정규화하고, 네 방위를 시험한다.source: "fused"이며, accuracy에는 선언된 정의가 있어야 한다. 3단계 목표와 맞추려면 95 % 수평 반경이 바람직하다.제3장은 여기서 다룬 필드를 1단계 스키마 전체 안에 배치하고, 검증 코드와 구현자가 메워야 할 표준의 공백을 함께 살펴본다.
← 제1장: 라스트마일 배송 로봇과 WIA ROB-010의 자리 | 인덱스로 돌아가기 | 제3장: 1단계 데이터 형식 — RobotState·Package·Route 스키마 →