【ROS2笔记五】ROS2服务通信

ops/2024/9/23 18:27:49/

5.ROS2服务通信

文章目录

  • 5.ROS2服务通信
    • 5.1ROS2中的服务工具
    • 5.2 rclcpp实现服务通信
      • 5.2.1创建功能包和节点
      • 5.2.2服务端实现
      • 5.2.3客户端实现
    • Reference

服务通信也是ROS2中基本的通信方式,服务分为客户端和服务端,由客户端向服务端发送请求,然后服务端对客户端的请求进行处理,最后将处理后的结果返回给客户端,所以服务-客户端模型,可以称为请求-响应模型。

服务通信有一些需要注意的事项:

  • 同一个服务(名称相同)有且只能由一个节点来进行提供
  • 同一个服务可以被多个客户端调用。

服务通信的运行示例,如图所示:

Image

5.1ROS2中的服务工具

安装ROS2的时候提供了一些样例安装程序,其中就提供了服务通信的样例,我们可以直接调用

ros2 service -h

来查看相关的CLI工具。

5.2 rclcpp实现服务通信

5.2.1创建功能包和节点

这里我的工作空间名称为colcon_test03_ws

ros2 pkg create example_service_rclcpp --build-type ament_cmake --dependencies rclcpp --license Apache-2.0
touch example_service_rclcpp/src/service_server_01.cpp
touch example_service_rclcpp/src/service_client_01.cpp

然后分别编写如下代码:

service_server_01.cpp

#include "rclcpp/rclcpp.hpp"class ServiceServer01: public rclcpp::Node{
public:ServiceServer01(std::string name) : Node(name){RCLCPP_INFO(this->get_logger(), "Node: %s has been launched", name.c_str());}
private:};int main(int argc, char** argv){rclcpp::init(argc, argv);auto node = std::make_shared<ServiceServer01>("service_server_01");rclcpp::spin(node);rclcpp::shutdown();return 0;
}

service_client_01.cpp

#include "rclcpp/rclcpp.hpp"class ServiceClient01: public rclcpp::Node{
public:ServiceClient01(std::string name) : Node(name){RCLCPP_INFO(this->get_logger(), "Node: %s has been launched", name.c_str());}
};int main(int argc, char** argv){rclcpp::init(argc, argv);auto node = std::make_shared<ServiceClient01>("service_client_01");rclcpp::spin(node);rclcpp::shutdown();return 0;
}

然后修改配置文件CMakeLists.txt

add_executable(service_server_01 src/service_server_01.cpp)
ament_target_dependencies(service_server_01 rclcpp)add_executable(service_client_01 src/service_client_01.cpp)
ament_target_dependencies(service_client_01 rclcpp)install(TARGETSservice_server_01service_client_01DESTINATION lib/${PROJECT_NAME}
)

接着进行编译

colcon build --packages-select example_service_rclcpp

如果编译没问题就可以进行下一步了。

5.2.2服务端实现

(1)导入消息接口interface

这里使用的服务文件是内置的,并不是我们自己自定义的,在后续的文章中会介绍如何自定义自己的.msg.srv文件。

我们使用ROS2自带的example_interfaces接口,先查看一下这个接口的信息

ros2 interface show example_interfaces/srv/AddTwoInts 

结果如下:

int64 a
int64 b
---
int64 sum

可以看出该服务的接口接收两个int的数字ab,相加之后返回一个int类型的sum

ament_cmake类型的功能包导入消息接口分为三步:

  1. CMakeListts.txt中导入,具体是先find_packagesament_target_dependencies
  2. packages.xml中导入,具体是添加到depend标签并将消息接口写入
  3. 在代码中导入,Cpp中是#include "消息功能包/xxx/xxx.hpp"

具体操作如下:

CMakeLists.txt

find_package(example_interfaces REQUIRED)add_executable(service_server_01 src/service_server_01.cpp)
ament_target_dependencies(service_server_01 rclcpp example_interfaces)add_executable(service_client_01 src/service_client_01.cpp)
ament_target_dependencies(service_client_01 rclcpp example_interfaces)

packages.xml

<depend>example_interfaces</depend>

代码

#include "example_interfaces/srv/add_two_ints.hpp"

(2)编写服务端代码

相关API文档可以参考:https://docs.ros2.org/latest/api/rclcpp/

Image

我们需要设置的参数就三个,分别是:

  • ServiceT,是我们指定的消息数据接口,这里是example_interfaces::srv::AddTwoInts
  • service_name,是我们的服务名称
  • callback,是回调函数,使用成员函数作为回调函数,std::bind进行转换

编写服务端代码

#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"class ServiceServer01: public rclcpp::Node{
public:ServiceServer01(std::string name) : Node(name){RCLCPP_INFO(this->get_logger(), "Node: %s has been launched", name.c_str());// 创建服务add_ints_server_ = this->create_service<example_interfaces::srv::AddTwoInts>("add_two_ints_srv",std::bind(&ServiceServer01::handle_add_two_ints, this, std::placeholders::_1, std::placeholders::_2));}
private:// 在私有域中再次声明服务rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr add_ints_server_;// 服务的处理函数void handle_add_two_ints(const std::shared_ptr<example_interfaces::srv::AddTwoInts::Request> request,std::shared_ptr<example_interfaces::srv::AddTwoInts::Response> response){RCLCPP_INFO(this->get_logger(), "Recieve a: %ld b: %ld", request->a, request->b);response->sum = request->a + request->b;};};int main(int argc, char** argv){rclcpp::init(argc, argv);auto node = std::make_shared<ServiceServer01>("service_server_01");rclcpp::spin(node);rclcpp::shutdown();return 0;
}

5.2.3客户端实现

(1)相关API

相关API文档可以参考:https://docs.ros2.org/latest/api/rclcpp/

create_client

Image

一共有两个参数需要我们进行指定:

  • ServiceT,是我们指定的消息数据接口,这里是example_interfaces::srv::AddTwoInts
  • service_name,对应我们请求的服务名称

async_send_request

用于异步发送消息

Image
  • request,是用于请求的消息,这里存放两个数a,b
  • CallBack,回调函数,异步接受服务器的返回的函数

wait_for_service

Image

这个函数在等待服务上线之后返回true

(2)编写客户端程序

#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"class ServiceClient01: public rclcpp::Node{
public:ServiceClient01(std::string name) : Node(name){RCLCPP_INFO(this->get_logger(), "Node: %s has been launched", name.c_str());// 创建客户端client_ = this->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints_srv");}void send_request(int a, int b){RCLCPP_INFO(this->get_logger(), "Calculate %d + %d", a, b);// 等待服务上线while (!client_->wait_for_service(std::chrono::seconds(1))){if (!rclcpp::ok()){RCLCPP_ERROR(this->get_logger(), "Waiting for service to be interrupted");return;}RCLCPP_INFO(this->get_logger(), "Waiting for service");}auto request = std::make_shared<example_interfaces::srv::AddTwoInts_Request>();request->a = a;request->b = b;client_->async_send_request(request, std::bind(&ServiceClient01::result_callback_, this, std::placeholders::_1));}private:// 声明客户端rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr client_;void result_callback_(rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedFuture result_future){auto response = result_future.get();RCLCPP_INFO(this->get_logger(), "Result: %ld", response->sum);}
};int main(int argc, char** argv){rclcpp::init(argc, argv);auto node = std::make_shared<ServiceClient01>("service_client_01");// 调用服务node->send_request(5, 6);rclcpp::spin(node);rclcpp::shutdown();return 0;
}

这里需要讲解的就是这个回调函数

void result_callback_(rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedFuture result_future)

函数的参数是客户端AddTwoInts类型的SharedFuture对象,这个对象的定义如下:

Image

这个对象是利用c+11的新特性std::shared_future创建的SharedResponse类模板,类模板std::shared_future提供访问异步操作结果的机制,类似于std::future,允许多个线程共享同一个状态,详细可以查看std::shared_future的API

Image

可以使用其get()方法来获得结果。

最后编译然后运行

cd colcon_test_ws
colcon build --packages-select example_service_rclcpp
source install/setup.bash
ros2 run example_service_rclcpp service_client_01ros2 run example_service_rclcpp service_server_01

最后的运行结果如下:

Image

Reference

[1]d2lros2
[2]ROS2 Tutorial Official


http://www.ppmy.cn/ops/6026.html

相关文章

使用Python进行云计算:AWS、Azure、和Google Cloud的比较

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 使用Python进行云计算&#xff1a;AWS、Azure、和Google Cloud的比较 随着云计算的普及&am…

apache是什么

​Apache(音译为阿帕奇)是世界使用排名第一的Web服务器软件。它可以运行在几乎所有广泛使用的计算机平台上&#xff0c;由于其跨平台和安全性被广泛使用&#xff0c;是最流行的Web服务器端软件之一。它快速、可靠并且可通过简单的API扩充&#xff0c;将Perl/Python等解释器编译…

linux限权

shell命令以及运行原理 什么是shell命令&#xff1a; 将使用者的命令翻译给核心&#xff08;kernel&#xff09;处理。同时&#xff0c;将核心的处理结果翻译给使用者。 shell就相当于操作系统外的一层外壳 其实就是登录linux时的一个可执行程序&#xff08;进程&#xff09…

LeetCode刷题总结 | 图论3—并查集

并查集理论基础 1.背景 首先要知道并查集可以解决什么问题呢&#xff1f; 并查集常用来解决连通性问题。大白话就是当我们需要判断两个元素是否在同一个集合里的时候&#xff0c;我们就要想到用并查集。 并查集主要有两个功能&#xff1a; 将两个元素添加到一个集合中。判…

[论文笔记]Root Mean Square Layer Normalization

引言 今天带来论文Root Mean Square Layer Normalization的笔记&#xff0c;论文题目是均方根层归一化。 本篇工作提出了RMSNorm&#xff0c;认为可以省略重新居中步骤。 简介 层归一化对Transformer等模型非常重要&#xff0c;它可以帮助稳定训练并提升模型收敛性&#xf…

C++修炼之路之多态--多态的条件与例外,重载+重写+重定义

目录 前言 一&#xff1a;构成多态的条件及一些特殊情况&#xff08;前提是构成父子类&#xff09; 1.多态是在不同的继承关系的类对象&#xff0c;去调用同一函数&#xff0c;产生了不同的结果 2.两个条件 3.三同的两个例外 1.协变---返回值类型可以不同&#xff0c;但必…

深入了解 Gitea:轻量级的自托管 Git 服务

在软件开发和团队协作中&#xff0c;版本控制系统是不可或缺的工具。Git 是目前最流行的分布式版本控制系统之一&#xff0c;而 Gitea 则是基于 Git 的一个轻量级、自托管的 Git 服务。本文将介绍 Gitea 的特点、功能和使用方法&#xff0c;帮助读者更好地了解和使用这一工具。…

在nginx配置中返回文本

返回固定的文本&#xff1a; location ~ ^/get_text { default_type text/html; return 200 This is text!; } 返回固定的json: location ~ ^/get_json { default_type application/json; return 200 {"status":"success","result":"nginx…