🚀Project🚀/Navigation with detecting person

[Project1] - (3) usb 캠을 이용해서 실제 검출 결과 확인하기

고집호랑이 2023. 5. 5. 08:10

YOLOv8을 ROS2와 성공적으로 연동시켰으니 이제는 실제 카메라를 이용해서 객체를 잘 검출하는지 확인해 볼 차례입니다. 

 

당장 저에게 real sense camera나 성능 좋은 카메라가 없기 때문에 집에 굴러다니는 usb 카메라를 이용해 확인해볼 생각입니다. 

 

그렇다면 먼저 ROS2와 연동되며, usb 카메라의 이미지 정보를 publish 해줄 node가 필요합니다. 이를 이미 구성해놓은 코드는 github에서 손쉽게 찾을 수 있었습니다. 

 

https://github.com/ros-drivers/usb_cam/tree/ros2 

 

GitHub - ros-drivers/usb_cam: A ROS Driver for V4L USB Cameras

A ROS Driver for V4L USB Cameras. Contribute to ros-drivers/usb_cam development by creating an account on GitHub.

github.com

 

위 링크가 해당 github이며 파일의 내용을 수정하기 위해서 바이너리 설치가 아닌 소스 코드를 직접 다운받아서 패키지를 설치하였습니다.

 

Installation & Error Solution

$ cd /path/to/colcon_ws/src
$ git clone https://github.com/ros-drivers/usb_cam.git
$ cd /path/to/colcon_ws
$ rosdep install --from-paths src --ignore-src -y
$ cd /path/to/colcon_ws
$ colcon build
$ source /path/to/colcon_ws/install/setup.bash

이 과정이 위 github 링크에 적혀있는 설치 방법인데, 이대로 진행하니 역시 여러 오류가 발생하였습니다.

 

먼저 $ rosdep install --from-paths src --ignore-src -y 을 실행시켰을 때 다음과 같은 error가 발생했습니다.

 

패키지 설치 시 발생 오류

 

★ ERROR: the following packages/stacks could not have their rosdep keys resolved
to system dependencies: 

 

자세히는 모르겠지만 이 에러는 command line 마지막에 -r 옵션을 추가해줬더니 해결되었습니다. 즉 아래 명령어를 실행시키는 것이죠. 

$ rosdep install --from-paths src --ignore-src -y -r

 

ros wiki에 따르면, 이 명령어는 본인의 workspace 안에 있는 패키지가 의존하는 패키지(dependency package)가 존재하지만 본인의 컴퓨터에 없는 모든 패키지를 설치해준다고 합니다. 

 

아마 -r 옵션을 추가해주지 않으면 모든 의존성 패키지가 자동으로 설치되지 않는 것으로 보입니다.

 

ros wiki rosdep 관련 정보

 

이후 $ colcon build 과정에서도 다음과 같은 오류가 발생하였습니다.

 

★ ros2 Could not find a package configuration file provided by "catkin" with any of the following names:     

catkinConfig.cmake     

catkin-config.cmake

 

이는 ROS1용으로 만들어 패키지를 ROS2에서 build 시킬 때 발생하는 에러입니다. 

 

아시다시피 ROS1과 ROS2는 빌드 시스템은 서로 다른데 ROS1용 코드를 build 하려고 하니 에러가 발생한 것이죠.

 

위 github 링크의 usb_cam 패키지는 본래 ROS1에서 사용되기 위해 만들어졌다가 ROS2와도 연동시키기 위해서 해당 github의 ROS2라는 branch에 ROS2와 연동되는 usb_cam 패키지를 제공합니다.

 

하지만 설치 과정에서 ROS1, ROS2용으로 만들어진 전체 코드를 git clone으로 다운받았기 때문에 build 과정에서 오류가 발생한 것입니다.

 

만약 ROS2와 연동되는 코드가 따로 없다면 build 시스템을 변경하거나 특정 파일을 새로 작성하는 등 여러가지 복잡한 작업을 통해서 이 에러를 해결할 수 있겠지만, ROS2용 코드가 이미 존재하는 이상 그럴 필요가 없죠.

 

아래 2가지 방법 중 한가지를 선택하면 에러를 해결할 수 있습니다.

 

1. 아래 명령어를 통해서 ros2 branch에 있는 코드들만 git clone 해준 후 build

$ git clone -b ros2 --single-branch https://github.com/ros-drivers/usb_cam.git

 

2. ROS2용으로 만들어진 코드를 github에서 zip 파일로 다운받은 후 원하는 파일에 압축 해제를 하고 build

 

최종적으로 오류 없이 패키지를 설치하는 과정은 다음과 같습니다.

$ cd /path/to/colcon_ws/src
$ git clone -b ros2 --single-branch https://github.com/ros-drivers/usb_cam.git # or zip 파일 직접 다운로드
$ cd /path/to/colcon_ws
$ rosdep install --from-paths src --ignore-src -y -r
$ cd /path/to/colcon_ws
$ colcon build
$ source /path/to/colcon_ws/install/setup.bash

 

이후 아래 명령어로 launch 파일을 실행시켜주면 됩니다.

$ ros2 launch usb_cam demo_launch.py

 

노트북에 패키지를 설치하고 실행시키니 초기 설정으로 노트북 자체 카메라가 켜졌습니다.

 

제가 가진 usb 카메라를 키고 YOLOv8 패키지와 연동시키면서 이를 노트북에서 실행시키려면 몇몇 파일을 수정할 필요가 있습니다.

 

Setting

1. 내가 사용하는 usb 카메라 번호 확인하기

$ ls -l /dev/video*

 

터미널 창에 위와 같은 명령어를 입력하면 현재 기기에서 사용할 수 있는 카메라 장치들이 모두 뜹니다. 

 

제가 사용할 usb 카메라를 연결하기 전에는 0~3번까지만 뜨다가 usb 카메라를 연결하니 4, 5번이 추가된 것으로 봐선 제 usb 카메라의 번호는 4, 5번이라는 것을 알 수 있습니다. 

 

내가 사용하는 usb 카메라의 번호

 

2. 내가 사용하는 usb 카메라 정보 확인하기

$ v4l2-ctl -d /dev/video4 --list-formats-ext

 

위의 명령어를 치면 4번 카메라 즉 제가 사용할 usb 카메라의 정보가 나옵니다.

 

사용할 내 usb 캠 정보

 

출력 결과에 따르면 제 usb 카메라는 YUYV와 MJPG 2가지 포맷 방식을 지원하고, 각 포맷 별로 640 x 480, 352 x 288, 320 x 240 등등 여러 출력 사이즈들도 지원하는 것을 확인할 수 있습니다.

 

3. usb_cam 패키지 파일 값 수정하기

usb_cam 패키지는 사용할 카메라 parameter 값으로 usb_cam-ros2/config/params.yaml 안에 적혀있는 내용을 사용합니다.

 

원래 params.yaml 파일은 다음과 같습니다. 

 

/**:
    ros__parameters:
      video_device: "/dev/video0"
      framerate: 30.0
      io_method: "mmap"
      frame_id: "camera"
      pixel_format: "yuyv"
      color_format: "yuv422p"
      image_width: 640
      image_height: 480
      camera_name: "test_camera"
      camera_info_url: "package://usb_cam/config/camera_info.yaml"
      brightness: -1
      contrast: -1
      saturation: -1
      sharpness: -1
      gain: -1
      auto_white_balance: true
      white_balance: 4000
      autoexposure: true
      exposure: 100
      autofocus: false
      focus: -1

 

사용할 video_device 이름은 /dev/video0 으로 지정되어 있고 이것이 아마 노트북 내장 카메라의 번호일 것입니다. 제가 사용할 usb 카메라의 번호는 4번이었으므로 video_device 이름을 /dev/video4로 바꿔줘야 합니다.

 

video_device: "/dev/video4"

 

사용할 포맷 방법은 yuyv로, 출력 사이즈는 640 x 480으로 설정되어 있는데 (pixel_format: "yuyv", image_width: 640, image_height: 480) 이는 제 usb 카메라도 지원하는 형식이므로 수정할 필요는 없겠습니다.

 

4. yolov8_ros 패키지 파일 값 수정하기

yolov8_ros 패키지의 파일도 일부 수정할 필요가 있습니다. 일단 제 usb camera가 publish하는 이미지 topic과 yolov8_ros에서 subscribe하는 topic의 이름이 일치해야 합니다. 

 

또한 저는 노트북에서 두 패키지를 실행시킬 것이기 때문에 YOLOv8의 연산을 수행할 device도 cpu로 바꿔줘야 합니다.

 

usb_cam 패키지를 실행시키고 아래 그림과 같이 $ ros2 topic list 명령어로 확인한 결과 usb_cam 노드가 publish하는 topic 이름은 /image_raw 였습니다.    

  

usb_cam 노드가 publish하는 topic 이름

 

yolov8_ros 패키지의 yolov8_node.py에서 subscribe하는 topic 이름 또한 /image_raw로 이미 두 topic의 이름이 일치하는 것을 확인했습니다. 

 

결국 device의 parameter 값만 "cpu"로 수정해주면 되죠. 

 

아래는 수정한 yolov8_node.py의 일부입니다. device의 parameter 값을 "cpu"로 수정해주었고, 마지막 부분에 detect 결과를 이미지로 확인할 수 있는 코드도 추가해 주었습니다.

class Yolov8Node(Node):

    def __init__(self) -> None:
        super().__init__("yolov8_node")

        # params
        self.declare_parameter("model", "yolov8m.pt")
        model = self.get_parameter(
            "model").get_parameter_value().string_value

        self.declare_parameter("tracker", "bytetrack.yaml")
        tracker = self.get_parameter(
            "tracker").get_parameter_value().string_value

        self.declare_parameter("device", "cpu") # "cuda:0" cpu로 변경
        device = self.get_parameter(
            "device").get_parameter_value().string_value

        self.declare_parameter("threshold", 0.6)
        self.threshold = self.get_parameter(
            "threshold").get_parameter_value().double_value

        self.declare_parameter("enable", True)
        self.enable = self.get_parameter(
            "enable").get_parameter_value().bool_value

        self._class_to_color = {}
        self.cv_bridge = CvBridge()
        self.tracker = self.create_tracker(tracker)
        self.yolo = YOLO(model)
        self.yolo.fuse()
        self.yolo.to(device)

        # topcis
        self._pub = self.create_publisher(Detection2DArray, "detections", 10)
        self._dbg_pub = self.create_publisher(Image, "dbg_image", 10)
        self._sub = self.create_subscription(
            Image, "/image_raw", self.image_cb, # subscribe하는 topic 이름이 
                                                # usb_cam에서 publish하는 topic 이름과 동일 
            qos_profile_sensor_data
        )
                                        .
                                        .
                                        .
                                        
        cv2.imshow('result', cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)) # detect 결과를 확인하기 위해
        cv2.waitKey(10)                                                 # 추가해준 부분

def main():
    rclpy.init()
    node = Yolov8Node()
    rclpy.spin(node)
    rclpy.shutdown()

 

실행시켜보기 & 결과 확인하기

위의 모든 과정을 마쳤으면 이제 실행 결과를 확인해 볼 차례입니다. 

 

결과를 확인하려면 아래 명령어를 서로 다른 터미널에서 입력해주면 되겠죠.

 

$ cd path/to/usb_cam
$ . install/setup.bash
$ ros2 launch usb_cam demo_launch.py

$ cd path/to/yolov8_ros
$ . install/setup.bash
$ ros2 launch yolov8_bringup yolov8.launch.py

 

실행 파일의 경로를 사용자 path나 library path에 추가해줘야 실행되므로 각각의 실행 파일이 담겨있는 directory로 가서 $ . install/setup.bash를 입력해주는 것을 잊으면 안됩니다. 

 

 

실행 결과 위 동영상처럼 사람(person) 뿐만 아니라 시계(clock), 마우스(mouse), 휴대폰(cell phone) 등 여러 물체들을 높은 confidence score로 잘 detect하는 것을 확인할 수 있습니다.

 

$ ros2 topic echo /yolo/detections

 

또한 detect한 객체의 정보를 담고있는 '/yolo/detections' topic을 위 명령어를 통해서 실시간으로 받아왔더니 아래와 같이 detect된 object의 id 부터 Confidence score, bounding box의 중심좌표, 너비(size_x), 높이(size_y)까지 출력되는 것을 확인할 수 있습니다.

 

아래 값은 person이 detect된 '/yolo/detections' topic만을 가져온 것으로 실제로 터미널 창에는 아래와 같은 topic 형식이 반복적으로 출력되고 있습니다.

header:
  stamp:
    sec: 1683240144
    nanosec: 278132000
  frame_id: camera
detections:
- header:
    stamp:
      sec: 0
      nanosec: 0
    frame_id: ''
  results:
  - id: person
    score: 0.9295174479484558
    pose:
      pose:
        position:
          x: 0.0
          y: 0.0
          z: 0.0
        orientation:
          x: 0.0
          y: 0.0
          z: 0.0
          w: 1.0
      covariance:
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
      - 0.0
  bbox:
    center:
      x: 236.02658081054688
      y: 300.0291442871094
      theta: 0.0
    size_x: 364.4188232421875
    size_y: 358.8379821777344
  source_img:
    header:
      stamp:
        sec: 0
        nanosec: 0
      frame_id: ''
    height: 0
    width: 0
    encoding: ''
    is_bigendian: 0
    step: 0
    data: []
  is_tracking: false
  tracking_id: ''

 

이후 이 'yolo/detections' topic을 subscribe 한 후 parsing을 통해 results의 id 부분이 person인지 아닌지만 확인한다면 지금 카메라에 person이 detect되고 있는지 아닌지를 판단할 수 있을 것입니다.

 

Reference

https://kkastory.tistory.com/22

 

https://kang-korea-life.tistory.com/m/126 

 

https://answers.ros.org/question/385797/issue-with-creating-a-workspace-ros2/?sort=votes