ROS service in roscpp

354 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情

1、service通信简介

topic是ROS中的一种单向的异步通信方式。Service是一种请求-反馈的通信机制。请求的一方通常被称为客户端,提供服务的一方叫做服 务器端。 Service机制相比于Topic的不同之处在于:

  1. 消息的传输是双向的,有反馈的,而不是单一的流向。
  2. 消息往往不会以固定频率传输,不连续,而是在需要时才会向服务器发起请求。

service包括两部分,一部分是请求方(Clinet),另 一部分是应答方/服务提供方(Server)。这时请求方(Client)就会发送一个request,要等 待server处理,反馈回一个reply,这样通过类似“请求-应答”的机制完成整个服务通信。

这种通信方式的示意图如下: Node B是server(应答方),提供了一个服务的接口,叫做 /Service ,我们一般都会用 string类型来指定service的名称,类似于topic。Node A向Node B发起了请求,经过处理后得 到了反馈。 在这里插入图片描述 过程

Service是同步通信方式,所谓同步就是说,此时Node A发布请求后会在原地等待reply,直到 Node B处理完了请求并且完成了reply,Node A才会继续执行。Node A等待过程中,是处于 阻塞状态的成通信。这样的通信模型没有频繁的消息传递,没有冲突与高系统资源的占用, 只有接受请求才执行服务,简单而且高效。

2、service demo 实例

(1)创建service_demo package

进入工作空间的src文件夹

$ catkin_create_pkg service_demo_jone roscpp rospy std_msgs
# catkin_create_pkg 是创建package的命令
#service_demo_jone 是package的名称
#roscpp rospy std_msgs 依赖项

此时会在src文件夹下生成service_demo_jone文件夹 下面会有自动生成的相应文件 在这里插入图片描述

(2)创建.srv文件

在service_demo_jone文件夹下创建srv文件夹,用于存放.srv文件。 创建一个文件Greeting.srv文件。 里面写入

string name   	#短横线上边部分是服务请求的数据   即客户端发给服务器的数据
int32 age
---           	#短横线下面是服务回传的内容。     即服务器回传给客户端的数据
string feedback

然后编译生成cpp可以include的.h文件 在 CMakeLissts.txt 中加入


find_package(catkin REQUIRED COMPONENTS  #需要找个整个函数添加相应的依赖
  roscpp
  rospy
  std_msgs
  message_generation  # 需要添加的地方  
)


add_service_files(
   FILES	
   Greeting.srv    #自己定义的.srv文件 
   )

 generate_messages(
   DEPENDENCIES
   std_msgs           #由于定义的文件中用到了string、int32 关节字 所以必须要引入该依赖项
 )

在package.xml文件中加入

  <build_depend>message_generation</build_depend>     <!-- #generate new msg must add-->
  <exec_depend>message_runtime</exec_depend>          <!-- #generate new msg must add-->

然后回到工作空间 进行 编译

$ catkin_make

编译完成后 会在在图片路径下生成如下文件 在这里插入图片描述 新生成的Greeting类型的服务,其结构体的风格更为明显,可以这么理解,一个Greeting服务 结构体中嵌套了两个结构体,分别是请求和响应:

struct	Greeting
 {				
    struct	Request			
      {								
         string	name;								
         int	age;				
      }request;				
   	struct	Response				
  	{								
       string	feedback;				
    }response; 
 }

(3)创建服务 提供节点 server.cpp

#include <ros/ros.h>
#include <service_demo_jone/Greeting.h>


//服务的处理函数
bool handle_function(service_demo_jone::Greeting::Request &req,service_demo_jone::Greeting::Response &res){  //用到service的参数的固定写法
      
      //从中可以拿到请求的数据 ,req.name  req.age
    ROS_INFO("Request from %s with age %d",req.name.c_str(),req.age );		//显示请求信息
 
    //赋值返回 变量
   res.feedback = "Hi!"+req.name+". I'm server!";//处理请求,结果写入response

    return true;	//返回true,正确处理了请求
}



int main(int argc, char** argv){ //主函数

    ros::init(argc, argv, "greeting_server");  //声明节点名称 greeting_server
    
    ros::NodeHandle nd;  	//创建句柄,实例化node

    ros::ServiceServer service=nd.advertiseService("greeting",handle_function);  	//写明服务的处理函数 handle_function  greeting是service的名称
  
    ros::spin();  //轮询 查询请求 有请求时 调用 服务的处理函数 handle_function

    return  0;
}

在以上代码中,服务的处理操作都写在 handle_function() 中,它的输入参数就是Greeting的 Request和Response两部分,而非整个Greeting对象。通常在处理函数中,我们对Requst数 据进行需要的操作,将结果写入到Response中。在roscpp中,处理函数返回值是bool型,也 就是服务是否成功执行。

(4)创建服务请求节点(client.cpp)


#include "ros/ros.h"
#include "service_demo_jone/Greeting.h"


int main(int argc, char **argv)
{
   ros::init(argc, argv, "greeting_client");//声明节点名称 greeting_client

   ros::NodeHandle nh;//创建句柄,实例化node

   ros::ServiceClient client = nh.serviceClient<service_demo_jone::Greeting>("greeting");	//	定义service客户端,service名字为“greeting”
   
 	//	实例化srv,设置其request消息的内容,这里request包含两个变量,name和age,见Greeting.srv
   service_demo_jone::Greeting srv;
   srv.request.name="jone";
   srv.request.age=27;

   if(client.call(srv))//调用服务。
   {	
   //	注意我们的response部分中的内容只包含一个变量feedback,另,注意将其转变成字符串
      ROS_INFO("Response from server : %s",srv.response.feedback.c_str());
   }
   else 
   {
         ROS_INFO("Failed to call service Service_demo_jone");
         return 1;
   }
    return 0;
}

(5)修改CMakeList.txt

add_executable(greeting_server src/server.cpp )
#必须添加add_dependencies,否则找不到自定义的msg产生的头文件
add_dependencies(greeting_server ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(greeting_server ${catkin_LIBRARIES})

add_executable(greeting_client src/client.cpp )
add_dependencies(greeting_client ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(greeting_client ${catkin_LIBRARIES})

(6)编译

回到工作空间

$ catkin_make

3、测试

启动ros

$ roscore

检测是否生成package

$ rospack list

在列表里有刚做的package 在这里插入图片描述 运行greeting_server节点

$ rosrun service_demo_jone greeting_server

运行greeting_client节点

$ rosrun service_demo_jone greeting_client

当运行greeting_client节点时 收到并打印服务器反馈的信息 在这里插入图片描述 同时greeting_server节点打印 服务器此时的信息 在这里插入图片描述 此时说明在ros中创建service服务成功。

查看此时服务列表

$ rosservice list

在这里插入图片描述 在这里插入图片描述 有在代码中创建的服务

查看该服务信息

$ rosservice info /greeting

在这里插入图片描述