여기서는 기존의 젯봇 프로그래밍을 ROS Framework으로 하는 방법을 생각해 본다.본 장은 주로 jetbot_ros 을 참고했다.앞서 글에서 진행한 기본 SD card를 통해 CUDA, cuDNN, TensorRT 가 설치되어 있음을 가정한다.바로 ROS Melodic을 설치해 보자
ROS_INFO("failed to convert %ux%u %s image", input->width, input->height, input->encoding.c_str());
return;
}
// classify the image
float confidence = 0.0f;
const int img_class = net->Classify(cvt->ImageGPU(), cvt->GetWidth(), cvt->GetHeight(), &confidence);
// verify the output
...
}
위 코드에서 net 변수를 초기화 하는 방법은 다음과 같다.
#include <jetson-inference/imageNet.h>
imageNet::NetworkType model = imageNet::NetworkTypeFromStr("googlenet"));
imageNet* net = imageNet::Create(model);
처음에는 ROS의 python2 code로 구현한 노드가 어떻게 deep learning 의 python3 API를 부르는지 궁금했는데 실상은 python을 사용하지 않고 둘다 C++로 코딩되어 있었다.
jetbot_ros 패키지 설치
이 패키지는 다음의 3개의 노드를 제공한다.
jetbot_motors.py 노드: 젯봇의 모터 제어 메시지를 구독하여 모터를 제어하는 노드
jetbot_oled.py 노드: 문자열 메시지를 구독하여 OLED display 에 표시하는 노드
jetbot_camera 노드: 카메라에서 이미지 프레임을 얻어 이를 발행하는 노드
이 패키지를 설치하기 전에 python2 코드로 모터를 구동할 수 있게 해주는 Adafruit 모터 드라이버 라이브러리와 OLED 디스플레이 드라이버를 먼저 설치해야 한다. jetbot에는 이미 기본으로 "기본 동작 확인" 장에서 사용한 jetbot이라는 모터 제어 라이브러리가 있으나 이는 python3 코드로 작성되어야 이 라이브러리를 사용할 수 있다. 그러나 ROS는 python3를 지원하지 않기 때문에 따로 python2용 드라이버를 설치해야 한다.
이후 카메라로 들어오는 비디오 프레임은 /jetbot_camera/raw 토픽으로 발행된다.이때 타입은 sensor_msgs::Image 이며 BGR8 형식으로 인코딩된 이미지이다.안타깝게도 발행되는 이미지를 볼수 있는 구독 노드가 참고 자료에서 주어지지 않았다.하지만 rqt_image_view 도구를 사용하면 쉽게 볼 수 있지 않을까 해서 아래와 같이 시도해 보았다.만약 rqt_image_view 가 설치되어 있지 않다면 다음과 같이 설치한다.
sudo apt install ros-melodic-rqt-image-view
다음을 실행하고 /jetbot_camera/raw를 구독해 본다.
$ rqt_image_view
일단 구독된 이미지는 보여지지 않고 다음과 같은 메시지를 볼 수 있다.
[ WARN] [1570983432.590765540]: [image_transport] It looks like you are trying to subscribe directly to a transport-specific image topic '/jetbot_camera/raw', in which case you will likely get a connection error. Try subscribing to the base topic '/jetbot_camera' instead with parameter ~image_transport set to 'raw' (on the command line, _image_transport:=raw). See http://ros.org/wiki/image_transport for details.
메시지에 따르면 발행되는 이미지를 날로(raw) 구독하는 경우 워낙 밀고들어오는 메시지가 많아서 통신이 안될 수 있으므로 image_transport 패키지를 사용해서 압축해서 구독하라는 의미로 보인다. image_transport의 Wiki 를 참고해서 각자 시도해 보자.
이 이미지 메시지 타입에 대한 자세한 정보는 여기를 참고한다.
마치며
지금까지 소개한 ROS Framework의 노드를 활용하면 앞장의 충돌회피, 물체따라가기, 길 따라가기를 ROS framework으로 재작성할 수 있다.즉 jetbot_camera 노드로 카메라 이미지를 발행하고 이를 imagenet/detectnet/segnet 노드로 실시간 분석하여 적절하게 모터제어 메시지를 발행한다. 최종적으로 jetbot_motor.py 노드가 이 메시지를 구독하여 모터를 움직이게 하는 식이다.이를 그래프로 도시하면 아래와 같다.
이를 위해서는 imagenet/detectnet/segnet 노드가 이미지의 분석결과로부터 어떻게 모터를 제어할 지에 대한 제어 전략 구현과 jetbot_motors.py 노드로 메시지를 발행하는 코드를 추가하는 수정이 필요하다.기존의 프로그래밍 방법에 비해 ROS를 사용하여 프로그래밍하는 방법의 장점은 여기서는 사실 크지 않을 것 같다. 다만 ROS 프로그래밍을 연습하는 예로서 의미는 찾을 수 있다.
즉 아래의 orange_0.jpg 그림을 97.8%의 확률로 class 0950 (orange) 으로 인식한다는 의미이다.차순위 확률로 class 0951 (lemon) 을 2.09%의 확률로 인식했다.이번에는 입력 이미지로 granny_smith_0.jpg 화일을 입력하여 실행해 보았다.
결과는 다음과 같다.
발행한 이미지는 peds-004.jpg로 다음과 같다.결과는 아래와 같다. 위 결과는 5명의 Object가 탐지되었고 모두 class #0로 사람(person)으로 판정된다. 위 그림에서 서있는 5명의 사람에 해당한다.안타깝게도 github에서 detectnet 노드의 코드를 살펴보니 인식된 object를 boxing하여 합성된 이미지를 출력하는 코드는 구현되어 있지 않다.
ros_graph로 현재의 발행과 구독 상황을 보면 다음과 같다. 즉 image_publisher 노드가 /Image_raw 토픽으로 메시지를 발행하고 이를 detectnet 노드가 구독하는 상황이다.
ROS 노드에서 deep learning 추론 API 사용 방법deep learning 추론 라이브러리를 ROS 노드에서 어떻게 호출되는지 살펴보겠다.imagenet 노드를 구현한 C++ 코드에서 이미지를 구독했을 때 void img_callback( const sensor_msgs::ImageConstPtr& input ) 함수가 불려진다.이 함수는 아래와 같다. 처음 부분은 입력 이미지를 추론 엔진이 사용하는 입력 포맷으로 변환하는 부분이다.이후 이미지를 분류하기 위해 deep learning 추론 엔진을 부르는 부분은 net->Classify(); 이다.4개의 파라미터를 요구하는데 첫번째는 이미지이고 두번째, 세번째는 이미지의 size 정보이다. 나머지는 추론 확률을 받기 위한 변수이다.이 함수는 분류한 이미지의 레이블 번호를 반환한다. 자세한 정보는 Jetson Inference: imageNet Class Reference 를 참고한다.