지도는 그림과 공간 해석의 계약이다
점유 격자(occupancy grid)는 공간을 일정한 크기의 셀로 나누고 각 셀의 점유 상태를 기록하는 지도 표현이다. 지도에서 하얀 영역이 보인다고 그곳을 로봇이 반드시 통과할 수 있는 것은 아니다. 센서가 무엇을 관측했는지, 셀 값이 무엇을 뜻하는지, 로봇 크기와 현재 환경이 어떤지 확인해야 한다. 이 단위는 셀을 지도 좌표에 놓고 파일로 옮기는 과정을 다룬다. 경로 탐색이나 실제 주행 성공은 별도 문제다.
그림 M15.02-1. 그림의 숫자는 width=4인 별도 인덱스 예시이며 점유 확률이 아니다. 지도 y는 위로 증가한다. 화면 이미지 행 방향과 지도 셀 j의 대응은 저장/변환 규칙으로 확인한다. 미상은 빈 공간과 다르다. 직접 제작.
Jazzy OccupancyGrid 정의는 2D 격자와 행 우선 배열을 제공하지만 값의 의미는 응용에 따라 달라질 수 있다고 명시한다. 여기서는 Nav2의 계약을 사용해 0은 빈 셀, 100은 점유 셀, −1은 미상으로 정한다. 중간 값의 의미와 처리도 생산자·소비자가 합의해야 한다. −1을 숫자가 작으니 가장 비어 있다고 해석하면 안 된다. 미상은 빈 공간이라는 관측 근거가 없다는 뜻이다.
행 우선 배열을 지도 좌표로 읽는다
합성 지도는 너비 W=3셀, 높이 H=2셀, 해상도 r=0.5 m/cell이다. 정수 i는 셀의 x방향 번호, j는 y방향 번호이며 둘 다 0부터 시작한다. 유효 범위는 0≤i<W, 0≤j<H다. 배열 위치 n=jW+i를 사용하므로 data=[0,100,−1,0,0,100]의 처음 세 값은 j=0 행이고 다음 세 값은 j=1 행이다. 셀 (1,0)은 n=1에서 100, 셀 (1,1)은 n=4에서 0이다. 유효 셀 수와 배열 길이는 W×H=6으로 일치해야 한다.
지도 원점은 셀 (0,0)의 중심이 아니라 그 셀의 왼쪽 아래 모서리 pose다. 원점 위치를 o=(o_x,o_y) m, 격자 축이 지도 축에 대해 반시계 회전한 각을 θ rad라고 하자. 열벡터를 사용하고 R(θ)는 평면 회전행렬이다. 셀 중심의 지도 좌표 p는 다음과 같다.
θ=0, o=(1,−1) m이면 셀 (1,0)의 중심까지 격자 안에서 이동한 거리는 (0.75,0.25) m다. 원점을 더해 p=(1.75,−0.75) m가 된다. 1/2을 빠뜨리면 (1.5,−1)이라는 셀 모서리를 계산하게 된다. 지도 크기는 격자 축에서 1.5 m×1.0 m이며 셀 개수와 미터를 혼동하지 않는다.
같은 원점에서 θ=π/2이면 (0.75,0.25)를 회전한 값은 (−0.25,0.75)다. 따라서 중심은 (0.75,−0.25) m다. 원점 좌표만 더하고 회전을 무시하면 다른 장소를 가리킨다. MapMetaData의 origin은 Pose이므로 메시지에서는 방향을 quaternion으로 표현한다. 여기서 θ는 평면 yaw를 풀어 쓴 기호이며 YAML origin의 세 번째 값도 yaw rad다.
반대로 지도 점에서 셀을 찾으려면 원점을 빼고 역회전 R(−θ)를 적용한 뒤 각 성분을 r로 나눠 바닥 함수로 내린다. 바닥 함수는 해당 값 이하의 가장 큰 정수다. θ=0에서 점 (1.6,−0.4) m는 원점 기준 (0.6,0.6) m이므로 (i,j)=(1,1), n=4에 들어간다. 셀 중심은 (1.75,−0.25) m로 원래 점과 같을 필요가 없다. 오른쪽 바깥 경계 x=2.5 m는 i=3이 되어 범위 밖이다. 범위 밖 값을 마지막 셀로 조용히 바꾸지 않는다.
이미지 위아래와 지도 아래위는 다르다
일반적인 지도 이미지의 첫 행은 화면 위쪽이다. 격자 배열의 첫 행은 j=0인 아래쪽이다. 위 합성 지도를 화면에 그리면 윗줄은 [0,0,100], 아랫줄은 [0,100,−1]이다. 실제 색은 저장 방식에 따라 달라지지만 공간 배치는 이 순서다. 이미지의 첫 행을 그대로 격자 첫 행에 넣으면 장애물을 상하 반전한 지도를 만들 수 있다.
Nav2 Jazzy map_io 구현은 이미지 로딩과 저장에서 이 세로 방향을 변환한다. 직접 배열을 만드는 코드와 파일 로더가 이미 수행하는 변환을 함께 쓰면 두 번 뒤집을 수도 있다. 어떤 단계가 행 방향을 바꾸는지 기록한다. 화면 모양만 비슷하다고 인덱스와 원점이 맞는 것으로 승인하지 않는다.
파일 두 개와 변환 규칙을 함께 보관한다
가상 파일 /maps/lab.yaml은 image: lab.pgm, resolution: 0.5, origin: [1.0,−1.0,0.0], mode: trinary, negate: 0, free_thresh: 0.196, occupied_thresh: 0.65를 가진다고 하자. 이미지의 크기는 3×2다. image는 YAML 파일 위치를 기준으로 찾는 상대 경로이므로 이 입력의 이미지 목적지는 /maps/lab.pgm이다. 현재 터미널 폴더를 기준으로 해석하지 않는다. 실제 환경에서는 파일 존재·접근과 로더 결과를 따로 확인해야 한다.
trinary는 빈 공간·점유·미상의 세 범주로 바꾸는 방식이다. 투명도가 없는 8비트 회색 이미지와 negate=0인 이 예에서 밝기 g의 점유 변환값은 1−g/255다. g=0은 1로 점유, g=254는 약 0.00392로 빈 공간, g=205는 약 0.196078로 두 임계값 사이여서 미상이 된다. 임계값을 임의로 0.25로 바꾸면 같은 205가 빈 공간으로 해석될 수 있다. 색이 같아도 메타데이터 변경으로 의미가 바뀌는 이유다.
원래 격자의 중간 확률이나 신뢰 정보가 세 범주로 합쳐지면 원래 값을 복원할 수 없다. 저장 성공을 모든 추정 상태의 무손실 백업으로 쓰지 않는다. 정적 점유 지도 이미지와 SLAM의 관측·제약·pose graph 상태도 서로 다른 자료다. 지도 파일 쌍의 버전·생성 조건, 해상도·원점·인코딩과 필요한 별도 상태를 기록한다. 저장기와 로더의 버전 및 선택 mode에 따라 세부 변환이 다르므로 여기의 숫자를 모든 지도 파일 규격으로 일반화하지 않는다.
저장 지도를 불러오는 것과 현재 위치를 아는 것은 다르다
Nav2 Map Server는 지도 파일을 로딩하고 제공하는 역할이다. 지도 파일이 로봇의 현재 pose를 자동으로 알려주지는 않는다. 위치 추정에는 관측과 초기 조건, 지도 좌표와 odom을 연결할 근거가 필요하다. 다른 건물의 지도를 이름만 맞춰 로딩하거나 가구가 바뀐 구역을 과거의 빈 공간으로 믿으면 파일 처리가 성공해도 과업은 실패할 수 있다.
재사용 점검은 파일 쌍의 동일성, 실제 해석된 크기·해상도·원점, 좌표 변환, 현재 센서와 지도 대응, 환경 변경, 위치 추정의 모호성 순으로 진행한다. 정적 파일 읽기만 했다면 그 범위로 보고하고 실제 토픽 제공·위치 추정·주행 검증을 분리한다. 기존 4.3 TF2 트리 설계로 좌표 연결을, 6.6 평가 설계로 조건별 성공과 실패 기록을 보충한다. 이 활동의 계산 통과는 실제 지도 재사용 성능의 승인이 아니다.