프로젝트/졸업 프로젝트

Depth Estimation을 적용하여 자율주행 드론 구현하기

전공생 2022. 5. 17. 20:44

작년 가을부터 진행한 끝나지 않을 것만 같던 졸업프로젝트의 마지막을 달리고 있다. 시원하면서도 섭섭하다.

원래의 졸업프로젝트 주제였던 "산불 탐지 자율 주행 드론"에서 도메인을 약간 변경하여 “조난자 수색 자율 주행 드론" 프로젝트를 진행하였고, 필자는 프로젝트에서 드론의 자율주행 및 장애물 회피 코드 구현을 맡았다. 만약 졸업프로젝트의 작년 겨울 글을 보고 픽스호크와 ROS를 사용하여 조립한 드론을 어떻게 구현했는지 궁금했던 사람에게는(그런 사람이 있는지는 모르겠지만) 안타까운 소식이 있다.

하드웨어 및 구현 방법 변경

원래는 nvidia 젯슨 나노 혹은 라즈베리파이를 사용하여 ros을 사용하여 자율주행 드론을 구현하려고 했는데 다음의 이유

  • 드론을 직접 조립해야하는 문제 → 하드웨어에 대한 지식이 필요하고 완성했다 하더라도 하드웨어에서 결함이 생길 가능성이 높아서 소프트웨어 검증의 목적에서 벗어난다.(우리는 소프트웨어 개발이 목적이지, 하드웨어 개발이 목적이 아니다)
  • 기존에 컴퓨팅 보드로 사용하려고 했던 젯슨 나노가 동작을 안함 → 컴퓨팅 보드(스테레오 카메라를 연결하기 위해 두 개의 카메라를 연결할 수 있는 포트가 있는 젯슨 나노(b01이상의 버전) 혹은 라즈베리파이(컴퓨팅보드+메인보드);30만원 정도)를 추가로 구매해야하는 비용 문제가 발생한다.

때문에 팀이 구현한 소프트웨어(자율주행+사물인식)를 검증만 잘할 수 있는 프로토타입용으로 기본적인 기능만 있는 작은 완제품 드론을 사용하기로 하였다.

dji사의 tello edu라는 드론으로, 코딩용 드론으로 홍보를 하고 있다. dji사에서 드론을 제어하는 간단한 명령어를 사용할 수 있도록 파이썬 라이브러리(sdk)를 제공하기 때문에 코딩하기 편하고, 드론 프로그래밍 경진대회에서 제공하는 드론으로 자주 사용되는 것 같다.

이렇게 생긴 87g짜리 작은 드론이다. 매우 가벼워서 자격증이 필요한 드론으로도 취급을 안해준다. 최소한의 기능을 가진 프로그래밍이 가능한 드론으로 보면 될 것 같다.

이 드론은 비행시간이 13분이고, 가벼워서 실외에서 사용하기 힘들고, 영상 화질도 720픽셀로 요즘에는 만족스럽지 못한 화질과 드론이 컴퓨터로 와이파이 연결로 보내는 스트리밍 영상이 가끔씩 끊기는 현상이 있어서, 정밀한 기능을 테스팅하기에는 적합하지 않아보인다. 그래서 연구용으로 드론을 사용하는 경우엔 드론을 직접 조립을 하는 경우가 많은 것 같다.

조립이 아닌 위의 드론을 사용함으로써 변경되는 부분은 다음과 같다.

  • 픽스호크 사용X, 가제보/rviz와 같은 시뮬레이션 사용X
  • 드론 제어 파이썬 라이브러리 제공 → ROS를 사용할 필요가 없어졌다. ROS 공부는 나중에 기회가 되면
  • 카메라 하나
  • Gps 없음
  • 드론 제어 코드는 드론 자체에서가 아니라 드론과 와이파이로 연결된 컴퓨터에서 실행됨

드론을 직접 조립할 때에는 장애물 회피 기능을 위하여 카메라가 두 개 달린 스테레오 카메라를 사용할 예정이었고, 드론의 위치를 정확하게 파악하기 위해서 gps를 사용할 예정이었으나,

사용하기로한 드론은 단 하나의 카메라만 가지고 있고, gps가 내장되어있지 않아서 장애물 회피 기능을 구현하는 것과 드론의 위치를 파악할 방법을 다시 고안해야 했다.

 

경로 구현

djitellopy가 제공하는 드론의 정보 및 제공하는 드론 제어

pip install djitellopy

로부터 드론을 제어하는 함수들의 라이브러리를 사용할 수 있는데 사용할 수 있는 드론 제어 기능은

  • 이륙 takeoff()
  • 착륙 land()
  • x cm만큼 앞/뒤/위/아래/좌/우 방향으로 이동 move(direction, x)
  • x°만큼 시계/반시계 방향으로 회전 rotate_clockwise(x)(rotate_counter_clockwise(x))
  • x cm/s의 속도로 위아래/좌우/앞뒤/특정각도 방향으로 이동 send_rc_control(left_right_velocity, forward_backward_velocity, up_down_velocity, yaw_velocity)
  • flip 기능 flip(direction)

이 외에도 이름 끝에 mid라고 붙는 함수들이 있는데 이는 미션 패드라는 드론과 신호를 주고 받는 마우스 패드같이 생긴 패드를 사용하여 드론을 제어하는 함수이다. 미션패드는 유용하긴 하나, 드론이 미션패드로부터 멀리 떨어진 경우에는 해당 함수들이 제대로 동작하지 않아서 사용하지 않았다. 이외에도 링크를 통해 제공되는 더 많은 함수를 볼 수 있다.

드론의 주행 경로를 구현할 때 이동 거리로 드론 제어를 하는 move 함수보다는 주행 방향 및 속도로 제어하는 send_rc_control 함수를 사용하는게 좋다. 사용 방법은 말그대로 특정 방향의 velocity값을 통해 드론을 제어한다.

send_rc_control(10, 0, 0, 0)를 실행하면 왼쪽으로 1초에 10cm씩 이동하고,

send_rc_control(0, 10, 0, 0)을 실행하면 1초에 10cm씩 전진한다. 같은 속도로 후진하게 하려면 send_rc_control(0, -10, 0, 0)을 실행시키면 된다.

함수 argument중에 yaw_velocity라는 값이 있는데 여기서 yaw는 드론의 정면으로부터 틀어지는 각도를 말한다. 예를 들어 send_rc_control(0, 0, 0, 30)을 실행시키면 사람으로치면 앞을 보다가 옆사람을 보기 위해 고개를 시계방향으로 1초에 30도씩, 3초 뒤에는 90도 돌려 오른쪽 어깨 방향을 보게되는 것과 같은 행동을 한다. 음수값을 넣으면 반시계 방향으로 주행각도를 튼다.

 

또한 드론이 수집 및 출력할 수 있는 정보는 다음과 같다.

  • 속도, 가속도
  • 주행 처음 때보다 틀어진 각도(yaw)
  • 주행시간
  • 고도
  • 배터리 잔량
  • 드론의 카메라가 읽은 이미지 정보
  • 드론의 최고/최저/평균 온도
  • 미션패드와의 거리

드론의 주행 가능 시간이 짧기 때문에 배터리 잔량에 대한 정보는 매 주행마다 출력하여 확인하는 것이 좋다. 배터리가 특정값보다 부족하게 되면 제어하는 코드가 실행되지 않는다. 프로젝트에서 드론의 영상 이미지를 통해 사람과 장애물을 인식하기 때문에 이미지 관련된 값을 주요하게 사용하였다. 드론의 카메라로부터 실시간으로 영상을 받아오려면 다음의 코드를 실행시키면 된다.

import djitellopy as tello
import cv2

me = tello.Tello()
me.connect()
me.streamon()
while True:
    img = me.get_frame_read().frame
    cv2.imshow("streaming", img)
    cv2.waitKey(1)

 

Depth estimation

Monocular Depth Estimation은 하나의 RGB 이미지로 depth를 추정하는 기술이다. 3D모델링, 로보틱스, 자율주행 등에 사용된다.

하나의 RGB이미지와 그에 맞는 depth 이미지 쌍을 학습하여 추정하는 supervised learning방법으로 학습된 모델과, depth image 필요없이 스테레오 이미지의 왼쪽 RGB이미지와 오른쪽 RGB이미지를 비교하여 depth를 추정하는 unsupervised learning방법이 있는데, supervised 방법이 주로 사용되고 있다.

모델 검증

우리는 드론에 가까이에 있는 장애물을 회피해야하기 때문에 가까이 있는 물체와 나머지 물체사이의 depth 차이가 극명하게 나도록 정확도를 가지는 모델을 선택해야했다. 성능을 확인하기 위해 실행시켜본 모델들은 다음과 같다.

MonoDepth

monocular depth estimation 모델 중에서는 가장 많이 사용되는 모델이다. 때문에 처음에는 이 모델을 사용하려고 했지만 정확도가 너무 낮아서 사용하지 못했다. 

맨 왼쪽은 21cm, 가운데는 42cm 만큼 텀블러가 카메라로부터 떨어져 있는 경우이고 맨 오른쪽은 텀블러가 카메라로부터 42cm 떨어져 있고, 왼쪽으로 10.5cm 떨어져있는 경우의 사진

카메라로부터 21cm정도 떨어진 텀블러에 대해서는 배경보다 확연히 구분되는 depth값을 계산하였지만, 42cm 만큼 떨어져 있는 텀블러에 대해서는 텀블러 뒷배경과 비슷한 정도의 depth값을 가진다고 추정하였다. 적어도 30-40cm 정도 떨어진 물체에 대해 드론이 장애물이라고 인식을 해야하는데, 이 모델을 사용하면 가까운 물체도 장애물로 인식 못할 가능성이 높기 때문에 사용하기에 적합하지 않았다. 

GLPDepth

정확도가 높은 모델을 사용하기 위하여 당시(작년 겨울쯤) 기준으로 SOTA 모델들 중 깃허브 star개수가 가장 많았던 모델인 GLPDepth 모델을 사용해보려 시도해봤지만 모델이 너무 커서 실행할수가 없었다.

gpu memory가 33GiB나 필요하다고 한다.. 금붕어 어항에 흰수염고래를 넣으라는 격이다.

BTS

잘못적은게 아니라 모델 이름이 진짜로 BTS였다. 반갑게도 한양대 분들이 연구하였다. GLPDepth 다음으로 정확도가 높은 모델이었던 만큼 실제로 실행해봤을때 정확도가 높았다. 그러나 depth image를 계산해내는데 10초이상의 지연이 걸렸다. 지연 시간이 크면 장애물과 부딪히고 한참 뒤에 장애물을 회피하게 될 것이다. 따라서 이 모델도 사용하기에 적합하지 않았다.

Pydenet

실시간으로 depth estimation을 할 수 있는 가벼운 모델이라고 해서 실행시켜 봤다. 알려져 있는 것처럼 추론하는데 0.1초의 짧은 시간이 소요되었다. 그러나 정확도가 MonoDepth보다도 낮았어서 사용할 수가 없었다.

멀리 떨어져있는 벽과 가까이에 있는 사람의 depth가 비슷하다고 추정하고 있다. 정확도가 낮아서 사용하기 부적합하다.

DenseDepth

MonoDepth 다음으로 많이 사용되고 있는 depth estimation 모델이다. 3-4m 이상의 멀리 떨어져 있는 물체에 대해서 depth값 예측 정확도가 현저히 떨어지고, 비슷한 거리만큼 떨어져 있는 물체의 depth값을 전혀 다른 값으로 예측하는 경우들도 가끔 있어서 BTS와 같은 모델만큼의 정확도는 가지지 못한다. 그러나 depth image를 만들어내는데(추론하는데) 1초정도의 지연이 생겼고, 3m정도보다 가까운 물체에 대해서는 높은 정확도로 depth값을 예측해냈다. 그렇기 때문에 이 모델이 최선이라고 판단하여 선정하였다.

 

장애물 회피 기능 구현

depth estimation 기술을 사용하여 장애물 회피를 구현한 공개된 코드가 당연히 있을 줄 알았는데 없어서 직접 구현하였다. 

Depth estimation model인 DenseDepth을 사용하여 실시간으로 드론의 이미지로부터 depth 이미지를 출력하여 장애물 존재 여부를 파악한다. 만약에 depth 값이 작아 진다면, 즉 거리가 가까운 물체가 존재한다면 해당 영역의 반대 방향으로 주행 각도를 틀어 장애물을 회피한다. 사진처럼 드론의 이미지를 3x3의 9개의 영역으로 나누고 2번째 행의 영역에서 장애물이 왼쪽 혹은 가운데에 존재하면 오른쪽으로, 장애물이 오른쪽에 존재하면 왼쪽으로 피하게끔 구현하였다. 위의 DenseDepth 테스팅 영상에서 오른쪽 창에 실시간으로 계산되는 실수값이 영상 이미지를 3x3의 9개의 영역으로 나누어 각 영역 내의 depth값들을 평균내어 9개의 depth값을 출력한 것이다.

위의 그림: 드론의 영상 이미지를 9개의 영역으로 나눈 그림 / 아래 그림: 해당 이미지로부터 계산한 영역별 평균 depth 값

드론 영상으로부터 그림의 실수 값들처럼 실시간으로 영역별 depth 값이 계산된다. 이를 이용하여 장애물 회피를 구현하였다.

덧붙이자면, 이 DenseDepth 모델을 이용하여 조난자까지의 추정된 거리를 사용하여 드론으로부터 떨어진 거리도 예측하여 출력하도록 구현하였다.

 

드론 자체 결함 이슈

하드웨어 잠재적인 문제로 인해 구현한 코드를 제대로 테스트하기 힘들었다. 앞으로 10cm/s의 속도로 가라는 명령어를 똑같이 실행했을때 뜬금없이 오른쪽으로 이동할 때도 있고 대각선으로 이동할 때도 있고, 심지어 매번 다른 속도로 주행하는 문제가 자주 발생했다. 드론이 매우 작고 가볍워서 드론의 프로펠러로 인해 생기는 바람에도 영향을 받는 것 같고, 모터와 프로펠러의 성능이 안정적이지 못한 것을 원인으로 추측하고 있다. 이런 결함으로 대각선 방향으로 드론이 주행할 때 yaw값(틀어진 각도)을 보고 경로 조절하여 이런 오류를 줄이려는 시도를 해봤지만 효과는 미미했다. 프로젝트나 연구용으로는 이보다 좋은 드론을 사용하는 것을 강력히 추천한다.

 

프로젝트 후기

  • 드론을 조립하여 ROS로 드론을 제어하지 못한게 아쉽다. 완제품 드론은 구현하고 싶은 기능을 구현하기엔 한계가 있다. 다음에 기회가 되면 조립을 통해 드론의 기능을 완전히 제어하고 싶다.
  • 사용한 monocular depth estimation 기술들의 정확도가 높지 않아서 아쉬웠다. 모델의 ground truth인 depth image를 얻을 수 있으면 모델을 더 학습해볼 수 있었을 텐데 ground truth를 구할 수 있는 방법이 없어서 아쉬웠다.
  • 프로젝트를 통해 monocular depth estimation이라는 기술을 접하고 적용해 볼 수 있어서 좋았다. 생소한 분야였는데 재밌는 분야고 앞으로도 전망있는 연구인 것 같다.
  • 1년동안 잘 버텨주고 누구보다 열심히 참여해준 팀원들에게 너무 고마웠고 덕분에 힘든 부분들이 많았는데도 즐겁게 프로젝트를 진행할 수 있었다.

'프로젝트 > 졸업 프로젝트' 카테고리의 다른 글

ROS와 Gazebo로 드론 제어하기  (5) 2021.11.25