ROS learning: defining complex service message types and passing images

1 Introduction:

  there are many blogs on the Internet about creating client and server and defining service data types, but the service data types are generally simple. When the service data types include complex data types, such as images and point clouds, there are few relevant introductions. This blog post describes in detail how to use the client server mechanism to send images as service messages. Using the topic mechanism is similar to this. It should be noted that the first several data may be lost when publishing topic. When the amount of data is small or the data is important, it is better to choose the client-server mechanism. The contents involved include:

  1. Programming implementation of Client and Server
  2. Definition of complex service data types and how to use data types defined by other function packages
  3. Image format conversion between opecv and ros
    reference resources:
    Gu Yueju's blog:
    Video tutorial:
    Gu Yueju blog:
    See for source code:

2 implementation

2.1 create server and client

  first, create a simple server and client, including creating function packages and cpp files, and configuring cmakelist.

2.1.1 create a function package named image_service

cd ~/catkin_ws/src
catkin_create_pkg image_service roscpp rospy std_msgs sensor_msgs  cv_bridge

  in the above command, image_service is the name of the created function package, followed by the package it depends on. Including function pack sensor_msgs includes the image data type sensor to be used_ Msgs:: image, Feature Pack cv_bridge is used for image format conversion between opecv and ros.

2.1.2 create cpp program file

  at / catkin_ ws/src/image_ In the service / SRC folder, execute the command line to create a cpp file for writing programs on the client and server.

touch image_sever.cpp image_client.cpp

  the results are shown in the figure:

2.1.3 configuring CMakeLists.txt

    configure CMakeLists.txt below and add compilation options at the appropriate location. The related CPP files and their dependent libraries are configured here, and the configuration of server and client has been realized. It should be noted that there is no custom service data type involved here. Specific find_package (OpenCV 3 REQUIRED) is used to find the required dependent OpenCV library, add_ The executable (image_client Src / image_client. CPP) command is used to connect the executable file with the corresponding dependent cpp file, target_link_libraries (image_client ${catkin_LIBRARIES} ${OpenCV_LIBS} is used to connect the executable to the library it depends on.

find_package(OpenCV 3 REQUIRED)
include_directories(  ...   ${OpenCV_INCLUDE_DIRS} )

CATKIN_DEPENDS cv_bridge roscpp rospy sensor_msgs std_msgs

add_executable(image_server src/image_sever.cpp)
target_link_libraries(image_server ${catkin_LIBRARIES})

add_executable(image_client src/image_client.cpp)
target_link_libraries(image_client ${catkin_LIBRARIES} ${OpenCV_LIBS} )

  the opencv library is used in the program, so you should link OpenCV. After configuration, CMakeLists.txt should be like this.

2.2 defining service messages

2.2.1 create service data type file

  in the / catkin_ws/src/image_service folder, create an SRV file through the command line, and create a new data type file of Image.srv in the SRV file (note that the initial letter of Image should be capitalized).

mkdir srv
cd ./srv
touch Image.srv

   define the data structure of Image.srv as follows, where - represents the boundary between the application and the returned data. The above is the data to be applied for (sensor_msgs::Image img), and the following is the data to be returned (string result) It should be noted that the basic data types in ros are different from those in C + +. Before use, you need to investigate clearly. If you need some special data types, you need to introduce corresponding function packages.

sensor_msgs/Image img

string result

  as shown in the figure:

2.2.2 configuration related documents

    add the function package dependency in the corresponding location of package.xml.


  add compilation options in CMakeLists.txt

find_package(catkin REQUIRED COMPONENTS

add_service_files(FILES Image.srv)
generate_messages(DEPENDENCIES std_msgs sensor_msgs)
catkin_package( ...   message_runtime )

add_dependencies(rectify_server ${PROJECT_NAME}_gencpp)

add_dependencies(rectify_client ${PROJECT_NAME}_gencpp)

  where generate_messages(DEPENDENCIES std_msgs sensor_msgs) indicates that the Image data type needs to depend on the data type in the sensor_msgs package, so that the program can recognize sensor_msgs/Image img; add_dependencies (correct_client ${project_name} _gencpp) in Image.srv The command cannot be omitted, otherwise the function package will not be found when rosrun. After configuration, CMakeLists.txt is as shown in the figure.

2.2.3 expansion: use data types defined by other function packages

   sometimes, after defining a data type of data service / message in a function package, we also want to use this data type in other function packages. The specific implementation is very simple. We can enter the function package and header file where the data type is located. Specifically:
  a) first find in CMakeLists.txt_ Add the corresponding function package to the package (catkin required components) command
  b) then supplement the corresponding function package (build_depend ent, build_export_depend ent, exec_depend ent) in package.xml according to the corresponding format
  c) finally, the corresponding header file can be introduced into the cpp file of the program, such as rectify_ The header file to be added for the retry.srv data type in the service package is #include "retry_service:: retry. H"

3 Programming

3.1 CMakeList.txt

cmake_minimum_required(VERSION 3.0.2)

find_package(catkin REQUIRED COMPONENTS

find_package(OpenCV 3 REQUIRED)

add_service_files(FILES Image.srv)
generate_messages(DEPENDENCIES std_msgs sensor_msgs)

#  INCLUDE_DIRS include
#  LIBRARIES image_service
  CATKIN_DEPENDS cv_bridge roscpp rospy sensor_msgs std_msgs message_runtime
#  DEPENDS system_lib


add_executable(image_sever src/image_sever.cpp)
target_link_libraries(image_sever ${catkin_LIBRARIES} ${OpenCV_LIBS})
add_dependencies(image_sever ${PROJECT_NAME}_gencpp)

add_executable(image_client src/image_client.cpp)
target_link_libraries(image_client ${catkin_LIBRARIES} ${OpenCV_LIBS} )
add_dependencies(image_client ${PROJECT_NAME}_gencpp)

3.2 imange_client.cpp

  image on client_ The client reads the local image and generates the service data image_service::Image srv, and finally apply for service / gimbal_image. The format of the picture read with opencv is cv::Mat, and sensor is required_ msgs::ImagePtr msg = cv_ bridge::CvImage(std_msgs::Header(), “bgr8”, img).toImageMsg(); The sensor command converts it to the image data format of ros_ Msgs:: image can be encapsulated into the service data type.
  after the client applies for the service (srv.request), the server returns the corresponding result (srv.response).

#include <ros/ros.h>
#include <cv_bridge/cv_bridge.h>
#include <opencv2/opencv.hpp>
#include <sensor_msgs/Image.h>  
#include <iostream>
#include "image_service/Image.h" / / the defined service data type

int main(int argc,char **argv)
    ros::init(argc,argv,"image_client");// Initialize ROS node
    ros::NodeHandle node;// Create node handle
	// Discovery / gimbal_ After the image service, create a service client with the connection name of / gimbal_image service
    ros::ServiceClient image_client = node.serviceClient<image_service::Image>("/gimbal_image"); 

    //Read and convert pictures
    cv::Mat img = cv::imread("test.JPG", CV_LOAD_IMAGE_COLOR);
    sensor_msgs::ImagePtr msg = cv_bridge::CvImage(std_msgs::Header(), "bgr8", img).toImageMsg();
    sensor_msgs::Image msg1 = *msg; 

    //Initialize image_ Request data for service:: image
    image_service::Image srv;
    srv.request.img = *msg; //Current picture
    // Request service call;
    ROS_INFO("result:%s .", srv.response.result.c_str());
    return 0;

3.3 image_sever.cpp

  server image_sever accepts the request from the client and calls the callback function imageCallback to process the service data image_ Service:: Image:: request &req and return the result image_ service::Image::Response &res. In the callback function, use the command cv_bridge::CvImagePtr cv_ptr = cv_bridge::toCvCopy(req.img, sensor_msgs::image_encodings::TYPE_8UC3); The image data type in the ros message is set to sensor_ Msgs:: image is converted to opencv data type cv::Mat, and then the image can be further processed with OpenCV.

#include <ros/ros.h>
#include <cv_bridge/cv_bridge.h>
#include <opencv2/opencv.hpp>
#include "image_service/Image.h" / / the defined service data type

// service callback function, input parameter req, output parameter res
bool imageCallback(image_service::Image::Request &req, image_service::Image::Response &res)
    //Read and convert image format
    cv_bridge::CvImagePtr cv_ptr = cv_bridge::toCvCopy(req.img,  sensor_msgs::image_encodings::TYPE_8UC3);
    cv::Mat CurrentImg  = cv_ptr->image;
    ROS_INFO("Recived image.");
    res.result = "ok";// Set feedback data,
    return true;

int main(int argc,char **argv)
    ros::init(argc,argv,"image_server");// ROS node initialization
    ros::NodeHandle n; // Create node handle
    ros::ServiceServer image_service = n.advertiseService("/gimbal_image",imageCallback);//Create callback function
    ROS_INFO("Image server is ready.");
    return 0;

4 compilation and operation

4.1 compilation

cd ~/catkin_ws/src
source devel/setup.bash

4.2 operation

rosrun image_service image_sever
rosrun image_service image_client

   the operation results are shown in the figure:

Tags: C++ ROS

Posted on Wed, 06 Oct 2021 21:53:32 -0400 by kidbrax