2.ROS机器视觉——ROS图像(imgmsg)与opencv(cv2)对接

参考:古月学院和ROS机器人开发实践

目标:实现ROS系统读取摄像头的图像,ROS读取的图像数据转化为opencv中的图像,opencv对接受的图像进行处理,最后返回给ROS系统可视化输出。

安装opencv库与相关的接口包

由于我用的ROS-Melodic版本,其中roscore只能在python2中执行,而视觉部分要在python3中执行,故将包安装在两个python中。(重要操作,因为其他有关于视觉的库,比如pytorch,是需要python3的,如果默认环境是python环境是python2,没把相应的包安装进python3,会报缺失依赖的错。)

(1条消息) ROS修改:ubuntu系统更改默认python版本(重要操作)_机械专业的计算机小白的博客-CSDN博客https://blog.csdn.net/wzfafabga/article/details/127191057?spm=1001.2014.3001.5501具体切换python版本方法在如上文章中。

sudo apt-get install ros-melodic-vision-opencv libopencv-dev ros-melodic-vision-opencv python-opencv

下载好相应的源码,放在工作空间的src中

guyueclass/planning&perception/robot_vision_beginner/robot_vision at main · guyuehome/guyueclass (github.com)https://github.com/guyuehome/guyueclass/tree/main/planning%26perception/robot_vision_beginner/robot_vision这个古月学院和ROS机器人开发实践的源码。

运行的代码

首先是usb_cam.launch

<launch>

  <node name="usb_cam" pkg="usb_cam" type="usb_cam_node" output="screen" >
    <param name="video_device" value="/dev/video0" />
    <param name="image_width" value="640" />
    <param name="image_height" value="480" />
    <param name="pixel_format" value="yuyv" />
    <param name="camera_frame_id" value="usb_cam" />
    <param name="io_method" value="mmap"/>
  </node>

</launch>

 创建一个节点,之后作为发布者,摄像头的图像信息,其中参数必须和摄像头参数对应,要不会报错。

其次是cv_bridge_test.py

 其中要修改python版本声明,因为molodic版本默认python版本是python2,但是现在视觉算法都是用的python3,在python3中才有实际意义。

修改了声明:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import rospy
import cv2
from cv_bridge import CvBridge, CvBridgeError
from sensor_msgs.msg import Image

class image_converter:
    def __init__(self):    
        # 创建cv_bridge,声明图像的发布者和订阅者
        self.image_pub = rospy.Publisher("cv_bridge_image", Image, queue_size=1)
        self.bridge = CvBridge()
        self.image_sub = rospy.Subscriber("/usb_cam/image_raw", Image, self.callback)

    def callback(self,data):
        # 使用cv_bridge将ROS的图像数据转换成OpenCV的图像格式
        try:
            cv_image = self.bridge.imgmsg_to_cv2(data, "bgr8")
        except CvBridgeError as e:
            print(e)

        # 在opencv的显示窗口中绘制一个圆,作为标记
        (rows,cols,channels) = cv_image.shape
        if cols > 60 and rows > 60 :
            cv2.circle(cv_image, (60, 60), 30, (0,0,255), -1)

        # 显示Opencv格式的图像
        cv2.imshow("Image window", cv_image)
        cv2.waitKey(3)

        # 再将opencv格式额数据转换成ros image格式的数据发布
        try:
            self.image_pub.publish(self.bridge.cv2_to_imgmsg(cv_image, "bgr8"))
        except CvBridgeError as e:
            print(e)

if __name__ == '__main__':
    try:
        # 初始化ros节点
        rospy.init_node("cv_bridge_test")
        rospy.loginfo("Starting cv_bridge_test node")
        image_converter()
        rospy.spin()
    except KeyboardInterrupt:
        print("Shutting down cv_bridge_test node.")
        cv2.destroyAllWindows()

根据最后通过rqt_graph生成的计算图来看,/usb_cam为通过launch文件生成的发布者节点,而/cv_bridge_test为if __name__ == '__main__':下初始化的节点cv_bridge_test作为订阅者。

import cv2
from cv_bridge import CvBridge, CvBridgeError
from sensor_msgs.msg import Image

cv2——opencv库

cv_bridge——通过调用此函数中的api进行ROS图像(imgmsg)与opencv图像(cv2)的转换。

sensor_msgs.msg——传感器数据类型,其中此处导入的是Image图像的数据类型。

        self.image_pub = rospy.Publisher("cv_bridge_image", Image, queue_size=1)
        self.bridge = CvBridge()
        self.image_sub = rospy.Subscriber("/usb_cam/image_raw", Image, self.callback)

根据上面的计算图:

首先是订阅者,作用是接收usb_cam发布的图像原始信息,话题名/usb_cam/image_raw,话题名这里不能改的,因为在话题通信中,发布者和订阅者的话题名必须一样才能进行通信。

其次是发布者,注意这个发布者并不是和上面的订阅者成套,因为二者话题名不同,意味着二者是无法进行话题通信的。其中发布者发布的话题cv_bridge_image,数据类型为Image,可以通过rqt_image_view命令观察到。

self.bridge = CvBridge()定义一个句柄,之后可以灵活调用相关转换接口。

    def callback(self,data):
        # 使用cv_bridge将ROS的图像数据转换成OpenCV的图像格式
        try:
            cv_image = self.bridge.imgmsg_to_cv2(data, "bgr8")
        except CvBridgeError as e:
            print(e)

        # 在opencv的显示窗口中绘制一个圆,作为标记
        (rows,cols,channels) = cv_image.shape
        if cols > 60 and rows > 60 :
            cv2.circle(cv_image, (60, 60), 30, (0,0,255), -1)

        # 显示Opencv格式的图像
        cv2.imshow("Image window", cv_image)
        cv2.waitKey(3)

        # 再将opencv格式额数据转换成ros image格式的数据发布
        try:
            self.image_pub.publish(self.bridge.cv2_to_imgmsg(cv_image, "bgr8"))
        except CvBridgeError as e:
            print(e)

这是回调函数,为话题通信订阅者的参数,其中通过最后一个参数调用了回调函数。

self.image_sub = rospy.Subscriber("/usb_cam/image_raw", Image, self.callback)

 同时在主函数中,不停的spin回头,达到不停的循环订阅的目的。

rospy.spin()

在回调函数中:

第一步,ROS原始图像信息转化为opencv中的图像信息。

cv_image = self.bridge.imgmsg_to_cv2(data, "bgr8")

第二步,在opencv中进行图像处理

        # 在opencv的显示窗口中绘制一个圆,作为标记
        (rows,cols,channels) = cv_image.shape
        if cols > 60 and rows > 60 :
            cv2.circle(cv_image, (60, 60), 30, (0,0,255), -1)

        # 显示Opencv格式的图像
        cv2.imshow("Image window", cv_image)
        cv2.waitKey(3)

第三步,opencv处理过的图像转化回为ROS图像信息,同时发布出去,发布后的图像信息可以通过ROS命令来查看。

self.image_pub.publish(self.bridge.cv2_to_imgmsg(cv_image, "bgr8"))

分析完成,运行代码

roslaunch robot_vision usb_cam.launch
rosrun robot_vision cv_bridge_test.py
rqt_image_view

其中第二部如果是ROS—melodic版本肯定会报错,当然可以通过修改python版本声明来解决,用python2运行,但是现在python2已经被淘汰了,即使运行成功也毫无意义。

ROS报错:ROS-Melodic中cv_bridge报错_机械专业的计算机小白的博客-CSDN博客icon-default.png?t=M85Bhttps://blog.csdn.net/wzfafabga/article/details/127239566?spm=1001.2014.3001.5502如上是解决这个问题的方法。

运行结果:

这是opencv输出的图像。

 这是转化后并发布出的图像,其中话题名为cv_bridge_image,这在代码中有迹可查,而且符合逻辑。

 这是原始图像,话题名为/usb_cam/image_raw,在代码中订阅者接收的图像数据类型消息。(rqt_image_view)

 这是计算图(rqt_graph),其中只看见了两个节点

猜你喜欢

转载自blog.csdn.net/wzfafabga/article/details/127237869