본문 바로가기

FOSCAR-(Autonomous Driving)/대학생 창작 모빌리티 경진대회

[LiDAR팀] 정혁제 #Lesson 4 - Working with Real PCD

반응형

1. Load Real PCD

lesson 3까지 시뮬레이션 point cloud를 분할하고, 클러스터링 하는 방법을 배웠다.

이제 앞서 배운 개념들을 자율주행 차량의 실제 point cloud data에 적용해보는 방법을 배워보자!!

일단 실차의 PCD(Point Cloud Data)를 불러오는게 첫번째 일이다.

시작해보자!!

 

2. Load PCD

먼저 차에 기록된 PCD파일들을 로드할려면, 앞서 만든 simpleHighway 함수에서 만든 것과 유사한 새로운 point processor 를 만들어야 한다. 이 함수들의 차이점은 무엇일까?

새로운 함수(cityBlock)는 pcl의 PointXYZI 타입을 사용한다는 것이다. 여기서 "I"는 다들 알다시피, intensity(강도)를 뜻한다. 

이제, environment.cpp를 들어가서  simpleHighway 와 동일한 레이아웃인 CityBlock 함수를 생성해보자.

(CityBlock의 인수는 pcl viewer에 대해 reference를 하는 simpleHighway함수와 동일하다.)

CityBlock 함수에서 PointXYZI 템플릿 인수를 사용해서 새 point processor를 만들자.

point processor를 사용해서 자동차의 point cloud중 하나를 로드한 다음, renderPointCloud함수를 통해 볼 수 있다.

!! main함수에서 simpleHighway 대신 cityBlock를 호출하는 거 잊지말기 !!

차의 모든 PCD파일들은 src/sensors/data/pcd/data_1/ 에 있으니 궁금하면 확인해보면 된다.

 

<위에서 이야기한 cityBlock function>

void cityBlock(pcl::visualization::PCLVisualizer::Ptr& viewer)

{

// ----------------------------------------------------

// -----Open 3D viewer and display City Block -----

// ----------------------------------------------------

 

ProcessPointClouds<pcl::PointXYZI>* pointProcessorI = new ProcessPointClouds<pcl::PointXYZI>();

pcl::PointCloud<pcl::PointXYZI>::Ptr inputCloud = pointProcessorI->loadPcd("../src/sensors/data/pcd/data_1/0000000000.pcd");

renderPointCloud(viewer,inputCloud,"inputCloud");

}

 

이제 cityBlock 함수를 직접 선언해주자.

그다음, main함수 안에서 simpleHighway를 주석처리하고, 방금 생성한 cityBlock를 추가해주자!

그리고 make를 해준 뒤, environment 환경을 실행시키면 아래와 같은 장면이 나올 것이다 !

위의 이미지는 renderpointcloud 함수 인수에 색상을 지정하지 않을 때, default값의 intensity 색상이 나온 결과값이다.

여러대의 차들과 건물들의 위치를 확인할 수 있다.

우리의 목표는 자동차와 트럭 등에 bounding box를 그려서 global path를 주행할 때 충돌을 피하는 것이다!!

 

3. Challenges with Real World Lidar

실제 환경에서 라이다를 사용할 때 발생하는 문제점들은 환경 조건에서 비롯된다고 할 수 있다.

- 폭우, 정전, 또는 남부 캘리포니아처럼 공기 중에 먼지가 많은 곳에서는 작동이 어렵다는 단점이 있다.

- 반사를 기반으로 한 유령 물체가 생기기도 한다.

(비가 엄청 내릴때, 다른차의 바퀴에서 튀어져 나오는 물방울들을 거대한 스프레이 형식의 물체로 인식할 때)

 

4.Downsampling

라이다에서 얻은 raw 데이터를 downsampling 하는 이유는, 내부 차량 네트워크를 통해 raw데이터 전체를 보내버리면 데이터 양이 너무나도 많아서 real-time 작동이 불가하기 때문이다.

그래서 실제로는 스테레오 카메라처럼 point cloud을 stixel로 변환시키는 작업을 한다.

(stixel :  장면의 특정 수직 슬라이스 내에서 가장 가까운 장애물에 가까운 수직 막대 형태로 이미지의 깊이 정보를 슈퍼픽셀로 표현한 것)

여기서 stixel은 성냥개비와 같다고 생각할 수 있다.

차량 뒷부분이 있다면, 픽셀은 성냥개비를 여러 개 붙여서 트렁크를 가리거나 차량을 덮을 수 있다고 가정해보자.

이렇게 할 때, 2가지를 얻을 수 있다.

첫번째는 성냥개비의 수 이다.

각각의 성냥개비가 4인치라면, 차량의 너비는 금방 계산이 가능할 것이다.

또한 성냥개비의 높이로, 차량의 높이도 알 수 있다.

위와 같은 작업으로, 라이다의 데이터를 다운샘플링한다.

 

5.Filtering with PCL

앞선 이미지의 화면을 통해, 우리는 생각보다 해상도가 높고, 꽤 먼 거리까지 포인트 클라우드가 생성된다는 점을 알 수 있다.

저 수많은 점들을 빠르게 처리할려면, cloud를 필터링 해야 하는데, 이 작업을 수행하는데 사용되는 방법은 2가지가 있다!

바로 voxel grid 와 ROI(Region of Interest) 이다.

 

1. Voxel Grid

PCD를 3차원 큐브 형태의 공간으로 자른 다음, 하나의 큐브에 하나의 포인트만 남기는 방식으로 진행된다.

큐브의 길이가 클수록 PCD의 해상도가 낮아진다는 특징이 있다.

 

2. Region of Interest

X,Y,Z 축을 바탕으로 하나의 박스를 생성하고, 나머지 부분은 모조리 날려버리는 형식이다.

이러한 방식을 적용할려면, point process 함수 FilterCloud를 입력해야 한다.

이 함수의 인수는 input cloud, voxel grid size, 관심 영역을 나타내는 min/max points 이다.

 

자 이제, 실습을 진행해보자.

PCD를 필터링해주는 함수를 적용할 때, filterCloud 함수를 사용하면 된다.

// Experiment with the ? values and find what works best

filterCloud = pointProcessorI->FilterCloud(inputCloud, ? , Eigen::Vector4f (?, ?, ?, 1), Eigen::Vector4f ( ?, ?, ?, 1));

renderPointCloud(viewer,filterCloud,"filterCloud");

 

이거 어캐 코딩하는데... 진짜 모르겠어...아시는분...


위의 필터링 처리가 된 클라우드 이미지(저는 결국 만들지 못했습니다...)에서, 해상도가 원본보다 훨씬 낮다는 것과(voxel grid), 지정된 상자크기 이외의 모든 것은 버리는 것(ROI)을 확인 할 수 있다.

Voxel size는 처리 속도를 높이는 데 도움이 될 정도로는 키워야 하지만, Object 정의가 완전히 손실되면 안되므로 적당한 크기로 설정해줘야 한다.

ROI 또한 마찬가지로, 차량앞쪽에 무엇이 있는지 중요하므로 +X 방향으로 많은 거리를 확보하는 것이 중요하다.

또한 측면의 경우 최소한 도로 폭은 커버하도록 설정해야 한다.

enviornment.cpp에서 카메라 각도를 설정하면 좋은 ROI를 설정하는데 도움이 된다.

마지막으로, 내 차의 지붕에 부딪치는 포인트들을 제거해주는게 중요 요소 중 하나이다.

(pcl cropbox를 사용해서 지붕 포인트 인덱스를 찾은 다음, 해당 인덱스를 pcl 추출 인덱스 객체에 넣으면 제거 가능하다!!)

 

6. Steps for Obstacle Detection

(7. 에서 한번에 코드 설명 예정)

이제 PCD가 필터링 되었으므로, 이전 lesson에서 한것과 동일하게, segmentation / clustering을 시작해보자!!\

앞서 lesson들에서 이미 다음과 같은 진행사항에 대한 함수들을 다 만들었다. 참고해서 똑같이 진행하면 끝이다!

 

step 1. Segment the filtered cloud into two parts, road and obstacles.

관심부분만 분류하는 단계이다. (녹색은 도로, 빨간색은 장애물)

보라색 부분은 차량의 지붕을 없앤 자국이다.

step 2. Cluster the obstacle cloud.

장애물을 클러스터링하는 단계이다. 물체별로 다른 색상을 적용해준다.

distance tolerance 를 늘려준다면, 가까이 있는 2개의 물체가 1개로 통합될수도 있다.

step 3. Find bounding boces for the clusters

마지막으로, 각각의 클러스터링 된 물체들에게 바운딩 박스를 만들어준다. 

마찬가지로, 앞서 만들어놓은 bounding box 함수를 이용해주자 !!

 

7. Stream PCD

전에는 하나의 PCD파일에서 사용할 수 있는 함수들과 그로 인한 코드들을 살펴보았다.

이제는 여러개의 PCD파일에서 동일한 처리를 할 수 있게 변형해주는 단게가 팔요하다.

environment.cpp 안의 cityBlock 함수를 변경해보자. 

이제, cityBlock 함수에 point processor를 전달하게 되는데, 이는 매 프레임마다 이 object를 다시 생성할 필요가 없기 때문이다.

또한 point cloud input은 프레임마다 달라지므로, 이제 input point cloud는 cityBlock의 인수가 되어버린다!

 

cityBlock new Funtion Signature

함수 헤더에서, 변수 정의의 끝에 const(변수를 상수취급) , &(call by reference)를 이용하여 input cloud를 상수취급 해줄수 있다.

이렇게까지 해야겠냐 생각하겠지만, 우리는 들어오는 point cloud값을 전혀 변경하지 않을 것이기 때문에 상관이 없다.

상수참조를 사용하면 해당 변수를 복사해서 메모리에 공간을 차지할 필요도 없기 때문에, 효율이 상승한다.

 

code inside main

streamPcd라는 point processor의 새로운 메서드를 사용하고 있다.

처리되는 모든 pcd파일이 포함된 폴더 디렉터리를 streamPcd에 전달하면, 모든 파일 이름의 시간순으로 정렬된 벡터를 스트림이라고 하는 이름으로 반환한다!

iterator(반복자)를 이용해서 스트림 벡터를 살펴볼 수도 있다.

 

PCL Viewer Update Loop

마지막으로, 우리는 pcl viewer 실행 주기를 알아야 한다.

위의 방법에가 가장 먼저 하는 것은 이전에 렌더링된 point cloud 또는 도형을 지우는 것이다.

그런 다음, point processor와 stream iteratior를 이용해서 point cloud를 load한다.

그런 다음, cityBlock함수를 호출하고, iterator를 업데이트 한다.

iterator가 vector의 끝에 도달할 때, 다시 처음으로 설정하면 끝!!

viewer->spinOnce () 호출은 프레임 속도를 제어하며, 기본적으로 1time step을 기다리므로 가능한 한 바르게 실행된다.

 

                                          --------------------------------------- 완성본 --------------------------------------------

Screencast from 2023년 04월 10일 20시 30분 52초.webm
2.83MB

(마우스 오른쪽 클릭 후 -> 새윈도우 창 열기 클릭하면 다운이 된다. 클릭하면 동영상이 열린다)

 

8. Lidar Obstacle Detection Project

(프로젝트는 볼 필요 없으니 넘어가자)

 

9. Tracking Discussion

드디어 전체 라이다 장애물 탐지 과정을 완료했다...

여러 개의 pcd파일을 streaming하여 1.filtering, 2.segmentation, 3.clustering, 4. bounding box detection 할 수 있게 되었다.

사실 지금까지 해온 알고리즘이 유일한 Object Detection 알고리즘이 아니다.

최근에도 무궁무진한 알고리즘들이 나오고 있고, 어떠한 공식을 적용해야 최적의 값과 최고의 효율이 나오는지는 차차 알아가보자.

 

10. Outro

여기까지 공부하고 따라와준 우리 LiDAR팀 고생 많았습니다!

부족한 점이 있을텐데 댓글로 피드백 달아주면 수정하면서 가겠습니다!

 

반응형