[ROS2] --- service

简介: [ROS2] --- service

1 service介绍

1.1 service概念

话题通信是基于订阅/发布机制的,无论有没有订阅者,发布者都会周期发布数据,这种模式适合持续数据的收发,比如传感器数据。机器人系统中还有另外一些配置性质的数据,并不需要周期处理,此时就要用到另外一种ROS通信方式——服务(Service)。服务是基于客户端/服务器模型的通信机制,服务器端只在接收到客户端请求时才会提供反馈数据。

1.2 service特点

  • 同一个服务(名称相同)有且只能有一个节点来提供

  • 同一个服务可以被多个客户端调用(可以一对一也可一对多)

  • 同步通信
    这个过程一般要求越快越好,假设服务器半天没有反应,你的浏览器一直转圈圈,那有可能是服务器宕机了,或者是网络不好,所以相比话题通信,在服务通信中,客户端可以通过接收到的应答信息,判断服务器端的状态,我们也称之为同步通信。

2 自定义通信接口

和话题通信类似,服务通信的核心还是要传递数据,数据变成了两个部分,一个请求的数据,比如请求苹果位置的命令,还有一个反馈的数据,比如反馈苹果坐标位置的数据,这些数据和话题消息一样,在ROS中也是要标准定义的,话题使用.msg文件定义,服务使用的是.srv文件定义。

2.1 新建工作空间

mkdir -p dev_ws/src
cd dev_ws/src
ros2 pkg create custom_interface --build-type ament_cmake

2.2 编写srv接口文件

# 创建srv目录
cd  dev_ws/src/custom_interface
mkdir srv
touch CapitalFullName.srv

CapitalFullName.srv

string name
string surname
---
string capitalfullname

2.3 编译

colcon build

3 service代码实现

上面第二步已经实现了自定义接口,并在install目录生成了对应的头文件,我们下面的代码中可以直接引用这个自定义接口了。

3.1 新建工作空间

前面已经讲解过,这里不再赘述

这里创建工作空间名为learning03_service

3.2 server_node_class.cpp

/**
 * @file service_node_class.cpp
 *
 * @brief A basic ROS2 service server node with class implementation that gets two 
 *        strings as request and answer with a capitalized full string as response.
 *        It's necessary to use the custom message defined in the external
 *        package "Custom msg and srv"
 *        To call the service from a terminal use on a single line:
 *        ros2 service call /create_cap_full_name 
 *        custom_interface/srv/CapitalFullName "{name: x, surname: y}"
 *
 * @author Antonio Mauro Galiano
 * Contact: https://www.linkedin.com/in/antoniomaurogaliano/
 *
 */
#include "rclcpp/rclcpp.hpp"
#include "custom_interface/srv/capital_full_name.hpp"
#include <boost/algorithm/string.hpp>
class MyServiceNode : public rclcpp::Node
{
private:
  rclcpp::Service<custom_interface::srv::CapitalFullName>::SharedPtr service_;
  void ComposeFullName(const std::shared_ptr<custom_interface::srv::CapitalFullName::Request> request,
        std::shared_ptr<custom_interface::srv::CapitalFullName::Response> response);
public:
  MyServiceNode(std::string passedNodeName="VOID")
    : Node(passedNodeName)
  {
    RCLCPP_INFO(this->get_logger(), "I am ready to capitalize your full name");
    // like the subscriber class node it's needed the boost::bind to acces the member method 
    // with 2 placeholders to pass request and response to the callback
    service_ = this->create_service<custom_interface::srv::CapitalFullName>("create_cap_full_name", 
      std::bind(&MyServiceNode::ComposeFullName, this, std::placeholders::_1, std::placeholders::_2 ));
  }
  
};
// method to handle the client request and give back a response
// the service gets the name and surname and responses with a capitalized full name
void MyServiceNode::ComposeFullName(const std::shared_ptr<custom_interface::srv::CapitalFullName::Request> request,
        std::shared_ptr<custom_interface::srv::CapitalFullName::Response> response)
{
  std::string fullName = request->name + " " + request->surname;
  std::string capitalFullName = boost::to_upper_copy<std::string>(fullName);
  response->capitalfullname = capitalFullName;    
  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming name: %s" "\tIncoming surname: %s",
              request->name.c_str(), request->surname.c_str());
  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Sending back the capitalize full name: [%s]", response->capitalfullname.c_str());
}
int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);
  auto node = std::make_shared<MyServiceNode>("service_node");
  rclcpp::spin(node);   // the service starts to wait and manage requests
  rclcpp::shutdown();
}

3.3 client_node_class.cpp

/**
 * @file client_node_class.cpp
 *
 * @brief A basic ROS2 service client node with class implementation that asks the user 
 *        to input twostrings and gets back a capitalized full string from the server service.
 *        It's necessary to use the custom message defined in the external 
 *        package "custom_interface"
 *
 * @author Antonio Mauro Galiano
 * Contact: https://www.linkedin.com/in/antoniomaurogaliano/
 *
 */
#include "custom_interface/srv/capital_full_name.hpp"
#include "rclcpp/rclcpp.hpp"
using namespace std::chrono_literals;
class MyClientNode : public rclcpp::Node
{
private:
  const std::string name_;
  const std::string surname_;
  rclcpp::Client<custom_interface::srv::CapitalFullName>::SharedPtr client_;
  void ServerResponse();
public:
  MyClientNode(std::string passedNodeName="VOID", std::string passedName="VOID", std::string passedSurname="VOID")
    : Node(passedNodeName), name_(passedName), surname_(passedSurname)
  {
    client_ = this->create_client<custom_interface::srv::CapitalFullName>("create_cap_full_name");
    this->ServerResponse();
  }
  
};
void MyClientNode::ServerResponse()
{
  auto request =
    std::make_shared<custom_interface::srv::CapitalFullName::Request>();
  request->name = name_;
  request->surname = surname_;
  std::chrono::seconds myPause = 1s;
  while (!client_->wait_for_service(1s)) 
  {
    if (!rclcpp::ok()) 
    {
      RCLCPP_ERROR(rclcpp::get_logger("rclcpp"),
                   "Interrupted while waiting for the service. Exiting.");
    }
    RCLCPP_INFO(rclcpp::get_logger("rclcpp"),
                "SERVICE NOT AVAILABLE, waiting again...");
  }
  auto result = client_->async_send_request(request);
  // note that for the MyClientNode object it's used the get_node_base_interface
  // to allow the client spinning for a server response
  // it returns the needed rclcpp::node_interfaces::NodeBaseInterface::SharedPtr 
  if (rclcpp::spin_until_future_complete(this->get_node_base_interface(), result) ==
      rclcpp::FutureReturnCode::SUCCESS)
  {
    RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Capitalized full name: %s",
                result.get()->capitalfullname.c_str());
  } else
  {
    RCLCPP_ERROR(rclcpp::get_logger("rclcpp"),
                 "Failed to call service create_cap_full_name");
  }
}
int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);
  std::string name = "";
  std::string surname = "";
  std::cout << "Insert the name -> ";
  std::cin >> name;
  std::cout << "Insert the surname -> ";
  std::cin >> surname;
  auto node = std::make_shared<MyClientNode>("client_node", name, surname);
  rclcpp::shutdown();
}

3.4 CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(learning03_service)
# Default to C99
if(NOT CMAKE_C_STANDARD)
  set(CMAKE_C_STANDARD 99)
endif()
# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(custom_interface REQUIRED)
if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # uncomment the line when a copyright and license is not present in all source files
  #set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # uncomment the line when this package is not in a git repo
  #set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()
# add_executable(service_node src/service_node.cpp)
# ament_target_dependencies(service_node rclcpp custom_interface)
# add_executable(client_node src/client_node.cpp)
# ament_target_dependencies(client_node rclcpp custom_interface)
add_executable(client_node_class src/client_node_class.cpp)
ament_target_dependencies(client_node_class rclcpp custom_interface)
add_executable(service_node_class src/service_node_class.cpp)
ament_target_dependencies(service_node_class rclcpp custom_interface)
install(TARGETS
  #  service_node
  #  client_node
   client_node_class
   service_node_class
   DESTINATION lib/${PROJECT_NAME}
 )
ament_export_dependencies(rosidl_default_runtime)
ament_package()

3.5 package.xml

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>learning03_service</name>
  <version>0.0.0</version>
  <description>Example nodes to create service server and service client</description>
  <maintainer email="foo@foo.foo">Antonio Mauro Galiano</maintainer>
  <license>TODO: License declaration</license>
  <buildtool_depend>ament_cmake</buildtool_depend>
  <build_depend>rclcpp</build_depend>
  <exec_depend>rclcpp</exec_depend>
  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>
  <depend>custom_interface</depend>
  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

4 编译运行

# 编译
colcon build
# source环境变量
source install/setup.sh
# 运行publisher
ros2 run learning03_service client_node_class
# 运行subsriber
ros2 run learning03_service server_node_class

5 service常用指令

# 查看service列表
ros2 service list
# 查看所有service类型
ros2 service list -t
# 查看service类型
ros2 service type <service_name>
# 查看同一类型所有在运行的service
ros2 service find <service_type>
# 查看service内容
ros2 interface show <service_interface>
ros2 interface show <service_name>
# 调用service
ros2 service call <service_name> <service_type>


相关文章
|
API
ROS Service 相关API接口与命令行介绍
ROS Service 相关API接口与命令行介绍
208 0
|
4月前
|
机器人
ROS2教程 05 服务Service
本文是关于ROS2(机器人操作系统2)中服务(Service)机制的教程,介绍了服务与话题(Topic)的区别、ROS2服务的相关命令,包括列出服务、查找服务、获取服务类型和调用服务,并通过示例代码展示了如何创建服务端(Server)和客户端(Client),以及如何测试服务调用过程。
167 0
ROS2教程 05 服务Service
ROS学习-写一个简单的Service 和 Client
ROS学习-写一个简单的Service 和 Client
145 0
ROS service in roscpp
topic是ROS中的一种单向的异步通信方式。Service是一种请求-反馈的通信机制。请求的一方通常被称为客户端,提供服务的一方叫做服 务器端。 Service机制相比于Topic的不同之处在于: 1. 消息的传输是双向的,有反馈的,而不是单一的流向。 2. 消息往往不会以固定频率传输,不连续,而是在需要时才会向服务器发起请求。
ROS service in roscpp
|
C++ Python
ROS入门笔记(十一):编写与测试简单的Service和Client (Python)
ROS入门笔记(十一):编写与测试简单的Service和Client (Python)
538 0
ROS入门笔记(十一):编写与测试简单的Service和Client (Python)
|
2月前
|
Ubuntu 机器人 Linux
|
20天前
|
自动驾驶 安全 机器人
ROS2:从初识到深入,探索机器人操作系统的进化之路
前言 最近开始接触到基于DDS的这个系统,是在稚晖君的机器人项目中了解和认识到。于是便开始自己买书学习起来,感觉挺有意思的,但是只是单纯的看书籍,总会显得枯燥无味,于是自己又开始在网上找了一些视频教程结合书籍一起来看,便让我对ROS系统有了更深的认识和理解。 ROS的发展历程 ROS诞生于2007年的斯坦福大学,这是早期PR2机器人的原型,这个项目很快被一家商业公司Willow Garage看中,类似现在的风险投资一样,他们投了一大笔钱给这群年轻人,PR2机器人在资本的助推下成功诞生。 2010年,随着PR2机器人的发布,其中的软件正式确定了名称,就叫做机器人操作系统,Robot Op
59 14
|
1月前
|
XML 算法 自动驾驶
ROS进阶:使用URDF和Xacro构建差速轮式机器人模型
【11月更文挑战第7天】本篇文章介绍的是ROS高效进阶内容,使用URDF 语言(xml格式)做一个差速轮式机器人模型,并使用URDF的增强版xacro,对机器人模型文件进行二次优化。
|
1月前
|
自动驾驶 安全 机器人
ROS2:从初识到深入,探索机器人操作系统的进化之路
【11月更文挑战第4天】ROS2的学习过程和应用,介绍DDS系统的框架和知识。
|
7月前
|
传感器 人工智能 算法
ROS机器人操作系统
ROS机器人操作系统
182 1

推荐镜像

更多