目录
- 通信流程
- 创建服务器端及客户端
- 新建服务通讯文件
- 修改service的xml及cmakelist
- CMakeLists.txt编辑 msg 相关配置
- 编译消息相关头文件
- 在cmakelist中包含头文件的路径
- 在service包下编写service.cpp
- 在client包下编写client.cpp
- 测试运行
- 查询服务的相关指令
- 列出目前的所有服务:
- 查询参数:
- 显示某包下的服务
- 显示服务消息的具体信息
服务通信是基于请求产生的通信。
通信流程
0.Server注册
Server 启动后,会通过RPC在 ROS Master 中注册自身信息,其中包含提供的服务的名称。ROS Master 会将节点的注册信息加入到注册表中。
1.Client注册
Client 启动后,也会通过RPC在 ROS Master 中注册自身信息,包含需要请求的服务的名称。ROS Master 会将节点的注册信息加入到注册表中。
2.ROS Master实现信息匹配
ROS Master 会根据注册表中的信息匹配Server和 Client,并通过 RPC 向 Client 发送 Server 的 TCP 地址信息。
3.Client发送请求
Client 根据步骤2 响应的信息,使用 TCP 与 Server 建立网络连接,并发送请求数据。
4.Server发送响应
Server 接收、解析请求的数据,并产生响应结果返回给 Client。
注意:
1.客户端请求被处理时,需要保证服务器已经启动;
2.服务端和客户端都可以存在多个。
创建服务器端及客户端
cd 到ws/src目录下:
catkin_create_pkg service std_msgs rospy roscpp
catkin_create_pkg client std_msgs rospy roscpp
新建服务通讯文件
在服务端的src目录下,新建srv文件夹,并在内新建mymessage.srv
# 客户端请求时发送的两个数字
string a
string b
#客户端发送与服务端响应,中间使用---进行隔开,这里是简单把两个string拼接在一起
---
# 服务器响应发送的数据
string ab
修改service的xml及cmakelist
package.xml中添加编译依赖与执行依赖
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
CMakeLists.txt编辑 msg 相关配置
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
需要加入 message_generation,必须有 std_msgs
#配置 srv 源文件
add_service_files(
FILES
AddInts.srv
)
生成消息时依赖于 std_msgs
generate_messages(
DEPENDENCIES
std_msgs
)
#执行时依赖
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES demo02_talker_listener
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
编译消息相关头文件
退回到ws目录,使用catkin_make --pkg service
编译出相关的三个头文件
在cmakelist中包含头文件的路径
新生成的.h文件位于devel/include文件夹下,在请求及响应方的cmakelist中添加:
include_directories(
# include
${catkin_INCLUDE_DIRS}
"/root/work/ws/devel/include/"
)
在service包下编写service.cpp
服务端的功能主要是将收到的两个string连接起来。
#include "ros/ros.h"
#include "service/mymessage.h"
#include <string>
// bool 返回值由于标志是否处理成功
bool doReq(service::mymessage::Request& req,
service::mymessage::Response& resp){
std::stringstream ss;
ss<<std::string(req.a)<<std::string(req.b);
ROS_INFO("拼好的字符串:%s",ss.str().c_str());
resp.ab = ss.str();
return true;
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
// 2.初始化 ROS 节点
ros::init(argc,argv,"testServer");
// 3.创建 ROS 句柄
ros::NodeHandle nh;
// 4.创建服务以及注册回调函数
ros::ServiceServer server = nh.advertiseService("mymessage",doReq);
ROS_INFO("服务已经启动....");
// 5.回调函数处理请求并产生响应
// 6.由于请求有多个,需要调用 ros::spin()
ros::spin();
return 0;
}
同时在cmakelist中:
add_executable(${PROJECT_NAME}_node src/service.cpp)
target_link_libraries(${PROJECT_NAME}_node
${catkin_LIBRARIES}
)
在service包下新增launch文件
<launch>
<node name="myservice" pkg="service" type="service_node" output="screen"/>
</launch>
在client包下编写client.cpp
// 1.包含头文件
#include "ros/ros.h"
#include "service/mymessage.h"
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
// argv[0]:指向程序名称 ./program 的字符串指针
// argv[1]:指向 arg1 的字符串指针
// argv[2]:指向 arg2 的字符串指针
// 调用时动态传值,如果通过 launch 的 args 传参,需要传递的参数个数 +3,//launch 传参(0-文件路径 1传入的参数 2传入的参数 3节点名称 4日志路径)
if (argc != 5)
{
ROS_INFO("argc=(%d)",argc);
ROS_ERROR("请提交两个字符串");
return 1;
}
// 2.初始化 ROS 节点
ros::init(argc,argv,"testClient");
// 3.创建 ROS 句柄
ros::NodeHandle nh;
// 4.创建 客户端 对象
ros::ServiceClient client = nh.serviceClient<service::mymessage>("mymessage");
//等待服务启动成功
//方式1
ros::service::waitForService("mymessage");
//方式2
// client.waitForExistence();
// 5.组织请求数据
service::mymessage ai;
//直接输入命令行的字符串
ai.request.a = argv[1];
ai.request.b = argv[2];
// 6.发送请求,返回 bool 值,标记是否成功
bool flag = client.call(ai);
// 7.处理响应
if (flag)
{
ROS_INFO("请求正常处理,响应结果:%s",ai.response.ab.c_str());
}
else
{
ROS_ERROR("请求处理失败....");
return 1;
}
return 0;
}
同时在cmakelist中:
add_executable(${PROJECT_NAME}_node src/client.cpp)
target_link_libraries(${PROJECT_NAME}_node
${catkin_LIBRARIES}
)
在client包下新增launch文件
<launch>
<arg name="a" default="empty"/>
<arg name="b" default="empty"/>
<node name="myclient" pkg="client" type="client_node" args="$(arg a) $(arg b)" output="screen"/>
</launch>
测试运行
source /root/work/ws/devel/setup.bash
roslaunch service start.launch
#通过如下命令行的形式传入参数,launch文件中使用$(arg a)引用参数的值
roslaunch client start.launch a:=abcd b:=5678
查询服务的相关指令
列出目前的所有服务:
rosservice list
得到服务列表:
/mymessage
/myservice/get_loggers
/myservice/set_logger_level
/rosout/get_loggers
/rosout/set_logger_level
查询参数:
rosservice args /mymessage
输出:
a b
显示某包下的服务
rossrv package service
得出:
service/mymessage
显示服务消息的具体信息
rossrv show mymessage
得出具体信息
[service/mymessage]:
string a
string b
---
string ab