ROS学习(2)( service and client)

当节点使用服务进行通信时,发送数据请求的节点称为客户端节点,响应请求的节点称为服务节点。请求和响应的结构由.srv文件确定。
这里使用的例子是一个简单的整数加法系统;一个节点请求两个整数之和,而另一个节点以结果响应。

创建对应服务功能包

ros2 pkg create --build-type ament_cmake cpp_srvcli --dependencies rclcpp

创建对应的消息

在当前的功能包的目录下进行创建,创建对应的srv文件,并在次文件的下进行服务文件的建立

int64 a
int64 b
---
int64 sum

建立了 对应的文件之后,需要针对建立的文件进行编译处理,修改对应的xml文件和CMakeLists.txt文件

修改xml文件

打开package.xml,并添加以下行:

<buildtool_depend>rosidl_default_generators</buildtool_depend>

<exec_depend>rosidl_default_runtime</exec_depend>

<member_of_group>rosidl_interface_packages</member_of_group>

注意,在构建时,我们需要rosidl_default_generators,而在运行时,我们只需要rosidl_default_runtime
打开CMakeLists.txt并添加以下行:

修改cmakeList文件

查找从msg/srv文件生成消息代码的包:

find_package(rosidl_default_generators REQUIRED)

声明要生成的消息列表:

set(msg_files
  "msg/AddressBook.msg"
)

通过手动添加.msg文件,我们可以确保CMake知道在您添加其他.msg文件后何时需要重新配置项目。

rosidl_generate_interfaces(${PROJECT_NAME}
  ${msg_files}
)

还要确保导出消息运行时依赖项:

ament_export_dependencies(rosidl_default_runtime)

服务类通信服务端

#include "rclcpp/rclcpp.hpp"
#include "cpp_srvcli/srv/add_two_ints.hpp"

#include <memory>
//服务端处理函数
void add(const std::shared_ptr<cpp_srvcli::srv::AddTwoInts::Request> request,
          std::shared_ptr<cpp_srvcli::srv::AddTwoInts::Response>      response)
{
  response->sum = request->a + request->b;
  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld",
                request->a, request->b);
  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum);
}

int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);

  std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_server");

  rclcpp::Service<cpp_srvcli::srv::AddTwoInts>::SharedPtr service =
    node->create_service<cpp_srvcli::srv::AddTwoInts>("add_two_ints", &add);

  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add two ints.");

  rclcpp::spin(node);
  rclcpp::shutdown();
}

服务类通信客户端


#include "rclcpp/rclcpp.hpp"

#include "cpp_srvcli/srv/add_two_ints.hpp"
#include <chrono>
#include <cstdlib>
#include <memory>

using namespace std::chrono_literals;

int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);

  if (argc != 3) {
      RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "usage: add_two_ints_client X Y");
      return 1;
  }

  std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_client");
  rclcpp::Client<cpp_srvcli::srv::AddTwoInts>::SharedPtr client =
    node->create_client<cpp_srvcli::srv::AddTwoInts>("add_two_ints");

  auto request = std::make_shared<cpp_srvcli::srv::AddTwoInts::Request>();
  request->a = atoll(argv[1]);
  request->b = atoll(argv[2]);

  while (!client->wait_for_service(1s)) {
    if (!rclcpp::ok()) {
      RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");
      return 0;
    }
    RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");
  }

  auto result = client->async_send_request(request);
  // Wait for the result.
  if (rclcpp::spin_until_future_complete(node, result) ==
    rclcpp::FutureReturnCode::SUCCESS)
  {
    RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Sum: %ld", result.get()->sum);
  } else {
    RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service add_two_ints");
  }

  rclcpp::shutdown();
  return 0;
}

代码解读:解析wait_for_service函数等待一个服务准备好,让客户端地址停止一段时间

/// Wait for a service to be ready.
  /**
   * \param timeout maximum time to wait
   * \return `true` if the service is ready and the timeout is not over, `false` otherwise
   */
  template<typename RepT = int64_t, typename RatioT = std::milli>
  bool
  wait_for_service(
    std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
  {
    return wait_for_service_nanoseconds(
      std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
    );
  }

同步发送对应的请求auto result = client->async_send_request(request);

 /// Handle a server response
  /**
    * \param[in] request_header used to check if the secuence number is valid
    * \param[in] response message with the server response
   */
handle_response(
    std::shared_ptr<rmw_request_id_t> request_header,
    std::shared_ptr<void> response) override
  {
    std::unique_lock<std::mutex> lock(pending_requests_mutex_);
    auto typed_response = std::static_pointer_cast<typename ServiceT::Response>(response);
    int64_t sequence_number = request_header->sequence_number;
    // TODO(esteve) this should throw instead since it is not expected to happen in the first place
    if (this->pending_requests_.count(sequence_number) == 0) {
      RCUTILS_LOG_ERROR_NAMED(
        "rclcpp",
        "Received invalid sequence number. Ignoring...");
      return;
    }
    auto tuple = this->pending_requests_[sequence_number];
    auto call_promise = std::get<0>(tuple);
    auto callback = std::get<1>(tuple);
    auto future = std::get<2>(tuple);
    this->pending_requests_.erase(sequence_number);
    // Unlock here to allow the service to be called recursively from one of its callbacks.
    lock.unlock();

    call_promise->set_value(typed_response);
    callback(future);
  }

  SharedFuture
  async_send_request(SharedRequest request)
  {
    return async_send_request(request, [](SharedFuture) {});
  }

rclcpp::spin_until_future_complete(node, result):得到对应的结果

spin_until_future_complete(
  std::shared_ptr<NodeT> node_ptr,
  const std::shared_future<FutureT> & future,
  std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
{
  return rclcpp::spin_until_future_complete(node_ptr->get_node_base_interface(), future, timeout);
}

配置CMakeLists.txt文件

add_executable(server src/add_two_ints_server.cpp)
ament_target_dependencies(server
  rclcpp )
rosidl_target_interfaces(server
  ${PROJECT_NAME} "rosidl_typesupport_cpp")


add_executable(client src/add_two_ints_client.cpp)
ament_target_dependencies(client
  rclcpp )
rosidl_target_interfaces(client
  ${PROJECT_NAME} "rosidl_typesupport_cpp")

install(TARGETS client server
  DESTINATION lib/${PROJECT_NAME}
)

关键,由于本功能包内使用了本包内定义的数据类型,包含了定义的头文件,需要针对其编译的CMakeLists.txt添加

rosidl_target_interfaces(client
#   ${PROJECT_NAME} "rosidl_typesupport_cpp")

保证正确引入对应的头文件,否则就会出现错误。

fatal error: cpp_srvcli/srv/add_two_ints.hpp: 没有那个文件或目录
   11 | #include "cpp_srvcli/srv/add_two_ints.hpp"
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
make[2]: *** [CMakeFiles/client.dir/build.make:76:CMakeFiles/client.dir/src/add_two_ints_client.cpp.o] 错误 1
make[1]: *** [CMakeFiles/Makefile2:696:CMakeFiles/client.dir/all] 错误 2
make: *** [Makefile:146:all] 错误 2
---
Failed   <<< cpp_srvcli [12.5s, exited with code 2]

Summary: 1 package finished [14.4s]
  1 package failed: cpp_srvcli
  1 package had stderr output: cpp_srvcli

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值