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:
- Programming implementation of Client and Server
- Definition of complex service data types and how to use data types defined by other function packages
- Image format conversion between opecv and ros
reference resources:
Gu Yueju's blog: https://www.guyuehome.com/34998
Video tutorial: https://www.bilibili.com/video/BV1zt411G7Vn
Gu Yueju blog: https://blog.csdn.net/qq_44284082/article/details/114226574
See for source code: https://download.csdn.net/download/qinqinxiansheng/21984240
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_servicecd ~/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 fileat / 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.txtconfigure 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 $ $ is used to connect the executable to the library it depends on.
find_package(OpenCV 3 REQUIRED) include_directories( ... $ ) catkin_package( CATKIN_DEPENDS cv_bridge roscpp rospy sensor_msgs std_msgs ) include_directories( $ $ ) add_executable(image_server src/image_sever.cpp) target_link_libraries(image_server $) add_executable(image_client src/image_client.cpp) target_link_libraries(image_client $ $ )
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 filein 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.
##Image.srv sensor_msgs/Image img --- string result
as shown in the figure:
2.2.2 configuration related documentsadd the function package dependency in the corresponding location of package.xml.
<build_depend>message_generation</build_depend> <exec_depend>message_runtime</exec_depend>
add compilation options in CMakeLists.txt
find_package(catkin REQUIRED COMPONENTS ... message_generation ) add_service_files(FILES Image.srv) generate_messages(DEPENDENCIES std_msgs sensor_msgs) catkin_package( ... message_runtime ) add_dependencies(rectify_server $_gencpp) add_dependencies(rectify_client $_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 $ _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.1 CMakeList.txt
cmake_minimum_required(VERSION 3.0.2) project(image_service) find_package(catkin REQUIRED COMPONENTS cv_bridge roscpp rospy sensor_msgs std_msgs message_generation ) find_package(OpenCV 3 REQUIRED) add_service_files(FILES Image.srv) generate_messages(DEPENDENCIES std_msgs sensor_msgs) catkin_package( # INCLUDE_DIRS include # LIBRARIES image_service CATKIN_DEPENDS cv_bridge roscpp rospy sensor_msgs std_msgs message_runtime # DEPENDS system_lib ) include_directories( $ $ ) add_executable(image_sever src/image_sever.cpp) target_link_libraries(image_sever $ $) add_dependencies(image_sever $_gencpp) add_executable(image_client src/image_client.cpp) target_link_libraries(image_client $ $ ) add_dependencies(image_client $_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::service::waitForService("/gimbal_image"); 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 image_client.call(srv); 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."); ros::spin(); return 0; }4 compilation and operation
4.1 compilation
cd ~/catkin_ws/src catkin_make source devel/setup.bash
4.2 operation
roscore rosrun image_service image_sever rosrun image_service image_client
the operation results are shown in the figure: