안녕하세요. 이번 글에는 신호등 인지에 대해서 작성하도록 하겠습니다.
사실 신호등 자체는 yolo를 통한 딥러닝으로 쉽게 인지가 가능합니다.
yolo는 기본적으로 coco dataset으로 잘 학습된 pt 파일을 제공합니다. 그 중 traffic light가 포함되어 있습니다. 따라서 보시는 것처럼 신호등 자체의 인지는 잘 되는 편 입니다.
그렇다면 이를 잘 활용해서 빨간불인지 파란불인지, 차량용 신호등인지 보행자 신호등인지, 알고리즘을 잘 구상하면 됩니다.
이제부턴 제가 구상한 알고리즘에 대해서 설명해드리겠습니다.
먼저 yolo를 통해서 신호등을 인지합니다. 저는 yolov10m.pt 파일을 사용하였습니다. 해당 파일은 앞서 말씀드린대로 coco dataset으로 학습된 기본적으로 제공되는 pt 파일입니다. 신호등 말고도 많은 class들이 학습되어 있기 때문에 아래 코드와 같이 신호등만 따로 crop한 이미지를 전송하도록 하였습니다.
대신에 위 사진과 같이 차량용 신호등과 보행자 신호등을 모두 인식합니다. 저는 배달 로봇을 사용하기 때문에, 세로가 더 긴 보행자 신호등만을 인지해야합니다. 이것 또한 포함하여 코드로 작성하면 아래와 같습니다.
if box[4] > 0.3 and label == 'traffic light':
left = int(box[0])
bottom = int(box[1]) # top보다 더 작음
right = int(box[2])
top = int(box[3])
# 가로가 더 긴 신호등은 건너뛰기
if abs(left - right) > abs(bottom - top):
continue
self.traffic_image_pub.publish(self.br.cv2_to_imgmsg(image_copy[bottom:top, left:right]))
전달받은 사진은 아래와 같이 조그맣게 나타납니다.
파란불인지 빨간불인지 확인하는 방법은 HSV Filter를 이용하였습니다.
처음에는 RGB를 사용하였지만, Green이라고 해서 (0, 255, 0)이 아닙니다. 완벽한 G가 아니면 R과 B의 값도 섞여져있기 때문에 정확한 임계값(threshold)을 설정하기 어려웠습니다.
RGB 대신에 HSV를 이용하여 쉽게 구할 수 있었습니다.
HSV는 색을 나타내는 방법으로 Hue(색상) Saturation(채도) Value(명도)의 3요소로 구성되어있습니다. 색깔에 따라 Hue값이 정해져있기 때문에 RGB 보다 색을 구분하기 훨씬 편합니다.
// 초록색 범위 정의 (HSV)
cv::Scalar lowerGreen(40, 15, 170); // 낮은 초록색 범위
cv::Scalar upperGreen(70, 70, 255); // 높은 초록색 범위
cv::Scalar lowerRed(0, 0, 70); // 낮은 빨간색 범위
cv::Scalar upperRed(25, 160, 255); // 높은 빨간색 범위
제 github에 있는 hsv_filter.py를 이용해서 사진의 HSV를 조절하셔서 HSV값 임계값을 쉽게 찾으실 수 있습니다.
알고리즘을 큰 주제로 묶기 애매해서 한 주제로 설명드리겠습니다.
이동 평균 필터(Moving average filter)
순간 잘못 인지하는 것을 방지하기 위해서 이동 평균 필터를 사용하였습니다. 개수는 10개로 설정하였고, 절반인 5개를 임계값으로 설정하였습니다. 따라서 5개 이상값이 빨간불이냐 파란불이냐에 따라 로봇의 이동은 달라지게 됩니다.
(사실 평균을 사용하지는 않아서 엄격하게 이동 평균 필터는 아닙니다만 sum을 쓰든, 평균을 쓰든 비슷합니다.)
서비스(Service) 구현
ROS 서비스를 통해서 신호등 미션에 돌입했을때만 코드가 동작하게끔 하였습니다.
bool Traffic::go_crosswalk(traffic_light::traffic_srv::Request &req, traffic_light::traffic_srv::Response &res)
서비스의 request를 받지 않으면 신호등을 인지한다고 해도 아무런 작동을 하지 않습니다. Request를 받게 되면, 신호등 인지 처리에 들어갑니다.
대신, 파란불을 봤다고 해서 무조건 로봇을 가게 하지 않습니다. 왜냐하면 파란불이 몇초가 남았는지 로봇은 알기 힘들기 때문입니다. 깜빡이는 파란불을 봤다면, 사람 조차도 건너야하나 말아야하나 고민합니다. 저속 주행을 하는 배달 로봇은 깜빡이는 파란불에 건넜다간 빨간불이 되어서 교통에 방해를 줄 수 있습니다.
저는 빨간불을 본 이후에 파란불을 봐야 출발하도록 알고리즘을 구상하였습니다. 빨간불을 보고 정지하여 기다리다가 파란불이 새롭게 켜졌을때만 출발합니다. 만약 request를 받고 바로 초록불을 봤다면, 빨간불을 보지 못 하였기 때문에 출발하지 않습니다.
Service의 request가 들어오지 않으면 아무일도 일어나지 않습니다.
Service의 request가 들어왔습니다. 이제부터 인지를 시작합니다. 로봇은 빨간불을 무조건 봐야 출발할 수 있습니다.
Service의 request도 받았고, 빨간불도 보았기 때문에 GO라는 출발 신호로 변경되는 것을 확인하실 수 있습니다.
아래의 github를 통해서 구체적인 코드를 보실 수 있습니다.
https://github.com/jinhoyoho/traffic_light
시뮬레이션 기반이다보니 현실 세계(real world)에서 잘 동작하는지 모르겠습니다.
하지만 현실 신호등에 맞게 hsv의 threshold를 조절한다면 잘 동작할 것으로 기대합니다.
종형 신호등에 약합니다.
해당 사진은 여의도 한국거래소 앞 종형 신호등입니다.
코드 자체가 단순하게 가로보다 세로가 더 긴 것만 보행자 신호등으로 구성하였지만, 이런 신호등을 본다면 잘못 인식하여 출발할 가능성이 있습니다.
자동차 입장에서도 '세로보다 가로가 긴 신호등만 인식한다'라는 코드를 짰다면, 여의도 한국거래소 앞에서는 작동하지 않을 것 입니다.
단순히 가로, 세로의 길이로 판단하는 것이 아닌 추가적인 코드 개선이 필요합니다.
이번 대회에서는 인지부분을 맡아서 사람과 신호등을 중심으로 개발하였습니다.
항상 느끼는거지만 인지는 어렵지 않습니다. 인지를 한 후에 "어떤 알고리즘으로 차량을 움직일 것인가"가 어렵습니다.
파란불과 빨간불을 구분하는 것은 어렵지 않게 구현할 수 있습니다. 하지만 이를 이용해서 어떤 조건에서 차량을 움직이게 할지(ex: 파란불을 보면 무조건 출발해야 하는지, 언제 코드를 동작시키게 해야하는지 등등)는 어렵습니다.
위에서 언급한 것 처럼 종형 신호등의 문제도 있고요.(모든 까마귀는 검다!)
사람을 인지하고 calibration을 통해서 거리를 구하는 것은 쉽게 할 수 있습니다. 하지만 이를 이용해서 어떻게 차량을 움직여야하는지는 어렵습니다.
물론 해당 영역은 제어쪽에 가깝습니다만, 인지와 제어의 중간에 있는 부분을 메울 필요가 있다고 생각합니다.
아무래도 자율주행에 대한 저의 공부가 부족한 탓일 수도 있습니다. 자율주행에 대한 공부 커리큘럼이 하루 빨리 자리를 잡아, 많은 인재들이 공부를 했으면 좋겠습니다.
출처
https://velog.io/@smile_b/Filtering-Colors
https://ko.wikipedia.org/wiki/HSV_%EC%83%89_%EA%B3%B5%EA%B0%84