Robot Operating System——自定义Service/Client通信消息结构

2024-08-24 10:36

本文主要是介绍Robot Operating System——自定义Service/Client通信消息结构,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

大纲

  • 初始化环境
  • 生成自定义服务的工程
    • 创建包
    • 自定义消息
    • package.xml
      • 完整文件
    • CMakeLists.txt
      • 完整文件
    • 编译
    • 注册
  • 使用自定义服务的工程
    • 创建包
    • 代码
    • CMakeLists.txt
    • 编译
    • 运行
  • 工程地址
  • 参考资料

在《Robot Operating System——自定义订阅/发布的消息结构》一文中,我们讲解了如何自定义消息结构。这个消息是发布者向订阅者发送的消息,具有单向性。但是Service/Client类型的消息是具有双向性的。Client向Service发送的是Request消息,Service向Client发送的是Response消息。

初始化环境

在《Robot Operating System——深度解析自动隐式加载动态库的运行模式》一文中,我们展现了ROS2可执行文件的链接指令。可以看到它依赖了很多ROS2环境相关的动态库,所以我们在创建工程之前也要初始化环境。

source /opt/ros/jazzy/setup.bash

关于环境的安装可以参见《Robot Operating System——Ubuntu上以二进制形式安装环境》。

生成自定义服务的工程

创建包

ros2 pkg create --build-type ament_cmake --license Apache-2.0 custom_service

在这里插入图片描述
其目录结构如下
在这里插入图片描述

自定义消息

我们创建一个目录srv,然后在其下新建一个文件NavSatPose.srv,填入下面的内容

sensor_msgs/NavSatStatus nav
---
geometry_msgs/PoseWithCovariance pose
std_msgs/Bool boolvalue

nav是Request消息结构体中的内容;pose和boolvalue是Response消息结构体中的内容。
在这里插入图片描述

package.xml

在该文件中新增如下内容

  <depend>sensor_msgs</depend><depend>geometry_msgs</depend><depend>std_msgs</depend><buildtool_depend>rosidl_default_generators</buildtool_depend><exec_depend>rosidl_default_runtime</exec_depend><member_of_group>rosidl_interface_packages</member_of_group>

sensor_msgs、geometry_msgs和std_msgs是消息体中用的三种ROS2提供的基础消息类型;
rosidl_default_generators用于将上述msg文件生成代码;
rosidl_default_runtime是运行时依赖;
后三者是一定要加的,前面的三个根据自定义消息的类型酌情添加。
此时的目录结构如下

完整文件

<?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>custom_service</name><version>0.0.0</version><description>TODO: Package description</description><maintainer email="f304646673@gmail.com">fangliang</maintainer><license>Apache-2.0</license><buildtool_depend>ament_cmake</buildtool_depend><depend>sensor_msgs</depend><depend>geometry_msgs</depend><depend>std_msgs</depend><buildtool_depend>rosidl_default_generators</buildtool_depend><exec_depend>rosidl_default_runtime</exec_depend><member_of_group>rosidl_interface_packages</member_of_group><test_depend>ament_lint_auto</test_depend><test_depend>ament_lint_common</test_depend><export><build_type>ament_cmake</build_type></export>
</package>

CMakeLists.txt

新增如下内容

find_package(sensor_msgs REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(std_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)rosidl_generate_interfaces(${PROJECT_NAME}"srv/NavSatPoseBool.srv"DEPENDENCIES sensor_msgs geometry_msgs std_msgs 
)

DEPENDENCIES 中添加的是我们自定义消息的基础类型。

完整文件

cmake_minimum_required(VERSION 3.8)
project(custom_service)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)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)find_package(sensor_msgs REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(std_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)rosidl_generate_interfaces(${PROJECT_NAME}"srv/NavSatPoseBool.srv"DEPENDENCIES sensor_msgs geometry_msgs std_msgs 
)if(BUILD_TESTING)find_package(ament_lint_auto REQUIRED)# the following line skips the linter which checks for copyrights# comment the line when a copyright and license is added to all source filesset(ament_cmake_copyright_FOUND TRUE)# the following line skips cpplint (only works in a git repo)# comment the line when this package is in a git repo and when# a copyright and license is added to all source filesset(ament_cmake_cpplint_FOUND TRUE)ament_lint_auto_find_test_dependencies()
endif()ament_package()

编译

colcon build --packages-select custom_service

在这里插入图片描述

注册

source install/setup.sh 

经过注册后,我们可以在ROS2中查看这个自定义服务

ros2 interface show custom_service/srv/NavSatPoseBool

在这里插入图片描述

使用自定义服务的工程

创建包

ros2 pkg create --build-type ament_cmake --license Apache-2.0 custom_service_nodes --dependencies rclcpp custom_service

在这里插入图片描述
–dependencies参数可以帮我们在package.xml和CMakeLists.txt中自动添加相应依赖。此时我们添加上一步创建的custom_service。
此时目录结构如下
在这里插入图片描述

代码

在src目录下新建server.cpp和client.cpp,分别填入以下内容

#include "rclcpp/rclcpp.hpp"
#include "custom_service/srv/nav_sat_pose_bool.hpp"#include <memory>
#include <string>
#include <sstream>std::string serializeRequest(const custom_service::srv::NavSatPoseBool::Request &request) {std::ostringstream oss;oss << request.nav.service << " " << request.nav.status << " ";return oss.str();
}std::string serializeResponse(const custom_service::srv::NavSatPoseBool::Response &response) {std::ostringstream oss;oss << response.pose.pose.position.x << " " << response.pose.pose.position.y << " " << response.pose.pose.position.z << " " << response.pose.pose.orientation.x << " " << response.pose.pose.orientation.y << " " << response.pose.pose.orientation.z << " " << response.pose.pose.orientation.w << " " << response.boolvalue.data;return oss.str();
}void handle(const std::shared_ptr<custom_service::srv::NavSatPoseBool::Request> request,     std::shared_ptr<custom_service::srv::NavSatPoseBool::Response> response)  
{response->pose.pose.position.x = 1.0;response->pose.pose.position.y = 2.0;response->pose.pose.position.z = 3.0;response->pose.pose.orientation.x = 4.0;response->pose.pose.orientation.y = 5.0;response->pose.pose.orientation.z = 6.0;response->pose.pose.orientation.w = 7.0;response->boolvalue.data = true;RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %s", serializeRequest(*request).c_str()); RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%s]", serializeResponse(*response).c_str());
}int main(int argc, char **argv)
{rclcpp::init(argc, argv);std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("custom_server");   rclcpp::Service<custom_service::srv::NavSatPoseBool>::SharedPtr service =               node->create_service<custom_service::srv::NavSatPoseBool>("handle",  &handle);   RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to start service.");                     rclcpp::spin(node);rclcpp::shutdown();
}
#include "rclcpp/rclcpp.hpp"
#include "custom_service/srv/nav_sat_pose_bool.hpp"#include <chrono>
#include <cstdlib>
#include <memory>using namespace std::chrono_literals;std::string serializeRequest(const custom_service::srv::NavSatPoseBool::Request &request) {std::ostringstream oss;oss << request.nav.service << " " << request.nav.status << " ";return oss.str();
}std::string serializeResponse(const custom_service::srv::NavSatPoseBool::Response &response) {std::ostringstream oss;oss << response.pose.pose.position.x << " " << response.pose.pose.position.y << " " << response.pose.pose.position.z << " " << response.pose.pose.orientation.x << " " << response.pose.pose.orientation.y << " " << response.pose.pose.orientation.z << " " << response.pose.pose.orientation.w << " " << response.boolvalue.data;return oss.str();
}int main(int argc, char **argv)
{rclcpp::init(argc, argv);std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("custom_client");  rclcpp::Client<custom_service::srv::NavSatPoseBool>::SharedPtr client =                node->create_client<custom_service::srv::NavSatPoseBool>("handle");          auto request = std::make_shared<custom_service::srv::NavSatPoseBool::Request>();request->nav.service = 1;request->nav.status = 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);RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending request: [%s]", serializeRequest(*request).c_str());// Wait for the result.if (rclcpp::spin_until_future_complete(node, result) ==rclcpp::FutureReturnCode::SUCCESS){RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "receiving back response: [%s]", serializeResponse(*result.get()).c_str());} else {RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service custom_server");    }rclcpp::shutdown();return 0;
}

CMakeLists.txt

然后分别编译这两个文件成为可自行文件。

add_executable(server src/server.cpp)
ament_target_dependencies(server rclcpp custom_service)add_executable(client src/client.cpp)
ament_target_dependencies(client rclcpp custom_service)install(TARGETSserverclientDESTINATION lib/${PROJECT_NAME})

编译

 colcon build --packages-select custom_service_nodes

在这里插入图片描述

运行

在新的终端中,需要先初始化环境

source custom_service_nodes/install/setup.sh

然后执行server和client
在这里插入图片描述
在这里插入图片描述

工程地址

  • https://github.com/f304646673/ros2-examples/tree/main/custom_service
  • https://github.com/f304646673/ros2-examples/tree/main/custom_service_nodes

参考资料

  • https://docs.ros.org/en/jazzy/Tutorials/Beginner-Client-Libraries/Custom-ROS2-Interfaces.html

这篇关于Robot Operating System——自定义Service/Client通信消息结构的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1102238

相关文章

java向微信服务号发送消息的完整步骤实例

《java向微信服务号发送消息的完整步骤实例》:本文主要介绍java向微信服务号发送消息的相关资料,包括申请测试号获取appID/appsecret、关注公众号获取openID、配置消息模板及代码... 目录步骤1. 申请测试系统2. 公众号账号信息3. 关注测试号二维码4. 消息模板接口5. Java测试

MySQL中的索引结构和分类实战案例详解

《MySQL中的索引结构和分类实战案例详解》本文详解MySQL索引结构与分类,涵盖B树、B+树、哈希及全文索引,分析其原理与优劣势,并结合实战案例探讨创建、管理及优化技巧,助力提升查询性能,感兴趣的朋... 目录一、索引概述1.1 索引的定义与作用1.2 索引的基本原理二、索引结构详解2.1 B树索引2.2

Java实现自定义table宽高的示例代码

《Java实现自定义table宽高的示例代码》在桌面应用、管理系统乃至报表工具中,表格(JTable)作为最常用的数据展示组件,不仅承载对数据的增删改查,还需要配合布局与视觉需求,而JavaSwing... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码

一文详解Java Stream的sorted自定义排序

《一文详解JavaStream的sorted自定义排序》Javastream中的sorted方法是用于对流中的元素进行排序的方法,它可以接受一个comparator参数,用于指定排序规则,sorte... 目录一、sorted 操作的基础原理二、自定义排序的实现方式1. Comparator 接口的 Lam

如何使用Maven创建web目录结构

《如何使用Maven创建web目录结构》:本文主要介绍如何使用Maven创建web目录结构的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录创建web工程第一步第二步第三步第四步第五步第六步第七步总结创建web工程第一步js通过Maven骨架创pytho

Python循环结构全面解析

《Python循环结构全面解析》循环中的代码会执行特定的次数,或者是执行到特定条件成立时结束循环,或者是针对某一集合中的所有项目都执行一次,这篇文章给大家介绍Python循环结构解析,感兴趣的朋友跟随... 目录for-in循环while循环循环控制语句break语句continue语句else子句嵌套的循

MybatisPlus service接口功能介绍

《MybatisPlusservice接口功能介绍》:本文主要介绍MybatisPlusservice接口功能介绍,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友... 目录Service接口基本用法进阶用法总结:Lambda方法Service接口基本用法MyBATisP

如何自定义一个log适配器starter

《如何自定义一个log适配器starter》:本文主要介绍如何自定义一个log适配器starter的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录需求Starter 项目目录结构pom.XML 配置LogInitializer实现MDCInterceptor

RabbitMQ工作模式中的RPC通信模式详解

《RabbitMQ工作模式中的RPC通信模式详解》在RabbitMQ中,RPC模式通过消息队列实现远程调用功能,这篇文章给大家介绍RabbitMQ工作模式之RPC通信模式,感兴趣的朋友一起看看吧... 目录RPC通信模式概述工作流程代码案例引入依赖常量类编写客户端代码编写服务端代码RPC通信模式概述在R

在Spring Boot中实现HTTPS加密通信及常见问题排查

《在SpringBoot中实现HTTPS加密通信及常见问题排查》HTTPS是HTTP的安全版本,通过SSL/TLS协议为通讯提供加密、身份验证和数据完整性保护,下面通过本文给大家介绍在SpringB... 目录一、HTTPS核心原理1.加密流程概述2.加密技术组合二、证书体系详解1、证书类型对比2. 证书获