当我们需要知道目标物体位置的时候,通过服务通信的机制,岂不是更加合理。

自定义消息

  1. bool get # 获取目标位置的指令
  2. ---
  3. int32 x # 目标的X坐标
  4. int32 y # 目标的Y坐标

client节点

  1. import rclpy # ROS2 Python接口库
  2. from rclpy.node import Node # ROS2 节点类
  3. from learning_interface.srv import GetObjectPosition # 自定义的服务接口
  4. class objectClient(Node):
  5. def __init__(self, name):
  6. super().__init__(name) # ROS2节点父类初始化
  7. self.client = self.create_client(GetObjectPosition, 'get_target_position')
  8. while not self.client.wait_for_service(timeout_sec=1.0):
  9. self.get_logger().info('service not available, waiting again...')
  10. self.request = GetObjectPosition.Request()
  11. def send_request(self):
  12. self.request.get = True
  13. self.future = self.client.call_async(self.request)
  14. def main(args=None):
  15. rclpy.init(args=args) # ROS2 Python接口初始化
  16. node = objectClient("service_object_client") # 创建ROS2节点对象并进行初始化
  17. node.send_request()
  18. while rclpy.ok():
  19. rclpy.spin_once(node)
  20. if node.future.done():
  21. try:
  22. response = node.future.result()
  23. except Exception as e:
  24. node.get_logger().info(
  25. 'Service call failed %r' % (e,))
  26. else:
  27. node.get_logger().info(
  28. 'Result of object position:\n x: %d y: %d' %
  29. (response.x, response.y))
  30. break
  31. node.destroy_node() # 销毁节点对象
  32. rclpy.shutdown() # 关闭ROS2 Python接口

server节点

  1. import rclpy # ROS2 Python接口库
  2. from rclpy.node import Node # ROS2 节点类
  3. from sensor_msgs.msg import Image # 图像消息类型
  4. import numpy as np # Python数值计算库
  5. from cv_bridge import CvBridge # ROS与OpenCV图像转换类
  6. import cv2 # Opencv图像处理库
  7. from learning_interface.srv import GetObjectPosition # 自定义的服务接口
  8. lower_red = np.array([0, 90, 128]) # 红色的HSV阈值下限
  9. upper_red = np.array([180, 255, 255]) # 红色的HSV阈值上限
  10. class ImageSubscriber(Node):
  11. def __init__(self, name):
  12. super().__init__(name) # ROS2节点父类初始化
  13. self.sub = self.create_subscription(
  14. Image, 'image_raw', self.listener_callback, 10) # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
  15. self.cv_bridge = CvBridge() # 创建一个图像转换对象,用于OpenCV图像与ROS的图像消息的互相转换
  16. self.srv = self.create_service(GetObjectPosition, # 创建服务器对象(接口类型、服务名、服务器回调函数)
  17. 'get_target_position',
  18. self.object_position_callback)
  19. self.objectX = 0
  20. self.objectY = 0
  21. def object_detect(self, image):
  22. hsv_img = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 图像从BGR颜色模型转换为HSV模型
  23. mask_red = cv2.inRange(hsv_img, lower_red, upper_red) # 图像二值化
  24. contours, hierarchy = cv2.findContours(
  25. mask_red, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) # 图像中轮廓检测
  26. for cnt in contours: # 去除一些轮廓面积太小的噪声
  27. if cnt.shape[0] < 150:
  28. continue
  29. (x, y, w, h) = cv2.boundingRect(cnt) # 得到苹果所在轮廓的左上角xy像素坐标及轮廓范围的宽和高
  30. cv2.drawContours(image, [cnt], -1, (0, 255, 0), 2) # 将苹果的轮廓勾勒出来
  31. cv2.circle(image, (int(x+w/2), int(y+h/2)), 5,
  32. (0, 255, 0), -1) # 将苹果的图像中心点画出来
  33. self.objectX = int(x+w/2)
  34. self.objectY = int(y+h/2)
  35. cv2.imshow("object", image) # 使用OpenCV显示处理后的图像效果
  36. cv2.waitKey(50)
  37. def listener_callback(self, data):
  38. self.get_logger().info('Receiving video frame') # 输出日志信息,提示已进入回调函数
  39. image = self.cv_bridge.imgmsg_to_cv2(data, 'bgr8') # 将ROS的图像消息转化成OpenCV图像
  40. self.object_detect(image) # 苹果检测
  41. def object_position_callback(self, request, response): # 创建回调函数,执行收到请求后对数据的处理
  42. if request.get == True:
  43. response.x = self.objectX # 目标物体的XY坐标
  44. response.y = self.objectY
  45. self.get_logger().info('Object position\nx: %d y: %d' %
  46. (response.x, response.y)) # 输出日志信息,提示已经反馈
  47. else:
  48. response.x = 0
  49. response.y = 0
  50. self.get_logger().info('Invalid command') # 输出日志信息,提示已经反馈
  51. return response
  52. def main(args=None): # ROS2节点主入口main函数
  53. rclpy.init(args=args) # ROS2 Python接口初始化
  54. node = ImageSubscriber("service_object_server") # 创建ROS2节点对象并进行初始化
  55. rclpy.spin(node) # 循环等待ROS2退出
  56. node.destroy_node() # 销毁节点对象
  57. rclpy.shutdown() # 关闭ROS2 Python接口