【ROS2】使用摄像头功能包 usb_cam

1、准备工作

因为本人使用VirtualBox虚拟机运行的ROS2,所以首先要让摄像头可以在虚拟机中运行

1.1 安装VirtualBox扩展包

1)下载地址:https://www.virtualbox.org/wiki/Downloads,注意扩展包的版本要和虚拟机的版本匹配

【ROS2】使用摄像头功能包 usb_cam_第1张图片
2)安装
打开虚拟机 ——》工具 ——》扩展 ——》安装

【ROS2】使用摄像头功能包 usb_cam_第2张图片
3)安装成功

【ROS2】使用摄像头功能包 usb_cam_第3张图片

1.2 在虚拟机中添加摄像头

【ROS2】使用摄像头功能包 usb_cam_第4张图片

1.3 测试摄像头

1)查看摄像头节点

~$ ls /dev/video* 
/dev/video0  /dev/video1

2)使用ffplay测试

ffplay /dev/video0

【ROS2】使用摄像头功能包 usb_cam_第5张图片

2、安装usb_cam

安装命令如下,注意本人的Ubuntu版本是22.04,对应ROS2版本为humble

sudo apt install ros-humble-usb-cam

【ROS2】使用摄像头功能包 usb_cam_第6张图片

3、命令测试

1)启动摄像头及显示

~$ ros2 launch usb_cam demo_launch.py
[INFO] [launch]: All log files can be found below /home/laoer/.ros/log/2023-06-09-10-03-47-100527-laoer-VirtualBox-8264
[INFO] [launch]: Default logging verbosity is set to INFO
/opt/ros/humble/share/usb_cam/config/params.yaml
……

从打印信息可以看出,摄像头相关参数的配置文件路径:/opt/ros/humble/share/usb_cam/config/params.yaml

2)只启动摄像头节点

ros2 run usb_cam usb_cam_node_exe --ros-args --params-file /opt/ros/humble/share/usb_cam/config/params.yaml

3)使用rqt_image_view显示图像
下载,注意版本,本人的ROS2版本为humble

sudo apt install ros-humble-rqt-image-view

【ROS2】使用摄像头功能包 usb_cam_第7张图片

4、编程测试

4.1 python编程测试

1)进入功能包源码目录(根据自己的环境创建)

cd ~/ros/eg/src/py

2)创建功能包

ros2 pkg create --build-type ament_python camera

3)编辑源码

cd camera
vi topic_camera_sub.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import rclpy                            # ROS2 Python接口库
from rclpy.node import Node             # ROS2 节点类
from sensor_msgs.msg import Image       # 图像消息类型
from cv_bridge import CvBridge          # ROS与OpenCV图像转换类
import cv2                              # Opencv图像处理库

class ImageSubscriber(Node):
    def __init__(self, name):
        super().__init__(name)                                  # ROS2节点父类初始化
        self.sub = self.create_subscription(
            Image, 'image_raw', self.listener_callback, 10)     # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
    self.cv_bridge = CvBridge()                             # 创建一个图像转换对象,用于OpenCV图像与ROS的图像消息的互相转换

def show(self, image):
    cv2.imshow("camera", image)                             # 使用OpenCV显示图像效果
    cv2.waitKey(10)

def listener_callback(self, data):
    self.get_logger().info('Receiving video frame')         # 输出日志信息,提示已进入回调函数
    image = self.cv_bridge.imgmsg_to_cv2(data, 'bgr8')      # 将ROS的图像消息转化成OpenCV图像
    self.show(image)                               

def main(args=None):                                        # ROS2节点主入口main函数
    rclpy.init(args=args)                                   # ROS2 Python接口初始化
    node = ImageSubscriber("topic_webcam_sub")              # 创建ROS2节点对象并进行初始化
    rclpy.spin(node)                                        # 循环等待ROS2退出
    node.destroy_node()                                     # 销毁节点对象
    rclpy.shutdown()                                        # 关闭ROS2 Python接口

4)修改配置
修改setup.py,添加入口
在entry_points中添加

'topic_camera_sub= camera.topic_camera_sub:main',

完整的setup.py如下

~/ros/eg/src/py/camera$ cat setup.py 
from setuptools import setup
package_name = 'camera'
setup(
    name=package_name,
    version='0.0.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='laoer',
    maintainer_email='[email protected]',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
		'topic_camera_sub= camera.topic_camera_sub:main',
        ],
    },
)

5)编译
注意:在项目根目录中编译

cd ~/ros/eg
colcon build

6)运行
在终端1中启动摄像头

ros2 run usb_cam usb_cam_node_exe --ros-args --params-file /opt/ros/humble/share/usb_cam/config/params.yaml

在终端2中启动测试程序

cd ~/ros/eg
source install/setup.sh
ros2 run camera topic_camera_sub

4.2 c++编程测试

1)进入功能包源码目录(根据自己的环境创建)

cd ~/ros/eg/src/cpp

2)创建功能包

ros2 pkg create --build-type ament_cmake cpp_camera  --dependencies rclcpp OpenCV sensor_msgs cv_bridge

3)编辑源码

cd cpp_camera/src
vi camera_sub.cpp
#include 
#include 

#include "rclcpp/rclcpp.hpp"
#include "sensor_msgs/msg/image.hpp"
#include "cv_bridge/cv_bridge.h"

// std::bind占位符引用
using std::placeholders::_1;

class CameraSub : public rclcpp::Node
{
  public:
    CameraSub()
    : Node("cpp_camera_subscriber")
    {
      # a) 初始化订阅者,使用消息类型Image、主题名称image_raw */
      subscription_ = this->create_subscription<sensor_msgs::msg::Image>(
      "image_raw", 10, std::bind(&CameraSub::topic_callback, this, _1));
    }

  private:
    void topic_callback(const sensor_msgs::msg::Image & img) const
    {
      RCLCPP_INFO(this->get_logger(), "Receiving video frame");
      # b)cv_bridge::toCvCopy:Image 话题消息转 opencv 的 cv::Mat
      cv::imshow("camera", cv_bridge::toCvCopy(img, sensor_msgs::image_encodings::BGR8).get()->image);
      cv::waitKey(10);
    }

    rclcpp::Subscription<sensor_msgs::msg::Image>::SharedPtr subscription_;
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);

  // 创建并运行
  rclcpp::spin(std::make_shared<CameraSub>());

  rclcpp::shutdown();
  return 0;
}

4)配置
修改CMakeLists.txt
添加引用

find_package(rclcpp REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(OpenCV REQUIRED)
find_package(cv_bridge REQUIRED)

生成可执行文件(使用对应的源码文件和依赖)

add_executable(camera_sub src/camera_sub.cpp)
ament_target_dependencies(camera_sub rclcpp sensor_msgs OpenCV cv_bridge)

添加安装规则

install(TARGETS
  camera_sub  
  DESTINATION lib/${PROJECT_NAME})

完整CM艾克List.txt如下

cmake_minimum_required(VERSION 3.8)
project(cpp_camera)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)

find_package(rclcpp REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(OpenCV REQUIRED)
find_package(cv_bridge REQUIRED)

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  set(ament_cmake_copyright_FOUND TRUE)
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

add_executable(camera_sub src/camera_sub.cpp)
ament_target_dependencies(camera_sub rclcpp sensor_msgs OpenCV cv_bridge)

install(TARGETS
  camera_sub  
  DESTINATION lib/${PROJECT_NAME})

ament_package()

修改package.xml,添加依赖

  <depend>rclcppdepend>
  <depend>OpenCVdepend>
  <depend>sensor_msgsdepend>
  <depend>cv_bridgedepend>

5)编译
注意:在项目根目录中编译

cd ~/ros/eg
colcon build

6)运行
在终端1中启动摄像头

ros2 run usb_cam usb_cam_node_exe --ros-args --params-file /opt/ros/humble/share/usb_cam/config/params.yaml

在终端2中启动测试程序

cd ~/ros/eg
source install/setup.sh
ros2 run cpp_camera camera_sub

5、话题消息

5.1 原始未压缩图像 ImageRaw

1)python数据描述
class Image_

    _fields_and_field_types = {
        'header': 	'std_msgs/Header',	// 话题消息队列头
        'height': 	'uint32',			// 图像高(单位像素)
        'width': 	'uint32',			// 图像宽(单位像素)
        'encoding':	'string',			// 像素格式,如“bgr8”
        'is_bigendian': 'uint8',		// 数据大小端字节序
        'step': 	'uint32',			// 步长(图像宽,单位字节)
        'data': 	'sequence',	// 图像数据
    }

2)C++中的描述
C++中encoding(像素格式)定义在名字空间 sensor_msgs::image_encodings 中,如:

namespace sensor_msgs
{
namespace image_encodings
{
	const char RGB8[] = "rgb8";
	const char RGBA8[] = "rgba8";
	const char RGB16[] = "rgb16";
	……

sensor_msgs::msg::Image_

this->header 	= const std_msgs::msg::Header_……
this->height 	= 0ul;
this->width 	= 0ul;
this->encoding 	= "";
this->is_bigendian = 0;
this->step 		= 0ul;
this->data 		= std::vector<uint8_t……

5.2 压缩图像 CompressedImage

压缩格式支持:jpeg、png、tiff
1)python

_fields_and_field_types = {
    'header': 'std_msgs/Header',
    'format': 'string',
    'data': 'sequence',
}

2)C++

this->header	= const std_msgs::msg::Header_……
this->format	= ""; // 可选值:jpeg、png、tiff
this->data		= std::vector<uint8_t……

你可能感兴趣的:(ROS,ROS2)