2023년 10월 30일에 MORAI가 주관하는 '서울 버추얼 자율주행 챌린지'에 참가하였다. (https://www.morai.ai/ko/svadc)
이때, MORAI에서 대회를 위해 시뮬레이션을 사용하게 해주었다.
우리는 기존에 LiDAR 처리를 python을 이용해서 하고 있었는데, 이 기회에 C++로 바꾸고 싶어서 아래 과정들을 진행했다.
실험 환경은 다음과 같다.
먼저 차량들을 아래와 같이 위치시킨다.
우리의 차량은 Ego-0이다.
LiDAR의 Raw data는 pointcloud 형태로 들어오게 된다. 크게는 6가지로 분류된다고 한다.
물체의 위치 정보인 x, y, z와 Intensity라고 하는 I를 많이 사용한다. Intensity는 '세기'라는 의미로 빛이 반사되어 돌아오는 강도에 따라 다른 값을 갖게된다. 물체의 위치와 색에 따라서 반사되는 세기가 다르기 때문에 이와 같은 값을 갖게 된다.
Velodyne Driver의 launch파일을 통해서 original raw data를 받아온다. (https://github.com/ros-drivers/velodyne, apt로 설치해도 된다.)
차량과 장애물 차량의 위치는 고정되어있다.
LiDAR에서 바로 들어오는 Raw data는 연산량이 엄청 많기 때문에 바로 사용할 수 없다. 때문에 일련의 과정들을 거쳐서 원하는 결과만 뽑아서 사용해야 한다.
마찬가지로 필요 이상으로 많은 data가 들어오고 이를 바로 사용한다면 연산 속도가 오래 걸린다.
이것을 해결하기 위해서 down sampling인 Voxel을 수행한다.
Voxel은 설정한 크기의 3D 큐브안에 하나의 PointCloud만 존재하게 해주는 작업이다.
위 사진과 같이 하나의 큐브 안에 여러 점이 있다면 한 점만 남기고 모두 없애는 방식이다. 단위는 meter이다. Voxel하려는 큐브의 크기가 크면 클수록 많은 점들이 사라지기 때문에 sparse한 데이터를 얻을 수 있다.
Voxel처리를 완료한 데이터중에서 필요한 부분만 볼 수 있도록 설정한다.
필요 이상으로 보게 되면 연산량이 많아져 속도가 느려지기 때문이다.
예를 들어 위에 사진에서 보면 터널 천장까지 나오는데, 이는 불필요한 data이다.
따라서 보고싶은 부분만 관심영역으로 설정해서 계산량을 확 줄일 수 있다.
ROI 이전 사진
ROI 이후 사진
ROI를 통해 멀리있는 벽면과 터널을 없앴다.
Outlier를 이용하여 Noise를 제거한다.
Outlier에는 statical outlier removal과 raius outlier removal가 있다.
statistical outlier removal
Pointcloud에서 nb_neighbors 단위로 그룹을 묶는다.
포인트를 기준으로 인접한 포인트들의 평균과 표준 편차를 구한다.
threshold 값을 설정하여 거리의 편차가 큰 포인트를 outlier로 정한다.
radius outlier removal
특정한 점을 중심으로 하는 구(sphere) 내부에 포인트의 갯수가 일정 갯수 이하만 있으면 outlier로 제거한다.
단위는 meter이고 radius의 크기가 커지면 계산량이 많아져서 느려진다.
Statical outlier removal를 이용하여 제거해봤다.
Outlier 제거 전 사진
Outlier 제거 후 사진
위에 ROI 사진과 비교하였을 때 멀리 떨어져있는 Noise를 효과적으로 제거할 수 있다.
대회에서는 outlier를 사용하지 않았다.
장애물에 대한 pointcloud가 적었기 때문이다.
장애물을 outlier로 인식하고 제거하는 현상이 발생해서 사용하기 적합하지 않았다.
RANSAC은 데이터를 랜덤하게 샘플링하여 사용하고자 하는 모델을 fitting한 다음 fitting 결과가 원하는 목표치 (합의점, Consensus)에 도달하였는 지 확인하는 과정이다.
이를 설정한 반복횟수만큼 진행하여 가장 많은 점을 가지고 있는 최적의 평면을 찾게 된다.
쉽게 말해 무작위로 평면을 뽑고, 그 중 설정한 threshold이내에 점이 가장 많은 평면을 구한다.
RANSAC 알고리즘을 이용하여 지면을 제거할 수 있다.
지면에 가장 많은 점들이 분포하기 때문에 지면에 해당하는 평면을 찾아 제거하면 된다.
가장 적합한 평면을 찾는 것이기 때문에 바닥인 평면은 outlier가 아닌 inlier이다.
가장 많은 점인 빨간 지면이 효과적으로 없어진 것을 확인할 수 있다.
위처럼 RANSAC을 진행해도 지면이 보이는 경우가 있다.
브레이크를 밟는 경우 차량이 앞으로 기울게 된다.
이때, LiDAR가 순간적으로 기울게 되고, 이는 좌표계의 위치변화를 얘기한다.
때문에 지면이 완벽하게 제거되지 않을 수 있다.
이는 if문을 통해 z값의 높이를 이용하여 제거할 수 있다.
여기까지 진행해서 나온 PointCloud들은 객체로서의 가치가 있는 정보이다.
DBSCAN을 이용해서 해당 점들을 하나의 군집(clustering)으로 만드는 과정을 진행한다.
많이 사용되는 K-MEANS와 비교시 클러스터 수를 지정하지 않아도 되는 장점이 있으며, 실제 데이터의 밀도에 따라서 클러스터링이 되기 때문에 군집이 겹치지 않는다. 또한 노이즈 개념때문에 이상치에 대응이 가능하다.
같은 색으로 같은 군집을 표현했다.
이처럼 같은 군집에 대해서 bounding box를 설정했다.
DBSCAN의 library는 mlpack을 사용했다.
DBSCAN은 mlpack c++ 설치후 사용 가능하다.
(https://www.mlpack.org/)
mlpack을 사용하면서 bounding box를 이용하는 부분은 없앴다.
원래는 상훈이가 직접 만든 python을 사용했다. 인터프리터 언어 특성상 느리다는 단점이 있어서 RANSAC을 제대로 진행하지 못했다. 기존 코드는 반복을 50회만 줘도 real time에 적용하기 어려웠다.
반면에 C++의 경우 1000번의 반복을 해도 real time에 적용할 수 있었다.
C++에 대한 강함을 다시 한 번 느낄 수 있었다. 때문에 LiDAR에 관한 코드는 C++을 많이 사용하는 것 같다. 실제로 스터디에서도 로봇 및 LiDAR를 하시는 분들은 C++과 ROS1을 사용하신다고 한다. 반면에 딥러닝이 필요한 computer vision은 python을 사용하는 것 같다. 이 두 가지를 다 할 수 있다면 엄청난 고오오급 인력이 될 수 있을 것 같다.
Camera가 아닌 LiDAR를 다뤄볼 수 있어서 너무 재밌었다. 참고로 우리 팀은 '모라이 혁신상'을 받았다. 완주에 의의를 두자!
보고 싶은 팀원들~ 각자 위치에서 더 나은 미래를 위해서 열심히 사는 중이다.
다음에는 LiDAR와 딥러닝에 대해서 작성할까 한다. 최근에 안 사실인데 LiDAR도 딥러닝이 가능하다는 것이다..,.. 3D object detection 분야로, VoxelNet과 PointNet, NetVLAD 등등.. 과연 이들도 C++로 되어있을까? 다음에 확인해보도록 하자~
https://gaussian37.github.io/autodrive-lidar-intro/
https://pcl.gitbook.io/tutorial/