지금까지는 로봇의 이동에 대해서만 다루었기 때문에 물체를 집을 좌표를 고정해 두었다. 따라서, 집을 대상의 위치가 바뀌면 로봇은 대상물을 집을 수 없다. 그래서 카메라로 집을 대상물을 찾아서 그 좌표를 알아내야 한다. 현재로는 보드에 아무런 표시가 없는 단순한 직육면체이어서 카메라가 물체의 방향을 알아낼 수 없다. 또한 대상물 자체가 여러 개 있는 경우라면 어느 것이 로봇이 잡아야 할 대상물인지 구분할 수가 없다. 그래서 물체에 위치와 방향을 구분할 수 있는 마커를 붙이도록 한다.

마커 생성 스크립트, 모델 파일, 런치 변경은 커밋 3a0b49d에 있다. 여기서는 마커 종류의 결정 과정과 적용 시 발생한 문제에 대해서 쓴다.

1. 마커 종류 선택

후보는 ArUco, 피두셜 마크, 돔보(트림마크), 로고의 네 가지였다. 돔보나 로고는 검출기부터 만들어야 하는데, 그 작업은 지금 확인하려는 것(카메라 좌표를 로봇 좌표로 옮기는 경로)과는 다른 작업이기 때문에 제외하였다. 실제 PCB에 사용하는 피두셜 마크의 경우 보드의 방향을 알려면 최소 두 개의 마크를 찾아야 하고, 마크가 작아서 카메라가 보드에 가까이 다가가야 한다. 그러려면, 카메라를 로봇팔 끝에 붙여야 하는데 지금 당장 하기에는 난이도가 있는 일이어서 제외하였다. 결과적으로 ArUco 마커를 선택하였다. 검출기가 OpenCV에 들어 있어 따로 만들 필요가 없고, 마커 하나에서 위치와 자세가 함께 나와서 방향 판별이 쉽다.

ArUco 마크의 이진 패턴은 사전이라고 부르는데 격자 크기와 마커 개수에 따라 종류가 달라진다. 이 사전들 중 가장 크기가 작은 DICT_4X4_50를 선택하였다. 격자가 작을수록 같은 물리 크기에서 칸 하나가 커져 멀리서도 검출이 잘 된다. 컨테이너의 OpenCV 버전은 4.6이라 4.7부터 바뀐 API(drawMarker → generateImageMarker등)의 예제를 그대로 쓰면 동작하지 않는다.

2. 검출을 위한 흰 테두리 추가

ArUco 검출기는 검은 테두리와 배경의 경계를 찾아 사각형을 인식한다. 마커 주위에 흰색 테두리(quiet zone)가 없으면 경계를 판단할 수 없다.

4×4 데이터 격자에 검은 테두리를 한 칸 두르면 마커 본체는 6×6칸이다. 그 바깥에 흰 여백을 한 칸 둘러 이미지 전체를 8×8칸으로 만들었다. 이미지는 파이썬 스크립트로 생성하였고 한 칸을 60px로 하여 본체 360px, 전체 480×480px이 되었다.

3. 치수 결정

검출이 잘 되려면 마커가 커야하고, 그러려면 판이 커야 하고, 판이 크려면 보드가 넓어져야 하는데, 보드는 그리퍼 개방폭 안에 들어가야 한다. 기존 시뮬레이션에서 사용하던 보드 크기는 40 x 20mm이어서 마커를 넣기에는 너무 작은 크기라 보드를 키우기로 하였고 그리퍼의 최대 개방폭인 50mm 안에 들어가면서 약간 여유가 있을 수 있도록 60 x 40mm로 변경하였다.

3.1 마커 크기 결정

판은 보드 짧은 변 40mm 안에 들어가도록 36mm로 하였고, 8칸 중 6칸이 마커 본체이므로 36 × 6/8 = 27mm가 된다.

OpenCV의 마커 검출기는 마커가 비스듬히 찍힌 경우 마커의 네 군데 모서리를 찾아낸 뒤 이를 평평한 정사각형 이미지로 잘라낸다. 이때 마커의 1칸(cell)을 몇 픽셀로 샘플링해서 비트를 만들 것인지를 지정하는 perspectiveRemovePixelPerCell 변수가 있다. 이 변수의 기본값은 4, 즉 4 픽셀이다. 즉 마커의 한 셀당 최소 4픽셀은 되어야 한다. arucogrid.com의 가이드에서는 이 값을 선명하고 조명이 좋으며 정면에서 본 경우의 최소값으로 보고, 실전에서는 그 3배를 목표로 하라고 권한다. 시뮬레이션 카메라는 흐림이나 흔들림이 없고 마커를 정면에서 내려다보므로, 최소값의 약 2배인 한 칸 7픽셀을 기준으로 잡았다.

처음에는 카메라 높이를 1m, 카메라 화각이 60도, 해상도를 1280x720으로 가정하고 시작하였다. 이 조건에서 화면의 한 픽셀에 대한 바닥면에서의 실제 길이를 계산하면 다음과 같다.

시야 폭   = 2 × 1m × tan(60°/2) = 2 × 0.577 = 1.155m
픽셀 크기 = 1.155m / 1280 = 0.90mm

마커 한 칸을 7픽셀로 잡았으므로 한 칸이 7 × 0.90 = 6.3mm, 마커가 6칸이므로 약 38mm이다. 하지만, 흰색 여백까지 포함하면 8칸이 되면서 50.4mm가 된다. 마커는 보드 위에 올라가는 형태이므로 기존의 보드 크기에는 마커가 다 올라갈 수 없게 된다. 게다가 그리퍼의 최대 간격보다도 마커의 크기가 크다.

이 문제를 해결하기 위해서는 해상도를 1920x1080으로 높여서 마커의 크기를 줄일 수도 있는데 연산 부하가 커지는 단점이 있기 때문에 카메라의 화각을 줄이는 방향으로 진행하였다.

아래는 카메라 해상도와 화각별 한 픽셀의 크기를 계산한 표이다.

해상도수평 화각시야 폭 (2·tan(화각/2))픽셀 크기27mm 마커한 칸
1280×72060°1.155m0.90mm30px5.0px
1280×72040°0.728m0.57mm47px7.9px
1920×108060°1.155m0.60mm45px7.5px

화각을 40°로 좁혀 해상도를 높이지 않고 27mm 마커의 한 칸이 7.9픽셀이 되도록 결정하였다.

4. 텍스처를 이름으로 찾게 하기

SDF 파일에서 텍스처 이미지를 지정할 때에는 아래와 같은 방식으로 지정한다.

<albedo_map>model://board/materials/textures/aruco_4x4_id0.png</albedo_map>

여기서 model://board/materials/textures/aruco_4x4_id0.png에 보이는 것처럼 파일이 위치한 경로가 아니라 모델 이름으로 찾는다.
Gazebo는 GZ_SIM_RESOURCE_PATH에 등록된 디렉토리를 뒤져서 model.config가 있는 디렉토리를 모델로 보고, 그중 이름이 board인 것을 고른다. 공유 라이브러리를 LD_LIBRARY_PATH 에서 찾는 것과 같은 구조다.

기존의 작업 디렉토리 구조는 models/ 아래에 board.sdf 파일 하나만 있고 model.config 파일이 없어서 가제보에서 모델 파일로 인식되지 않았고, GZ_SIM_RESOURCE_PATH에 아무것도 등록하지 않아 모델 파일을 검색할 디렉토리도 없었다. 이전에는 런치에서 -file로 파일 경로를 직접 넘겨 주었기 때문에 문제가 없었다.

이제 보드 위에 텍스처를 입히기 위해 디렉토리 구조를 변경해서 src/arm_bringup/models/board/ 디렉토리 아래에 model.config, model.sdf, 텍스처를 모두 모으고, 런치 파일에 GZ_SIM_RESOURCE_PATH를 설정했다.

src/arm_bringup/models/
└── board/                      ← 모델 디렉토리
    ├── model.config            ← 신규. 이게 있어야 모델로 인식
    ├── model.sdf               ← 기존의 board.sdf 
    └── materials/textures/
        └── aruco_4x4_id0.png      

5. 마커가 검게 보임

환경 설정을 마무리하고 시뮬레이션을 실행하니 마커가 정상적으로 보이지 않고 검은색으로만 보이는 현상이 발생하였다. 결론을 먼저 말하자면 마커를 붙여둔 판의 재질이 원인이었다.

<pbr>
  <metal>
    <albedo_map>model://board/materials/textures/aruco_4x4_id0.png</albedo_map>
  </metal>
</pbr>

metalness를 지정하지 않은 것이 문제였다. 지정하지 않으면 기본값이 적용되는데, 그 값이 0이 아니다. 시스템에 설치되어 있는 SDF 명세 파일을 찾아보면 아래와 같이 적혀있다.

/opt/ros/jazzy/opt/sdformat_vendor/share/sdformat14/1.9/material.sdf : 83행
<element name="metalness" type="string" default="0.5" required="0">

기본값이 0.5, 즉 절반은 금속인 재질이 된다. 비금속은 빛을 표면에서 사방으로 흩뿌리므로 광원 하나만 있어도 색이 보이지만, 금속은 확산 반사를 거의 하지 않고 주변 환경을 거울처럼 비추는 것으로만 보인다. 지금 월드에는 태양광 하나뿐인 환경이어서 금속 성분만큼 어둡게 나온다.

metalness를 0으로 두면 확산 반사가 살아나 텍스처가 보인다. roughness도 기본값이 0.5인데 표면이 너무 매끈하면 조명 각도에 따라 흰 부분이 번들거려 검출이 불안정해질 수 있어서 값을 높였다.

<metalness>0.0</metalness>
<roughness>0.9</roughness>
profile
무선/임베디드 엔지니어의 ROS2 & AI 개척기

0개의 댓글