1 简介:
网上有很多关于客户端、服务端创建,并定义服务数据类型的博文,但是其服务数据类型一般比较简单,在服务数据类型包含复杂数据类型,比如图像、点云时,相关介绍较少。本博文详细介绍了如何利用客户端-服务端机制,以服务消息发送图像。使用话题机制与此类似,需要注意的是,发布topic时前若干个数据可能会丢失,在数据量较少或者数据比较重要是最好还是选择客户端-服务端机制。涉及内容包括:
- 1.客户端Client、服务端Server的编程实现
- 2.复杂服务数据类型的定义,以及如何使用其他功能包定义的数据类型
- 3.opecv与ros之间图像格式的转换
参考:
视频教程:https://www.bilibili.com/video/BV1zt411G7Vn
古月居博文:https://blog.csdn.net/qq_44284082/article/details/114226574
源代码见:https://download.csdn.net/download/qinqinxiansheng/21984240
2 实现
2.1 创建服务端与客户端
首先创建一个简单的服务端与客户端,包括创建功能包与cpp文件、配置cmakelist。
2.1.1 创建功能包,取名为image_service
cd ~/catkin_ws/src
catkin_create_pkg image_service roscpp rospy std_msgs sensor_msgs cv_bridge
上述命令中,image_service为所创建的功能包的名称,后面为其所依赖的包。其中功能包sensor_msgs包括了所需要使用的图像数据类型 sensor_msgs::Image, 功能包cv_bridge用于opecv与ros之间图像格式的转换。
2.1.2 创建cpp程序文件
在/catkin_ws/src/image_service/src文件夹下,通过命令行执行创建cpp文件用于编写客户端和服务端的程序。
touch image_sever.cpp image_client.cpp
结果如图所示:
2.1.3 配置CMakeLists.txt
下面配置CMakeLists.txt,在合适的位置添加编译选项。这里配置相关cpp文件及其所依赖的库,已经实现了服务端与客户端的配置,需要说明的是这里没有涉及自定义的服务数据类型。具体的find_package (OpenCV 3 REQUIRED)用于查找所需要依赖OpenCV库,add_executable (image_client src/image_client.cpp)命令用于连接可执行文件与相应依赖的cpp文件,target_link_libraries (image_client ${catkin_LIBRARIES} ${OpenCV_LIBS} 用于连接可执行文件与其依赖的库。
find_package(OpenCV 3 REQUIRED)
include_directories( … ${OpenCV_INCLUDE_DIRS} )
catkin_package(
CATKIN_DEPENDS cv_bridge roscpp rospy sensor_msgs std_msgs
)
include_directories(
${OpenCV_INCLUDE_DIRS}
${catkin_INCLUDE_DIRS}
)
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} )
程序中用到了opencv库,所以要把opencv链接上去。配置完后CMakeLists.txt应是这样的。
2.2 定义服务消息
2.2.1 创建服务数据类型文件
在/catkin_ws/src/image_service文件夹下,通过命令行创建srv文件,并在srv文件中新建Image.srv的数据类型文件(注意Image首字母要大写)。
mkdir srv
cd ./srv
touch Image.srv
定义Image.srv的数据结构如下所示,其中—-表示申请与返回数据的分界线,上面是需要申请的数据(sensor_msgs::Image类型的图像img),下面是需要返回的数据(string类型的result)。需要注意的是ros中的基本数据类型与C++是有区别的,使用之前需要调查清楚,如果需要一些特殊数据类型需要引入相应的功能包。
##Image.srv
sensor_msgs/Image img
---
string result
如图:
2.2.2 配置相关文件
在package.xml相应位置添加添加功能包依赖。
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
在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 ${PROJECT_NAME}_gencpp)
add_dependencies(rectify_client ${PROJECT_NAME}_gencpp)
其中generate_messages(DEPENDENCIES std_msgs sensor_msgs)指明了 Image数据类型需要依赖 sensor_msgs包中的数据类型,使得程序可以识别Image.srv中的sensor_msgs/Image img;add_dependencies(rectify_client ${PROJECT_NAME}_gencpp)命令不能省略,否则rosrun时会找不到该功能包。配置完后CMakeLists.txt如图所示。
2.2.3 拓展:使用其他功能包定义的数据类型
有时候我们在一个功能包中定义了一种数据服务/消息的数据类型之后,在其他功能包中也想使用这个数据类型。具体实现时其实非常简单,已入数据类型所在的功能包及头文件即可。具体的:
a) 首先在CMakeLists.txt中find_package(catkin REQUIRED COMPONENTS )命令中添加相应的功能包
b) 然后在package.xml中按照相应的格式补充相应功能包(build_depend, build_export_depend, exec_depend)
c) 最后在程序的cpp文件中引入相应的头文件即可,比如对于Rectify_service包中的Rectify.srv数据类型所要添加的头文件为#include “Rectify_service::Rectify.h”
3 编写程序
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(
${OpenCV_INCLUDE_DIRS}
${catkin_INCLUDE_DIRS}
)
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
在客户端imange_client读取本地的图片,并生成服务数据image_service::Image srv,最后申请服务/gimbal_image。其中用opencv读取图片的格式为cv::Mat,需要用 sensor_msgs::ImagePtr msg = cv_bridge::CvImage(std_msgs::Header(), “bgr8”, img).toImageMsg();命令将其转换为ros的图像数据格式sensor_msgs::Image,才能封装进服务数据类型。
客户端申请服务后(srv.request),服务端返回相应的结果(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"//所定义的服务数据类型
int main(int argc,char **argv)
{
ros::init(argc,argv,"image_client");// 初始化ROS节点
ros::NodeHandle node;// 创建节点句柄
// 发现/gimbal_image服务后,创建一个服务客户端,连接名为/gimbal_image的service
ros::service::waitForService("/gimbal_image");
ros::ServiceClient image_client = node.serviceClient<image_service::Image>("/gimbal_image");
//读取并转换图片
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;
//初始化image_service::Image的请求数据
image_service::Image srv;
srv.request.img = *msg; //当前图片
// 请求服务调用
image_client.call(srv);
ROS_INFO("result:%s .", srv.response.result.c_str());
return 0;
}
3.3 image_sever.cpp
服务端image_sever接受客户端的请求,调用回调函数imageCallback处理服务数据image_service::Image::Request &req,并返回结果image_service::Image::Response &res。在回调函数里通过命令cv_bridge::CvImagePtr cv_ptr = cv_bridge::toCvCopy(req.img, sensor_msgs::image_encodings::TYPE_8UC3);将ros消息中的图像数据类型sensor_msgs::Image转换为opencv的数据类型cv::Mat,然后就可以用opencv对图像进行进一步的处理。
#include <ros/ros.h>
#include <cv_bridge/cv_bridge.h>
#include <opencv2/opencv.hpp>
#include "image_service/Image.h"//所定义的服务数据类型
// service回调函数,输入参数req,输出参数res
bool imageCallback(image_service::Image::Request &req, image_service::Image::Response &res)
{
//读取并转换图像格式
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";// 设置反馈数据,
return true;
}
int main(int argc,char **argv)
{
ros::init(argc,argv,"image_server");// ROS节点初始化
ros::NodeHandle n; // 创建节点句柄
ros::ServiceServer image_service = n.advertiseService("/gimbal_image",imageCallback);//创建回调函数
ROS_INFO("Image server is ready.");
ros::spin();
return 0;
}
4 编译运行
4.1 编译
cd ~/catkin_ws/src
catkin_make
source devel/setup.bash
4.2 运行
roscore
rosrun image_service image_sever
rosrun image_service image_client
运行结果如图所示:
评论(1)
您还未登录,请登录后发表或查看评论