Load Balancing

Load Balancing

MOTIE Project

프로젝트의 목표는 이기종 컴퓨팅으로 자원 공유를 통한 저가형 ADAS 구현이다. 블랙박스는 값이 저렴한 대신 한 가지 AI 모델만 구동해도 fps가 낮아진다. 이를 해결하기 위해 상대적으로 성능이 좋은 핸드폰에 연산을 분배하여 성능 개선을 도모한다.

Application

Android Studio로 블랙박스용 LBMaster 앱과 핸드폰용 LBServer 앱을 만들어 이기종 컴퓨팅을 구현하였다. Master에서 카메라로 입력된 이미지를 Server으로 보내면 Server에서 AI 연산 후 그 결과를 Master로 보내는 방식이다. 핸드폰과 달리 블랙박스에서는 ndk카메라가 작동하지 않아 bitmap과 rgb 변환 과정을 직접 구현하였는데 이는 fps의 성능을 저하시킨다.

LBMaster

MainActivity에서 조회 버튼을 눌러 Master에 연결된 Server의 IP 주소 목록을 확인한다. 카메라 버튼을 눌러 CameraPreview로 진입하면 실시간으로 영상을 입력받기 시작한다. CameraPreview에서 DETSEG 버튼을 누르면 Master 스스로 AI 연산을 수행한 후 결과를 화면에 표시한다(homogeneous computing). 연결된 Server가 있을 경우 SEND 버튼을 눌러 Server로 영상 송신 여부를 결정할 수 있다. 버튼 우측에는 Server와의 연결 여부를 알 수 있는 Device가 있다. Server에서 수신 버튼을 누를 경우 해당 Device가 on으로 변경되고 Server에서 처리한 AI 연산의 결과값을 수신받아 화면에 표시한다(heterogeneous computing). 영상 우측 상단의 FPS를 통해 연산 처리 속도를 확인할 수 있다.

LBServer

Master의 핫스팟을 켠 상태에서 Wi-Fi를 이용하여 Server를 Master에 연결시킨다. MainActivity에서 검색 버튼을 눌러 Master의 IP를 확인 할 수 있다. 현재는 Master IP인 192.168.43.1로 고정되어 있다. Option을 통해 수행하고자하는 AI를 선택할 수 있다. 한 Server당 하나의 Option만 가능하며 각각의 Server는 서로 다른 Option을 선택해야 한다. 수신 버튼을 누를 경우 CameraPreview로 진입하여 Master로부터 실시간 영상을 수신받아 선택한 Option에 해당하는 AI연산을 수행하여 결과를 화면에 표시하고 Master로 전송한다.

Network

Master는 실시간 영상을 각 포트를 통해 Server로 전송한다. 각 Server에서는 수신받은 영상에 detection이나 segmentation을 수행한 후 결과값을 Master의 각 포트로 전송한다.

Deep Learning

딥러닝 모델을 모바일 기기에서 실행하기 위해서는 모델을 학습시킨 후 경량화가 필요하다. 이 프로젝트에서는 ncnn을 이용하여 경량화 작업을 진행하였다. ncnn의 특징은 다음과 같다.

  • Tencent에서 개발한 경량화 및 최적화에 중점을 둔 딥러닝 프레임워크
  • GPU, Vulkan 등 다양한 하드웨어 가속기를 지원
  • C++ 사용
  • 풍부한 모델을 지원

yolop

하나의 모델에서 detection과 segmentation이 모두 가능하다는 점 때문에 선택하였다. 하지만 detection 클래스가 car 1개라는 단점이 있다.

데스크탑에서 학습을 진행하여 모델을 생성하였지만 ncnn으로 변환을 실패하여 기존에 나와있던 yolopncnn을 구현해보는 걸로 마무리 지었다. 하지만 뒤이은 모델들을 구현하며 yolop는 다중의 작업을 위한 역할 분배를 어떤 식으로 해야할지에 대한 프로토타입이 되었다. 갤럭시 s22에서 fps 5프레임 정도 나온다.

yolov5-seg

yolopncnn은 yolov5를 기반으로 만들어졌다. 프로젝트를 확인 결과 fps 13프레임에 segmentation 결과도 양호하여 진행을 결정하였다. yolop에서는 bdd100k의 라벨을 차선(lane line)과 차도(drive available) 2가지 클래스 인식만을 위해 이진 마스크로 변형하였다. bdd100k의 경우 lane line, crosswalk, curb가 있는데 yolop에서는 이것을 lane line으로 통합한 후 직선 형태로 바꾸었다.

binary maskcontour

이 데이터셋을 yolov5 학습을 위해 이진 마스크에 cv2.findContours를 사용하여 윤곽선을 구한 후 yolo format으로 저장하였다.

det8det16det32

하지만 yolov5 학습 후 모델을 ncnn으로 변환하는 과정에서 기존 코드와의 호환이 맞지 않아 어려움이 있었다. ncnn에서는 yolov5의 출력부 연산을 지원하지 않아 출력 부분을 잘라서 코드로 구현한다. 학습한 모델을 ncnn 변환 시 잘리는 부분의 이름이 달라 수정하였지만 연산자가 달라져서 구동에 실패하였다.

yolov8-seg

커스텀 학습 모델을 ncnn에 맞게 export하여 android에서 구동을 지원한다. yolov5-seg와 동일한 yolo format을 사용하여 손쉽게 학습을 진행하였다.

yolov8-seg는 객체별로 분할 작업을 수행하는 instance segmentation이다. Bbox를 먼저 검출한 후 해당 부분에 대한 segmentation을 한다. Bbox를 출력에서 삭제하고 차선과 차도만 보이게 변경하였다.

통신에 있어서 객체 갯수만큼 생성된 마스크를 전송하는 것은 비효율적이다. 따라서 객체의 클래스을 픽셀값으로 설정하여 모든 객체를 하나의 마스크로 통합하였다. 위 마스크는 시각화를 위해 모든 객체의 픽셀값을 255로 설정하였다.

갤럭시 s22에서 fps 23프레임 정도 나온다.

yolov5

ncnn-android-yolov5를 실시간 추론용으로 변경 후 android에서 확인 결과 fps가 5프레임이 나와 nanodet을 이용하기로 결정하였다.

nanodet

ncnn-android-nanodet에 fps가 30프레임으로 나와있다.

결과값 전송을 위해 Bbox를 cls prob x y w h/ 형태로 연장시켜 string 형태로 변경하였다. 위 출력값은 각 프레임 당 detection 결과로 출력되는 Bbox 로그이다.

Optimization

big.LITTE solution

clustercorecpu
big1ARM Cortex-X2
middle3ARM Cortex-A710
litte4ARM Cortex-A510

갤럭시 s22의 경우 big.LITTLE 솔루션을 적용한 DynamIQ 방식 HMP 모드 지원 옥타코어 CPU를 사용한다. ncnn은 powersave 옵션을 통해 원하는 cluster를 바인딩시켜서 성능을 극대화시킬 수 있다.

Protobuf

Google에서 개발한 데이터 직렬화 형식으로 구조화된 데이터를 이진 형식으로 효율적이고 컴팩트하게 저장하여 네트워크 연결을 통해 더 빠르게 전송할 수 있다.

네트워크의 통신 속도를 높이고자 적용을 하였지만 크게 차이는 없었다. 대신 구조화된 데이터 처리가 가능해지면서 통신 여부를 확인하기 위한 flag를 함께 사용하였다. 통신 중에는 결과값과 true flag를 사용한다. 통신이 멈출 때 false flag를 사용하여 종료를 알린다.

Thread

프로파일러를 통해 thread를 확인하고 필요없는 thread를 중단시키거나 재활용하여 앱의 가동성을 높였다.

thread interrupt

thread 내부에서 while문을 사용하면서 반복 중단을 위한 처리가 어려운 경우 외부에서 thread에 interrupt를 발생시켜 반복을 중단시킬 수 있다. 프로젝트에서는 카메라 구동 중 뒤로가기를 눌렀을 때 interrupt를 발생시켜 데이터 수신이 중단되도록 하였다.

thread pool

Master에서는 연결된 Server가 있을 경우 실시간으로 이미지를 전송시킨다. 이 과정에서 새로운 thread를 지속적으로 생성하여 thread 수가 무의미하게 많아지는 것을 확인하였다.

thread pool을 이용하여 thread를 5개로 설정하고 이것들이 지속적으로 재활용되도록 하였다. 하지만 MainActivity로 돌아올 경우 thread pool이 중단되지 않아 다시 CameraPreview를 실행할 경우 thread pool이 쌓이는 것을 확인하였다.

CameraPreview로 돌아갈 경우 즉 TextureView의 SurfaceTexture가 파괴되어 onSurfaceTextureDestroyed() 메서드가 호출될 때 thread pool을 shutdown시켜 작업을 중단시켰다.

thread reuse

CameraPreview로 진입할 때, DET SEG 버튼을 누를 때 모두 화면 업데이트를 위한 새로운 클래스를 생성하여 thread pool이 계속 생기는 것을 확인하였다. 상단 분홍색 점이 버튼을 누를 때이고 그때마다 새로운 thread pool이 생성되는 것을 확인할 수 있다.

생성된 클래스를 재사용하고 변수만 업데이트되도록 변경하였다. 버튼 클릭 시 thread pool이 재활용되는 것을 확인할 수 있다.

Memory

CameraPreview에서 Server의 결과값을 수신받을 때 메모리가 급격히 증가하는 것을 확인하였다.

첫 번째 CameraPreview 실행 후 50초~1분 30초까지 결과값을 수신받을 동안 256MB로 유지되던 메모리가 대략 800MB까지 치솟는다. 하단에 Garbage Collection이 동작하고 있지만 소용이 없어보인다. 두 번째 CameraPreview 실행에서는 결과값을 수신받지 않아 메모리가 유지된다. 하지만 3분 30초에 세 번째 CameraPreview를 실행하며 다시 결과값을 수신받으니 메모리가 치솟는다. 1.2GB 이후 메모리 초과로 앱이 강제 종료되어 그 전에 수신을 멈추고 기록을 종료하였다.

ADAS

Lane Departure Warning

차선 이탈 방지 시스템

차선 이탈을 감지하기 위해 가로선을 그은 후 이것이 차도에서 얼마나 벗어났는지 파악하였다.

차선을 이탈하지 않은 상태에서는 가로선이 차도를 벗어난 부분이 차선 양끝에 걸쳐져 있다. 차선을 이탈하면 가로선이 한쪽 방향으로 차도를 벗어나기 시작한다. 세그멘테이션의 인식력이 완벽하지 않기에 차도 클래스가 아닌 부분은 모두 차도를 벗어난 부분으로 처리하였다.

  • 좌우 차도가 인식되지 않는 경우
    차선과 인식되지 않은 차도가 차도를 벗어난 부분
    차도와 차도가 아닌 부분의 비율이 0.45를 넘을 경우 경고를 출력
  • 좌우 차도가 인식되는 경우
    차선만 차도를 벗어난 부분
    차도를 벗어난 부분이 가로선의 \(\displaystyle\frac{1}{5}\)과 \(\displaystyle\frac{4}{5}\) 사이인 경우에만 경고를 출력

Forward Collision Warning

전방 충돌 방지 시스템

차량 간의 거리를 측정하는 방법에는 Bbox의 크기를 이용하는 방법이 있을 것이다. 하지만 Bbox의 크기만으로는 좌우측 차량에 대한 거리 측정이 어려울 수 있다. 따라서 Bird’s Eye View로 관점을 변화시키고 거리를 측정하였다.

변환 행렬인 extrinsic matrix는 원근법에 의해 중심이 좁아지는 차선을 사다리꼴로 설정 후 warpPerspective로 직선화되는 값을 구하였다.

관점 변화 후 인식된 차량의 왜곡이 심하므로 Bbox의 (\(\displaystyle\frac{x_{min}+x_{max}}{2}, \displaystyle\frac{y_{min}+4y_{max}}{5}\))에 해당하는 부분을 차량의 위치로 설정 후 perspectiveTransform으로 점 변환을 시켰다.

현재 위치(\(\displaystyle\frac{w}{2}, h\))인 흰 점을 기준으로 인식된 차량 위치인 녹색 점과의 유클리디안 거리를 녹색 점 옆에 출력하였다. FCW를 위한 기준선인 부채꼴은 \(r\)이 250, \(\theta\)가 +-60이다. 실제 거리와 BEV상의 거리는 차이가 있기에 \(r\)은 상대거리로 봐야한다.


Modified by Sungbin Shim