: 이번 포스팅은 turtlebot teleop(key) 패키지를 optical flow를 이용하여 직접 구현해보는 것입니니다. Camera node에서 publish한 이미지 메시지를 subscribe하여 optical flow를 통해 direction을 결정하고, angular와 velocity에 관한 정보를 담고 있는 geometry 메시지를 publish하여서, 최종적으로 Gazebo가 이 geometry 메시지를 subscribe하여 turtlebot을 움직이게 하는 것이 이번 프로젝트의 큰 흐름이라 할 수 있겠습니다.
Installation command : $ sudo apt install ros-kinetic-opencv*
Installation command : $ sudo apt install ros-kinetic-usb-cam
Execution command : $ rosrun usb_cam usb_cam_node
먼저 ROS-OpenCV 라이브러리를 통해 시각 데이터를 ROS내부에서 분석이 가능하게 만들었습니다. 다음으로 usb_cam 노드에서 publish한 이미지 메시지를, 구현한 노드에서 subscribe하여 프레임 단위로 받을 수 있게 한 것이 가장 기초적인 과정이라 할 수 있습니다. Usb_cam 노드에서는 빈번하게 캡쳐한 프레임을 publish하고, 이를 subscribe하면은 연속적인 프레임들, 즉 영상을 처리할 수 있습니다.
다음으로는 제공받는 optical flow 코드를 이용하여 노드가 subscribe한 이미지들에 적용하는 것이었습니다. 먼저 제공받은 코드는 OpenCV의 videocapture를 이용하여, 비디오 영상을 캡쳐하여 프레임을 가져왔는데, 이 과정과 유사하게 ROS내에서 USB cam을 통해 찍은 프레임들을 연속적으로 받아오는 것으로 대체할 수 있었습니다. 받아온 프레임의 process 과정은, cvtColor를 이용하여 흑백으로 만들고, hsv로 변환하는 과정을 겪습니다. 프레임별 10픽셀을 기준으로 point를 얻어서 flow를 추적하게 되는데, 이전 프레임의 point와 새로 받은 프레임의 point들을 비교하여 flow를 추적하는 것으로 생각하였습니다. 따라서 Point class type의 변수 direction을 추가하여 pt1, pt2의 x, y 위치 이동값을 저장하였고, 이를 array에 저장하여 추후에 이를 토대로 총합을 평균하여 Major direction을 얻을 수 있게 구현하였습니다. 또한 Direction을 정할 때, 완전한 상하좌우가 아닌 경우나 대각선으로 이동하는 경우, 상하좌우 중에서 가장 크게 방향의 이동이 있는 것을 major direction으로 설정하였습니다.
기존에 참고한 turtlebot_teleop_key의 코드를 보면, 누르는 키에 따라 angular/velocity의 값을 +-하는 메커니즘을 사용하고 있었습니다. 이를 대체하기 위해 위에서 얻은 major direction을 누르는 키를 대체할 수 있게 하였으며, 기존처럼 limit까지 angular/velocity +-0.1을 진행하려 하였으나, 카메라에 flow를 계속 주어서 조종해야 하는 애로사항이 있어서, 조종의 편의성을 위해 각각의 direction을 받을 때 angular/velocity 값을 +-0.3으로 맞춰 주었습니다. 또한 영상에 flow, 즉 움직임이 없을 때도, 진동과 같은 문제로 인해 flow가 미세하게 잡혀서 터틀봇이 움직이는 문제가 발생하였습니다. 이를 보완하기 위해 stop이라는 bool type을 도입하여, 정지해 있는 상태를 추가하고자 하였습니다. 위에서 pt1, pt2의 변화값(이동량)에 0보다 큰 임계값을 주어, 이 값보다 작을 때 stop = True로 하고, angular/velocity값을 0으로 만들어, flow가 없는 경우 카메라의 매우 미세한 움직임에 의해 움직이는 경우를 방지하였습니다. Threshold 값을 구하는 과정은 먼저 실제 point의 변화값을 출력하게 하여, trial & error를 통해 설정하였습니다. 하지만, 너무 크게 잡으면 flow를 많이 주어야 터틀봇이 움직인다는 문제점이 있고, 너무 작게 설정하면 정지 상태서도 진동에 의한 flow를 인식한다는 문제점이 있었고, 이것의 tradeoff를 고려하여 적절한 값을 찾는 것이 어려웠습니다.
위와 같은 과정을 통해 얻은 velocity, angular 값을 ROS의 메시지타입의 일종인 geometry msg타입으로 publish 하였습니다. Geometry message타입을 조금 더 알고자 rqt를 이용하여 기존의 turtlebot teleop key를 분석해보았는데, [‘linear’, ‘angular’]형태로 되어 있으며, ‘[x,y,z]’’[x,y,z]’ 형태로 value를 가지고 있음을 알 수 있었습니다. geometry_msgs::Twist vel; vel.angular.z = angular; vel.linear.x = linear;
위와 같이 vel라는 geometry_msgs 타입의 클래스 변수의 linear.x값에 위에서 정한 velocity값을 주어 터틀봇의 전진/후진을, angular.z값에 위에서 정한 angular값을 주어 터틀봇의 좌회전/우회전을 조정할 수 있게 하였습니다. 이 geometry 메시지를 ‘/cmd_vel’이라는 topic name을 주어 publish하였는데, 이는 gazebo simulator가 ‘cmd_vel’ 토픽을 subscribe하기 때문입니다. Gazebo는 optical flow를 통해 publish된 메시지를 subscribe하여, 그 속의 제어 정보를 이용하여 터틀봇을 움직입니다.
물체를 조종하는 방법으로는, 만약 물체가 오른쪽으로 이동하고 있으면 터틀봇은 우회전을, 물체가 왼쪽으로 이동하면 좌회전을 하도록 만들었습니다. 마찬가지로 물체가 위로 움직이면 직진, 아래로 움직이면 후진을 할 수 있도록 구현하였습니다.
$ roscore
$ rosrun usb_cam usb_cam_node
$ roslaunch turtlebot3_gazebo turtlebot3_world.launch
$ rosrun turtlebot_teleop turtlebot_teleop
패키지가 실행되면, mission#1 / mission#2에 해당하는 window가 뜨게 됩니다. 이제 물체를 움직여서 터틀봇을 조종하면 됩니다전후좌우 이동하는 결과를 나타냈습니다. 결과 이미지들을 확대하여 살펴보면, flow vector(초록색 선)의 방향에 따라 terminal에 방향이 출력되고, 이에 따라 터틀봇이 움직이는 것을 확인할 수 있습니다.