话题通信接口的定义和运行效果

描述

话题接口的定义与使用

话题通信接口的定义也是类似的,继续从之前的机器视觉案例中来衍生,我们想把服务换成话题,周期发布目标识别的位置,不管有没有人需要。

通信接口

运行效果

现在我们会运行三个节点:

第一个节点,将驱动相机并发布图像话题,此时的话题数据使用的是ROS中标准定义的Image图像消息;

第二个节点,会运行视觉识别功能,识别目标的位置,这个位置我们希望封装成话题消息,发布出去,谁需要使用谁就来订阅;

第三个节点,订阅位置话题,打印到终端中。

启动三个终端,分别运行以上节点:

$ ros2 run usb_cam usb_cam_node_exe$ ros2 run learning_topic interface_object_pub$ ros2 run learning_topic interface_object_sub

通信接口

接口定义

在这个例程中,我们使用ObjectPosition.msg定义了服务通信的接口:

learning_interface/msg/ObjectPosition.msg

int32 x      # 表示目标的X坐标int32 y      # 表示目标的Y坐标

话题消息的内容是一个位置,我们使用x、y坐标值进行描述。

完成定义后,还需要在功能包的CMakeLists.txt中配置编译选项,让编译器在编译过程中,根据接口定义,自动生成不同语言的代码:

...find_package(rosidl_default_generators REQUIRED)rosidl_generate_interfaces(${PROJECT_NAME}  "msg/ObjectPosition.msg")...

程序调用

我们在代码中再来重点看下接口的使用方法。

发布者接口调用

learning_topic/interface_object_pub.py

#!/usr/bin/env python3# -*- coding: utf-8 -*-"""@作者: 古月居@说明: ROS2接口示例-发布目标位置"""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图像处理库import numpy as np                                 # Python数值计算库from learning_interface.msg import ObjectPosition  # 自定义的目标位置消息lower_red = np.array([0, 90, 128])                 # 红色的HSV阈值下限upper_red = np.array([180, 255, 255])              # 红色的HSV阈值上限"""创建一个订阅者节点"""class ImageSubscriber(Node):    def __init__(self, name):        super().__init__(name)                                  # ROS2节点父类初始化        self.sub = self.create_subscription(            Image, 'image_raw', self.listener_callback, 10)     # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)        self.pub = self.create_publisher(            ObjectPosition, "object_position", 10)              # 创建发布者对象(消息类型、话题名、队列长度)        self.cv_bridge = CvBridge()                             # 创建一个图像转换对象,用于OpenCV图像与ROS的图像消息的互相转换        self.objectX = 0        self.objectY = 0       def object_detect(self, image):              hsv_img = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)        # 图像从BGR颜色模型转换为HSV模型        mask_red = cv2.inRange(hsv_img, lower_red, upper_red)   # 图像二值化        contours, hierarchy = cv2.findContours(            mask_red, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)     # 图像中轮廓检测        for cnt in contours:                                    # 去除一些轮廓面积太小的噪声            if cnt.shape[0] < 150:                continue            (x, y, w, h) = cv2.boundingRect(cnt)                # 得到苹果所在轮廓的左上角xy像素坐标及轮廓范围的宽和高            cv2.drawContours(image, [cnt], -1, (0, 255, 0), 2)  # 将苹果的轮廓勾勒出来            cv2.circle(image, (int(x+w/2), int(y+h/2)), 5,      # 将苹果的图像中心点画出来                       (0, 255, 0), -1)               self.objectX = int(x+w/2)            self.objectY = int(y+h/2)        cv2.imshow("object", image)                             # 使用OpenCV显示处理后的图像效果        cv2.waitKey(50)    def listener_callback(self, data):        self.get_logger().info('Receiving video frame')         # 输出日志信息,提示已进入回调函数        image = self.cv_bridge.imgmsg_to_cv2(data, 'bgr8')      # 将ROS的图像消息转化成OpenCV图像        position = ObjectPosition()        self.object_detect(image)                               # 苹果检测        position.x, position.y = int(self.objectX), int(self.objectY)        self.pub.publish(position)                              # 发布目标位置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接口

订阅者接口调用

learning_topic/interface_object_sub.py

#!/usr/bin/env python3# -*- coding: utf-8 -*-"""@作者: 古月居@说明: ROS2接口示例-订阅目标位置"""import rclpy                                       # ROS2 Python接口库from rclpy.node   import Node                      # ROS2 节点类from std_msgs.msg import String                    # 字符串消息类型from learning_interface.msg import ObjectPosition  # 自定义的目标位置消息"""创建一个订阅者节点"""class SubscriberNode(Node):    def __init__(self, name):        super().__init__(name)                                                # ROS2节点父类初始化        self.sub = self.create_subscription(            ObjectPosition, "/object_position", self.listener_callback, 10)   # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度    def listener_callback(self, msg):                                         # 创建回调函数,执行收到话题消息后对数据的处理        self.get_logger().info('Target Position: "(%d, %d)"' % (msg.x, msg.y))# 输出日志信息,提示订阅收到的话题消息def main(args=None):                                 # ROS2节点主入口main函数    rclpy.init(args=args)                            # ROS2 Python接口初始化    node = SubscriberNode("interface_position_sub")  # 创建ROS2节点对象并进行初始化    rclpy.spin(node)                                 # 循环等待ROS2退出    node.destroy_node()                              # 销毁节点对象    rclpy.shutdown()                                 # 关闭ROS2 Python接口

接口命令行操作

接口命令的常用操作如下:

$ ros2 interface list                    # 查看系统接口列表$ ros2 interface sh
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分