< >
Home » ROS与C++入门教程 » ROS与C++入门教程-服务

ROS与C++入门教程-服务

ROS与C++入门教程-服务

说明:

  • 介绍服务及使用

服务定义、请求消息和响应消息

  • 查阅消息定义
  • ROS服务由SRV文件中定义,其中包含一个请求消息和响应消息。
  • 这些都是用与ROS的话题信息相同
  • roscpp将这些SRV文件转化为C++源代码和创建三个类,你需要熟悉:服务的定义,请求消息和响应消息。
  • 这些类别的名称直接来自SRV文件名:
my_package/srv/Foo.srv →
    my_package::Foo
    my_package::Foo::Request
    my_package::Foo::Response

生成的结构:

  • roscpp服务生成器生成结构类似:
namespace my_package
{
struct Foo
{
  class Request
  {
    ...
  };

  class Response
  {
    ...
  };

  Request request;
  Response response;
}; 
}
  • Request类提供作为输入到服务,Response类提供作为服务的输出

创建服务的请求/反馈

  • 任何服务调用的方法两个版本:
    • 一个Foo struct:
my_package::Foo foo;
foo.request.<var> = <value>;
...
<call>(foo); 
  • 一个独立对象:
my_package::Foo::Request req;
my_package::Foo::Response res;
req.<var> = <value>;
...
<call>(req, res);

调用服务

//remember that ros::init(...) must have been called
my_package::Foo foo;
...
if (ros::service::call("my_service_name", foo))
{
  ...
}
  • ros::service命名空间同样提供一些便利的函数如:exists() 和waitForService()
  • 查阅ros::service namespace API docs获取更多信息。
  • Handle 方法:
    • Handle方法返回ros::ServiceClient对象,用于调用服务
    • 示例代码:
ros::ServiceClient client = nh.serviceClient<my_package::Foo>("my_service_name");
my_package::Foo foo;
...
if (client.call(foo))
{
  ...
}

持久连接

  • ROS也支持服务的持久连接,在这种模式下,客户端能长期连接到服务端。
  • 否则客户端会每次执行查找和重连到服务端。
  • 这可能允许客户端每次访问服务调用时连接到一个不同的节点,假设查找返回一个不同的节点。
  • 持久连接应小心使用。它们大大提高了重复请求的性能,但也使您的客户对服务故障更加脆弱。
  • 如果持久连接失败,使用持久连接的客户端应该实现自己的重新连接逻辑。
  • 可以使用可选的第二个参数创建持久连接
  • ros::NodeHandle::serviceClient():
ros::ServiceClient client = nh.serviceClient<my_package::Foo>("my_service_name", true);
  • 注意:使用持久性服务,您可以通过测试句柄来判断连接是否失败:
if (client)
{
  ...
}
  • ros::ServiceClient句柄引用内部的计数,他们可以被复制,一旦最后的副本销毁,持久连接也会结束。
  • 也可以通过ros::ServiceClient::shutdown()手工关闭。

提供服务

(1)可选项

  • 针对不同回调函数,有不同版本的advertiseService(),一般形式为:
template<class MReq, class MRes>
ros::ServiceServer nh.advertiseService(const std::string& service, <callback>);
  • 函数说明:
    • MReq [通常不需要] ,模板参数,指定请求类型。多数不需要定义。
    • MRes [通常不需要] ,模板参数,指定反馈类型。多数不需要定义。
    • service,服务名称,
    • ,回调函数,当请求收到,调用处理请求的函数。

(2)回调函数的返回值:

  • 示例:
bool callback(MReq& request, MRes& response);
  • Req和MRes匹配提供给advertiseService()的request/response类型
  • 返回值为True代表服务成功,response会有相应的数据。
  • 返回值为false代表调用失败,response不会返回。

(3)回调函数类型

  • roscpp 使用任何支持boost::function的回调函数类型:
    • functions(函数)
    • class methods(类方法)
    • functor objects (包括boost::bind)

(4)函数作为回调函数

  • 示例:
bool callback(std_srvs::Empty::Request& request, std_srvs::Empty::Response& response)
{
  return true;
}

ros::ServiceServer service = nh.advertiseService("my_service", callback);

类方法作为回调函数

  • 示例:
bool Foo::callback(std_srvs::Empty::Request& request, std_srvs::Empty::Response& response)
{
  return true;
}

Foo foo_object;
ros::ServiceServer service = nh.advertiseService("my_service", &Foo::callback, &foo_object);

函数对象作为回调函数

  • 函数对象是一个类,用operator()来声明的。
  • 示例:
class Foo
{
public:
  bool operator()(std_srvs::Empty::Request& request, std_srvs::Empty::Response& response)
  {
    return true;
  }
};
  • functor 传递给advertiseService()必需是复制的。
  • Foo可用于advertiseService() 例如:
ros::ServiceServer srv = nh.advertiseService<std_srvs::Empty::Request, std_srvs::Empty::Response>("my_service", Foo());
  • 注意:当使用函数对象必须显式指定请求和响应类型作为模板参数,因为在这种情况下,编译器无法推断出他们。

服务连接的头信息

  • 连接头是ROS主题和ROS服务的一个特性,它可以在两个节点之间进行初始连接时发送额外的元数据。
  • ROS使用头信息来传递基本信息,例如连接上的客户端的callerid
  • 在服务端,这个功能可以定制,实现先进的功能,如“session”(即cookie)。
  • 服务客户端可以发送自己的附加元数据,例如与请求关联的标识符。
  • 在客户端,你能传递std::map<std::string, std::string>到ros::NodeHandle::serviceClient()作为第三个参数
std::map<std::string, std::string> header;
header["val1"] = "val";
header["val2"] = "val";
ros::ServiceClient client = nh.serviceClient<my_package::Foo>("my_service_name", false, header);

纠错,疑问,交流: 请进入讨论区点击加入Q群

获取最新文章: 扫一扫右上角的二维码加入“创客智造”公众号


标签: ROS与C++入门教程