我们将创建一个简单的service节点("add_two_ints_server"),该节点将接收到两个整形数字,并返回它们的和。
一、服务器和客户端简介
这一部分先空着,有空补。。
二、编程实现服务器和客户端
1.基础环境的搭建
创建工作空间,创建功能包(本次实验在之前的基础之上,默认使用之前的communication功能包)
2.定义srv文件
3.编写服务器节点
在communication包中创建src/add_two_ints_server.cpp文件,并复制粘贴下面的代码:
#include "ros/ros.h"
#include "communication/AddTwoInts.h"//编译系统自动根据我们先前创建的srv文件生成的对应该srv文件的头文件。
// service回调函数,输入参数req,输出参数res
bool add(communication::AddTwoInts::Request &req,
communication::AddTwoInts::Response &res)
{
// 将输入参数中的请求数据相加,结果放到应答变量中
res.sum = req.a + req.b;
//关于request和response的信息被记录下来
ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
ROS_INFO("sending back response: [%ld]", (long int)res.sum);
return true;
}
int main(int argc, char **argv)
{
// ROS节点初始化
ros::init(argc, argv, "add_two_ints_server");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个名为add_two_ints的server,注册回调函数add()
ros::ServiceServer service = n.advertiseService("add_two_ints", add);
// 循环等待回调函数
ROS_INFO("Ready to add two ints.");
ros::spin();
return 0;
}
4.编写客户端节点
同理,创建add_two_ints_client.cpp文件,并输入以下内容:
#include <cstdlib>
#include "ros/ros.h"
#include "communication/AddTwoInts.h"
int main(int argc, char **argv)
{
// ROS节点初始化
ros::init(argc, argv, "add_two_ints_client");
// 从终端命令行获取两个加数
if (argc != 3)
{
ROS_INFO("usage: add_two_ints_client X Y");
return 1;
}
// 创建节点句柄
ros::NodeHandle n;
// 创建一个client,请求add_two_int service,service消息类型是communication::AddTwoInts
ros::ServiceClient client = n.serviceClient<communication::AddTwoInts>("add_two_ints");
// 创建learning_communication::AddTwoInts类型的service消息
communication::AddTwoInts srv;
srv.request.a = atoll(argv[1]);//atoll Convert string to long long integer
srv.request.b = atoll(argv[2]);
// 发布service请求,等待加法运算的应答结果
//这段代码是在调用service。由于service的调用是模态过程(调用的时候占用进程阻止其他代码的执行),
//一旦调用完成,将返回调用结果。如果service调用成功,call()函数将返回true,srv.response里面的值将是合法的值。
//如果调用失败,call()函数将返回false,srv.response里面的值将是非法的。
if (client.call(srv))
{
ROS_INFO("Sum: %ld", (long int)srv.response.sum);
}
else
{
ROS_ERROR("Failed to call service add_two_ints");
return 1;
}
return 0;
}
5.编辑communication里面的CMakeLists.txt,添加
find_package(catkin REQUIRED COMPONENTS
std_msgs
roscpp
message_generation
)
## Generate services in the 'srv' folder
add_service_files(
FILES
AddTwoInts.srv
)
## Generate added messages and services with any dependencies listed here
generate_messages(
DEPENDENCIES
std_msgs
)
catkin_package(
INCLUDE_DIRS include
LIBRARIES communication
CATKIN_DEPENDS std_msgs roscpp message_runtime
DEPENDS system_lib
)
include_directories(
${catkin_INCLUDE_DIRS}
)
add_executable(add_two_ints_server src/add_two_ints_server.cpp)
add_executable(add_two_ints_client src/add_two_ints_client.cpp)
target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
add_dependencies(add_two_ints_client communication_gencpp)
add_dependencies(add_two_ints_server communication_gencpp)
6.在package.xml文件中添加功能包依赖
<build_depend>message_generation</build_depend>
<run_depend>message_runtime</run_depend>
note:上面两个文件修改不当会造成许多意想不到的错误,在找错的过程中发现一篇博客总结的不错,附上链接:
https://blog.csdn.net/wangjingqi930330/article/details/73967720
7.编译
在工作空间下执行catkin_make命令,根据错误提示找错误并且修正
三、运行
1.启动ros核:
exbot@ubuntu:~$ roscore
2.在另一个终端运行communication功能包的add_two_ints_server功能
exbot@ubuntu:~$ rosrun communication add_two_ints_server
3.运行客户端
exbot@ubuntu:~$ rosrun communication add_two_ints_client 3 5
客户端发送请求后,服务器段显示如下:
后记:
本次实验的过程中出现了许多小问题,从网上基本能找得到解决方案,但是碰到一个十分奇怪的问题,
AddTwoInts::Response has no menber named sum
如下图所示:
首先检查.srv文件有没有错误:
应该是没毛病。
其次检查有没有正确生成对应的头文件,
也没毛病。
这就奇怪了,忽然想到请求结构体没有报未知成员的错误,只有回应结构体报错,那我们来看看这两个头文件究竟差别在哪里?
首先看请求的:
再来看看回复的:
竟然是空的,问题就出在这里,我照着请求的结构改了下,
在编译之后就完美通过了。这个问题要不是ros本身的bug,要不就是我细节没有处理好,但我认为前者的概率大点。
ref:
http://wiki.ros.org/cn/ROS/Tutorials/WritingServiceClient%28c%2B%2B%29