零之前言
基于服务与基于话题的节点编写都差不多,所以内容和前一篇基本上很相似。
但服务与话题的通信机制不一样:
话题是我只管说,你要听就订阅。
服务是我想听你说,我(客户端)求你(服务端),你答应了,再给我说。
一.创建一个服务
先创建这次使用的功能包:
(对的, message_generation 对msg和srv都起作用)
然后创建服务:
其中服务的数据类型中有一个---
,这个以上,是请求(.request
)的数据类型; 这个以下,是响应(.response
)的数据类型。
修改CMakeList,取消注释即可:
add_service_files(
FILES
add.srv
)
generate_messages(
DEPENDENCIES
std_msgs
)
然后编译:
查看一下:
rossrv show add
服务创建完成
二.编写通信节点:
代码讲解在节点的编写逻辑与实现(C++/Python)
如果使用Python更推荐先阅读这一节:Python与C++编写节点的不同
1.服务端
在/src文件下创建server.cpp
#include "ros/ros.h"
#include "server/add.h"
bool add(server::add::Request &req,
server::add::Response &res)
{
res.sum = req.a + req.b;
ROS_INFO("Get request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
return true;
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "server");
ros::NodeHandle n;
ROS_INFO("The server is start running!");
ros::ServiceServer service = n.advertiseService("add", add);
ros::spin();
return 0;
}
2.客户端
在/src文件下创建client.cpp
#include "ros/ros.h"
#include "server/add.h"
#include <cstdlib>
int main(int argc, char **argv)
{
ros::init(argc, argv, "client");
ros::NodeHandle n;
ros::ServiceClient client = n.serviceClient<server::add>("add");
server::add srv;
ros::Rate loop_rate(1);
long int a,b;
while(ros::ok)
{
a = rand()%100;
b = rand()%100;
srv.request.a = a;
srv.request.b = b;
ROS_INFO("Send a request: %ld + %ld = ?", a, b);
if (client.call(srv))
{
ROS_INFO("Sum: %ld", (long int)srv.response.sum);
}
else
{
ROS_ERROR("Failed to call service add");
return 1;
}
loop_rate.sleep();
}
return 0;
}
3.修改配置,编译运行
CMakeList.txt
中添加:
add_executable(ser src/server.cpp)
target_link_libraries(ser ${catkin_LIBRARIES})
add_executable(cli src/client.cpp)
target_link_libraries(cli ${catkin_LIBRARIES})
然后编译运行
roscore
rosrun server ser
rosrun server cli
然后得到下图:
我们打开rqt_graph,也没有发现两者之间有话题。所以服务测试成功。
而且我们可以从代码看出以下流程:
- server闲置,client发起请求,并(阻塞)等待响应;
- server获得请求,处理请求,返回响应,闲置;
- client又获得响应,继续执行代码。
Nice,成功!