话题通信接口的定义也是类似的,继续从之前的机器视觉案例中来衍生,我们想把服务换成话题,周期发布目标识别的位置,不管有没有人需要。
运行效果
现在我们会运行三个节点:
第一个节点,将驱动相机并发布图像话题,此时的话题数据使用的是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
-
接口
+关注
关注
33文章
8580浏览量
151046 -
相机
+关注
关注
4文章
1350浏览量
53591 -
通信接口
+关注
关注
3文章
237浏览量
30982 -
ROS
+关注
关注
1文章
278浏览量
17004
发布评论请先 登录
相关推荐
评论