当前位置:网站首页>Ros2 - Service Service (IX)

Ros2 - Service Service (IX)

2022-07-05 06:51:00 Me and nano

ROS2 Robot operating system


Preface

Topic communication can realize multiple ROS One way transmission of data between nodes , Use this asynchronous communication mechanism , Publishers cannot accurately know whether subscribers receive messages , In this lecture, we will study together ROS Another common communication method —— service , It can achieve the synchronous communication effect similar to what you asked and answered .

One 、 Service details

client / Server model

From the perspective of service implementation mechanism , This form of asking and answering is called client / Server model , Referred to as CS Model , When the client needs some data , For a specific service , Send request information , After the server receives the request , It will process and feed back the response information .
 Insert picture description here
This communication mechanism is also very common in life , For example, we often browse various web pages , At this point, your computer browser is the client , Through the domain name or various operations , Send a request to the web server , After receiving it, the server returns the page data that needs to be displayed . This is the way that client-side rendering used when learning crawlers before .

Synchronous communication

This process generally requires that the faster the better , Suppose the server doesn't respond for half a day , Your browser keeps circling , It may be that the server is down , Or the network is bad , So compared with topic communication , In service communication , The client can receive the response information , Determine the state of the server , We also call it synchronous communication

One to many communication

For example, Gu Yueju website , The server is the only one , There are not many exactly the same Gu Yueju websites , But the client that can visit Gu Yueju website is not unique , Everyone can see the same interface . So in the service communication model , The server side is unique , But the client can not be unique .
 Insert picture description here

Service interface

Similar to topic communication , The core of service communication is still to transmit data , The data becomes two parts , A requested data , For example, the command to request the location of Apple , There is also a feedback data , For example, feedback the data of Apple coordinate position , These data are the same as topic news , stay ROS It is also defined in the standard , Topic use .msg File defines , The service USES .srv File defines

Two 、 Service cases

1. Addition solver

Now everyone is right ROS Service communication should have a basic understanding , Next we are going to start writing code . Or start with a relatively simple routine , It's also ROS An official routine , Realize the function of an addition solver through services .
 Insert picture description here

When we need to calculate the sum of two addends , Through the client node , Encapsulate two addends into request data , For services “add_two_ints” Send out , The server-side node that provides this service , After receiving the requested data , Start adding , And encapsulate the sum result into response data , Feedback to the client , Then the client can get the desired results .
Start two terminals , And run the following nodes , The first node is the server , Wait for the requested data and provide the summation function , The second node is the client , Send the two addends passed in and wait for the sum result .

ros2 run learning_service service_adder_server
ros2 run learning_service service_adder_client 2 3

 Insert picture description here
Server code implementation

import rclpy                                     # ROS2 Python Interface library 
from rclpy.node   import Node                    # ROS2  Node class 
from learning_interface.srv import AddTwoInts    #  Customized service interface 

class adderServer(Node):
    def __init__(self, name):
        super().__init__(name)                                                           # ROS2 Node parent class initialization 
        self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.adder_callback)  #  Create a server object ( Interface type 、 service name 、 Server callback function )

    def adder_callback(self, request, response):   #  Create callback function , Execute the processing of data after receiving the request 
        response.sum = request.a + request.b       #  Complete the addition and sum calculation , Put the results into the feedback data 
        self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b))   #  Output log information , Prompt that the addition and summation calculation has been completed 
        return response                          #  Feedback and response information 

def main(args=None):                             # ROS2 Node main entrance main function 
    rclpy.init(args=args)                        # ROS2 Python Interface initialization 
    node = adderServer("service_adder_server")   #  establish ROS2 Node object and initialize 
    rclpy.spin(node)                             #  Loop waiting for ROS2 sign out 
    node.destroy_node()                          #  Destroy the node object 
    rclpy.shutdown()                             #  close ROS2 Python Interface 

Client code

import sys

import rclpy                                  # ROS2 Python Interface library 
from rclpy.node   import Node                 # ROS2  Node class 
from learning_interface.srv import AddTwoInts #  Customized service interface 

class adderClient(Node):
    def __init__(self, name):
        super().__init__(name)                                       # ROS2 Node parent class initialization 
        self.client = self.create_client(AddTwoInts, 'add_two_ints') #  Create a service client object ( Service interface type , service name )
        while not self.client.wait_for_service(timeout_sec=1.0):     #  Wait for the server to start successfully 
            self.get_logger().info('service not available, waiting again...') 
        self.request = AddTwoInts.Request()                          #  Create the data object of the service request 

    def send_request(self):                                          #  Create a function to send service requests 
        self.request.a = int(sys.argv[1])
        self.request.b = int(sys.argv[2])
        self.future = self.client.call_async(self.request)           #  Send service requests asynchronously 

def main(args=None):
    rclpy.init(args=args)                        # ROS2 Python Interface initialization 
    node = adderClient("service_adder_client")   #  establish ROS2 Node object and initialize 
    node.send_request()                          #  Send service request 

    while rclpy.ok():                            # ROS2 The system operates normally 
        rclpy.spin_once(node)                    #  Loop through the nodes once 

        if node.future.done():                   #  Whether the data processing is completed 
            try:
                response = node.future.result()  #  Receive feedback data from the server 
            except Exception as e:
                node.get_logger().info(
                    'Service call failed %r' % (e,))
            else:
                node.get_logger().info(          #  Print out the feedback received 
                    'Result of add_two_ints: for %d + %d = %d' % 
                    (node.request.a, node.request.b, response.sum))
            break

    node.destroy_node()                          #  Destroy the node object 
    rclpy.shutdown()                             #  close ROS2 Python Interface 

After writing the code, you need to set the compilation options of the function package , Let the system know Python Program entry , To open a feature pack setup.py file , Add the following entry point configuration :

    entry_points={
    
        'console_scripts': [
         'service_adder_client  = learning_service.service_adder_client:main',
        ],
    },

Process summary
Analyze the above procedures , If we want to achieve one A client , The process is as follows :

Programming interface initialization
Create nodes and initialize
Create client objects
Create and send request data
Wait for the server to answer the data
Destroy the node and close the interface

Analyze the above procedures , If we want to achieve one A server , The process is as follows :

Programming interface initialization
Create nodes and initialize
Create server-side objects
Service through callback function
Feed back the response result to the client
Destroy the node and close the interface

2. Machine vision recognition

All right. , The addition solver has been implemented , Think back to the visual recognition process we just mentioned , When we need to know the position of the target object , Through the mechanism of service communication , Isn't it more reasonable
 Insert picture description here
Running effect
Three nodes will appear :

Camera drive node , Publish image data ;
Visual recognition node , Subscribe to image data , And it integrates a server-side object , Be ready to provide the target location ;
Client node , We can think of it as a node for robot target tracking , When you need to move according to the target , Just send a request , Then get a current target location .
Start three terminals , Run the above three nodes respectively :

ros2 run usb_cam usb_cam_node_exe
ros2 run learning_service service_object_server
ros2 run learning_service service_object_client

 Insert picture description here

Client code parsing

import rclpy                                            # ROS2 Python Interface library 
from rclpy.node   import Node                           # ROS2  Node class 
from learning_interface.srv import GetObjectPosition    #  Customized service interface 

class objectClient(Node):
    def __init__(self, name):
        super().__init__(name)                          # ROS2 Node parent class initialization 
        self.client = self.create_client(GetObjectPosition, 'get_target_position')
        while not self.client.wait_for_service(timeout_sec=1.0):
            self.get_logger().info('service not available, waiting again...')
        self.request = GetObjectPosition.Request()

    def send_request(self):
        self.request.get = True
        self.future = self.client.call_async(self.request)

def main(args=None):
    rclpy.init(args=args)                             # ROS2 Python Interface initialization 
    node = objectClient("service_object_client")      #  establish ROS2 Node object and initialize 
    node.send_request()

    while rclpy.ok():
        rclpy.spin_once(node)

        if node.future.done():
            try:
                response = node.future.result()
            except Exception as e:
                node.get_logger().info(
                    'Service call failed %r' % (e,))
            else:
                node.get_logger().info(
                    'Result of object position:\n x: %d y: %d' %
                    (response.x, response.y))
            break
    node.destroy_node()                              #  Destroy the node object 
    rclpy.shutdown()                                 #  close ROS2 Python Interface 

Server code parsing

import rclpy                                           # ROS2 Python Interface library 
from rclpy.node import Node                            # ROS2  Node class 
from sensor_msgs.msg import Image                      #  Image message type 
import numpy as np                                     # Python Numerical calculation library 
from cv_bridge import CvBridge                         # ROS And OpenCV Image conversion class 
import cv2                                             # Opencv Image processing library 
from learning_interface.srv import GetObjectPosition   #  Customized service interface 

lower_red = np.array([0, 90, 128])     #  Red HSV Lower threshold 
upper_red = np.array([180, 255, 255])  #  Red HSV Upper threshold 

class ImageSubscriber(Node):
    def __init__(self, name):
        super().__init__(name)                              # ROS2 Node parent class initialization 
        self.sub = self.create_subscription(
            Image, 'image_raw', self.listener_callback, 10) #  Create subscriber object ( Message type 、 Topic name 、 Subscriber callback function 、 The queue length )
        self.cv_bridge = CvBridge()                         #  Create an image conversion object , be used for OpenCV Image and ROS Mutual conversion of image messages of 

        self.srv = self.create_service(GetObjectPosition,   #  Create a server object ( Interface type 、 service name 、 Server callback function )
                                       'get_target_position',
                                       self.object_position_callback)    
        self.objectX = 0
        self.objectY = 0                              

    def object_detect(self, image):
        hsv_img = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)      #  Images from BGR The color model is converted to HSV Model 
        mask_red = cv2.inRange(hsv_img, lower_red, upper_red) #  Image binarization 
        contours, hierarchy = cv2.findContours(
            mask_red, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)   #  Contour detection in image 

        for cnt in contours:                                  #  Remove some noise with too small contour area 
            if cnt.shape[0] < 150:
                continue

            (x, y, w, h) = cv2.boundingRect(cnt)              #  Get the upper left corner of the outline of the apple xy Pixel coordinates and the width and height of the contour range 
            cv2.drawContours(image, [cnt], -1, (0, 255, 0), 2)#  Outline the apple 
            cv2.circle(image, (int(x+w/2), int(y+h/2)), 5,
                       (0, 255, 0), -1)                       #  Draw the center point of the apple image 

            self.objectX = int(x+w/2)
            self.objectY = int(y+h/2)

        cv2.imshow("object", image)                            #  Use OpenCV Display the processed image effect 
        cv2.waitKey(50)

    def listener_callback(self, data):
        self.get_logger().info('Receiving video frame')        #  Output log information , Prompt that the callback function has been entered 
        image = self.cv_bridge.imgmsg_to_cv2(data, 'bgr8')     #  take ROS The image message is transformed into OpenCV Images 
        self.object_detect(image)                              #  Apple detection 

    def object_position_callback(self, request, response):     #  Create callback function , Execute the processing of data after receiving the request 
        if request.get == True:
            response.x = self.objectX                          #  Target object XY coordinate 
            response.y = self.objectY
            self.get_logger().info('Object position\nx: %d y: %d' %
                                   (response.x, response.y))   #  Output log information , Prompt that feedback has been given 
        else:
            response.x = 0
            response.y = 0
            self.get_logger().info('Invalid command')          #  Output log information , Prompt that feedback has been given 
        return response


def main(args=None):                                 # ROS2 Node main entrance main function 
    rclpy.init(args=args)                            # ROS2 Python Interface initialization 
    node = ImageSubscriber("service_object_server")  #  establish ROS2 Node object and initialize 
    rclpy.spin(node)                                 #  Loop waiting for ROS2 sign out 
    node.destroy_node()                              #  Destroy the node object 
    rclpy.shutdown()                                 #  close ROS2 Python Interface 

3、 ... and 、 Service command line operation

$ ros2 service list                  #  View the list of services 
$ ros2 service type <service_name>   #  View service data types 
$ ros2 service call <service_name> <service_type> <service_data>   #  Send service request 

summary

Topics and services are ROS The two most commonly used data communication methods in , The former is suitable for sensors 、 Control instructions and other periodicity 、 Unidirectional data , The latter is suitable for asking and answering , Data with higher synchronization requirements , For example, get the target location recognized by machine vision .

In the process of robot development , Similar communication applications can be found everywhere ,ROS For most common scenarios , Standard topics and service data types are designed , Like image data 、 Radar data 、 Odometer data, etc , However, the software and hardware of robots are complicated , Many times these standard definitions cannot meet our needs , This is the time , We are going to customize the communication interface .

原网站

版权声明
本文为[Me and nano]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/186/202207050635157773.html