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

news2024/11/13 23:38:32

大纲

  • 初始化环境
  • 生成自定义服务的工程
    • 创建包
    • 自定义消息
    • 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 files
  set(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 files
  set(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(TARGETS
  server
  client
  DESTINATION 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2068697.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux | 文件系统进阶:Inode与软硬链接艺术剖析

当时共我赏花人&#xff0c;点检如今无一半。 - 《木兰花》(晏殊) 2024.8.24 目录 1. 文件系统的基本概念 1.1 ls -l命令查看目录信息 1.2 stat命令查看具体文件的详细信息 1.3 inode ext2文件系统的主要组成部分&#xff1a; 例子&#xff1a;创建main.c文件 文件的创建步骤&a…

leetcode:2520. 统计能整除数字的位数(python3解法)

难度&#xff1a;简单 给你一个整数 num &#xff0c;返回 num 中能整除 num 的数位的数目。 如果满足 nums % val 0 &#xff0c;则认为整数 val 可以整除 nums 。 示例 1&#xff1a; 输入&#xff1a;num 7 输出&#xff1a;1 解释&#xff1a;7 被自己整除&#xff0c;因…

继续修改原神角色数据列表

<!DOCTYPE html> <html lang"zh-cn"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>原神4.8版本获得角色数据表</title> </…

【STM32】一些外设通用内容

在学习各种外设的过程中&#xff0c;发现外设有一些通用的东西可以总结一下&#xff0c;后面发现再继续更新。图来源于正点原子的学习视频和PPT。 目录 1 外设的时钟的开启 2 外设初始化的回调机制 3 外设的中断服务函数 4 HAL库中断回调机制 5 函数的常见…

SpringCache源码解析(一)

一、springCache如何实现自动装配 SpringBoot 确实是通过 spring.factories 文件实现自动配置的。Spring Cache 也是遵循这一机制来实现自动装配的。 具体来说,Spring Cache 的自动装配是通过 org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration 这个类来…

文件树控件开发

文件树控件和获取驱动信息功能 然后添加上查看文件信息的按钮 双击这个按钮添加上如下代码 void CRemoteClientDlg::OnBnClickedBtnFileinfo() {int ret SendCommandPacket(1);if (ret -1) {AfxMessageBox(_T("命令处理失败!!!"));return;}ClientSocket* pClient…

c++每日练习记录5-(链表的结尾指向nullptr)

解题方法&#xff1a;双指针法 ListNode *partition(ListNode *head, int x){ListNode *head1 new ListNode(0);ListNode *head2 head1;ListNode *head3 new ListNode(0);ListNode *head4 head3;while (head! nullptr){if (head->val < x){head1->next head;head…

成品CNC外壳的巧妙使用

有些时候10块买一个CNC外壳&#xff0c;钻个孔&#xff0c;比单独的3D打印更能提升板子的档次感&#xff1a; 这个CNC是真的好看&#xff0c;再加上3D打印辅助设计&#xff0c;堪称精美&#xff1a;

k8s安装Metabase开源报表系统

metabase是什么&#xff1f; metabase是一款开源的简易但强大同时又无缝兼容大数据和传统数据库的分析工具&#xff0c;帮助公司每一个人对企业数据的学习挖掘&#xff0c;进而达到更好的数据化运营和决策。 Metabase is a simple and powerful analytics tool which lets anyo…

热血传奇1.76版本完美仿官单机版安装教程+GM工具+无需虚拟机

今天给大家带来一款单机游戏的架设&#xff1a;热血传奇1.76版本完美仿官。 另外&#xff1a;本人承接各种游戏架设&#xff08;单机联网&#xff09; 本人为了学习和研究软件内含的设计思想和原理&#xff0c;带了架设教程仅供娱乐。 教程是本人亲自搭建成功的&#xff0c;…

软件上显示“mfc140.dll丢失”错误信息?那么mfc140.dll丢失该如何修复

mfc140.dll是 Microsoft Foundation Class (MFC) 库的一部分&#xff0c;这个库被用于基于 C 的 Windows 应用程序的开发。当 Windows 或软件上显示“mfc140.dll丢失”或“找不到 mfc140.dll”这类错误信息时&#xff0c;表示你的系统可能缺少与 Visual C 相关的组件或这些组件…

软考:软件设计师 — 14.算法基础

十四. 算法基础 1. 算法的特性 算法是对特定问题求解步骤的描述&#xff0c;它是指令的有限序列&#xff0c;其中每一条指令表示一个或多个操作。 有穷性&#xff1a;执行有穷步之后结束&#xff0c;且每一步都可在有穷时间内完成。确定性&#xff1a;算法中每一条指令必须有…

代码随想录算法训练营第三十五天 | 416. 分割等和子集

416. 分割等和子集 题目链接&#xff1a;力扣题目链接 文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;动态规划之背包问题&#xff0c;这个包能装满吗&#xff1f;| LeetCode&#xff1a;416.分割等和子集 给定一个只包含正整数的非空数组。是否可以将这个数组分割…

面向对象01:类和对象的创建

本节内容视频链接&#xff1a;面向对象04&#xff1a;类与对象的创建_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV12J41137hu?p63&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 1.类、对象定义及关系 类&#xff08;‌Class&#xff09;‌是一个模板或蓝图&#…

如何规避DDoS攻击带来的风险?服务器DDoS防御软件科普

DDoS攻击是目前最常见的网络攻击方式之一。其见效快、成本低的特点&#xff0c;使它深受不法分子的“喜爱”。对于未受保护的企业来说&#xff0c;每次DDoS攻击的平均成本为20万美元&#xff0c;当DDoS攻击汹涌而至&#xff0c;缺失详细的保护预案&#xff0c;企业很可能会陷入…

erlang学习:gen_server书上案例22.6练习题4

昨天没有输出Fun中的io的原因是因为在任务函数中没有调用Fun方法&#xff0c;相当于只传了Fun函数但是没有进行调用&#xff0c;因此没有执行Fun函数&#xff0c;所以控制台中没有进行io的输出&#xff0c;今天在add_job中调用了Fun方法并执行&#xff0c;所以输出了相应的io。…

图像数据处理22

五、边缘检测 5.4 Hough变换 该技术主要用于检测图像中的基本形状&#xff0c;如直线、圆、椭圆等。 Hough变换的基本原理 Hough变换的基本原理是将图像空间中的直线或曲线变换到参数空间中&#xff0c;通过检测参数空间中的极值点&#xff08;局部最大值&#xff09;&…

自制镜像(贫穷版)

在装了docker的机子root目录操作 mkdir -p docker-images/tomcat-image/ cd docker-images/tomcat-image/ 把这两个红框的拉到docker-images/tomcat-image/ vim Dockerfile #导入基础镜像 from centos:7 #定义作者 maintainer GGBond<2958458916qq.com&…

SpringCloudGateway重写负载均衡策略

背景 gateway中多实例请求转发&#xff0c;默认采用轮训转发策略。在有些场景下&#xff0c;某些请求想固定到某一台实例上&#xff0c;这里通过重写默认负载均衡策略的方式实现。 以下代码为&#xff0c;大文件分片上传&#xff0c;多实例场景&#xff0c;根据文件md5和实例…