ROS 2边学边练(16)-- 自定义msg和srv文件

前言

        在前面的文章我们在学习主题(topic)和服务(service)通信方法时,使用的一直是ROS 2提供好的消息结构文件(xxx.msg)和服务结构文件(xxx.srv),稀里糊涂的就这样过去了,如果我们有个需求,ROS 2提供的msg和srv无法满足,那我们就得定义自己结构的msg和srv文件了。

动动手

创建一个功能包

        .msg和.srv文件需要分别处于两个文件夹下(msg和srv文件夹,具体功能包目录下同一等级),我们先来创建功能包,进入工作空间根路径(先source install/setup.bash),执行下面命令创建tutorial_interfaces功能包:

$ros2 pkg create --build-type ament_cmake --license Apache-2.0 tutorial_interfaces

 

完成后再进入功能包tutorial_interfaces的根路径下,新建俩个文件夹用来放置.msg和.srv文件:

$mkdir msg srv

这俩文件夹可以被C++实现的节点模块使用,也能被python实现的节点模块使用。

自定义

msg

        首先在msg文件夹下新建一个叫Num.msg的文件,添加下面这行内容到里面:

int64 num

该消息名称为Num,只有一个类型为int64的元素num。

        然后同样在msg文件夹下新建一个叫Sphere.msg的文件,添加下面两行内容到里面:

geometry_msgs/Point center
float64 radius

Sphere消息包含了两个元素,第一个为系统提供的功能包geometry_msgs中Point类型的center(可以看出我们可以嵌入消息),第二个为float64类型的radius。

srv

        在功能包的srv文件夹下(ros2_ws/src/tutorial_interfaces/srv)新建一个叫AddThreeInts.srv的文件,添加下面内容到里面:

int64 a
int64 b
int64 c
---
int64 sum

请求元素有3个:a、b、c,都是int64类型,回复依然是int64类型的sum。

修改CMakeLists.txt

        要将我们自定义的接口转换为特定语言(C++/PYTHON)的代码,我们需要将下面的内容增加到CMakeLists.txt中去:

find_package(geometry_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)rosidl_generate_interfaces(${PROJECT_NAME}"msg/Num.msg""msg/Sphere.msg""srv/AddThreeInts.srv"DEPENDENCIES geometry_msgs # Add packages that above messages depend on, in this case geometry_msgs for Sphere.msg
)

对于上面的内容,“rosidl_generate_interfaces()”第一个参数必须是{PROJECT_NAME},否则可能会出现非同一般的错误。

修改package.xml

        将下面内容添加到package.xml内:

<depend>geometry_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>

简单解释一下,geometry_msgs在我们自定义的Sphere.msg中有引用,所以需要依赖(库依赖<depend>),rosidl_default_generators会生成语言的代码(C++/PYTHON),其是一种构建工具(工具依赖<buildtool_depend>),所以需要依赖,rosidl_default_runtime是一个运行时或执行阶段需要的依赖项,需要接口能够在以后被使用(执行依赖<exec_depend>),rosidl_interface_packages是功能包tutorial_interfaces应该与之关联的依赖组的名称,使用<member_of_group>标记声明。

 

构建包

        进入工作空间根路径,按照下面的命令进行包的构建工作:

$colcon build --packages-select tutorial_interfaces

确认自定义的msg和srv被成功构建 

        大家应该还记得如何查看msg和srv具体数据结构的命令吧,新开一个终端,先source环境变量如何进行下面操作:

$ros2 interface show tutorial_interfaces/msg/Num

再来看看另外一个消息Sphere和剩下的AddThreeInts服务:

$ros2 interface show tutorial_interfaces/msg/Sphere
$ros2 interface show tutorial_interfaces/srv/AddThreeInts

 

 Perfect!

测试新的接口

Num.msg

        为方便测试,我们利用之前用过的例子(发布者/订阅者)来测试这个新的消息Num.msg,我们在工作空间的src路径下,进入cpp_pubsub文件夹,先修改发布者节点源文件publisher_member_function.cpp,按照下面的内容进行修改:

#include <chrono>
#include <memory>#include "rclcpp/rclcpp.hpp"
#include "tutorial_interfaces/msg/num.hpp"                                            // CHANGEusing namespace std::chrono_literals;class MinimalPublisher : public rclcpp::Node
{
public:MinimalPublisher(): Node("minimal_publisher"), count_(0){publisher_ = this->create_publisher<tutorial_interfaces::msg::Num>("topic", 10);  // CHANGEtimer_ = this->create_wall_timer(500ms, std::bind(&MinimalPublisher::timer_callback, this));}private:void timer_callback(){auto message = tutorial_interfaces::msg::Num();                                   // CHANGEmessage.num = this->count_++;                                                     // CHANGERCLCPP_INFO_STREAM(this->get_logger(), "Publishing: '" << message.num << "'");    // CHANGEpublisher_->publish(message);}rclcpp::TimerBase::SharedPtr timer_;rclcpp::Publisher<tutorial_interfaces::msg::Num>::SharedPtr publisher_;             // CHANGEsize_t count_;
};int main(int argc, char * argv[])
{rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<MinimalPublisher>());rclcpp::shutdown();return 0;
}

原先调用标准消息的语句是:

publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);

现在使用自定义消息的语句是:

publisher_ = this->create_publisher<tutorial_interfaces::msg::Num>("topic", 10); 

可以发现<..>里面的内容变化了,另外在install路径下对应的tutorial_interfaces包里面的include路径下有自动生成一个Num.hpp头文件。

再来subscriber_member_function.cpp.c文件,内容如下:

#include <functional>
#include <memory>#include "rclcpp/rclcpp.hpp"
#include "tutorial_interfaces/msg/num.hpp"                                       // CHANGEusing std::placeholders::_1;class MinimalSubscriber : public rclcpp::Node
{
public:MinimalSubscriber(): Node("minimal_subscriber"){subscription_ = this->create_subscription<tutorial_interfaces::msg::Num>(    // CHANGE"topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));}private:void topic_callback(const tutorial_interfaces::msg::Num & msg) const  // CHANGE{RCLCPP_INFO_STREAM(this->get_logger(), "I heard: '" << msg.num << "'");     // CHANGE}rclcpp::Subscription<tutorial_interfaces::msg::Num>::SharedPtr subscription_;  // CHANGE
};int main(int argc, char * argv[])
{rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<MinimalSubscriber>());rclcpp::shutdown();return 0;
}

调整改变的地方也是与publisher差不多,都是将之前的std_msgs::msg::String消息换成了tutorial_interfaces::msg::Num消息。

CMakeLists.txt

CMakeLists.txt中将std_msgs替换为新包tutorial_interfaces即可,

#...find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(tutorial_interfaces REQUIRED)                      # CHANGEadd_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp tutorial_interfaces)    # CHANGEadd_executable(listener src/subscriber_member_function.cpp)
ament_target_dependencies(listener rclcpp tutorial_interfaces)  # CHANGEinstall(TARGETStalkerlistenerDESTINATION lib/${PROJECT_NAME})ament_package()

package.xml

只需修改一行即可,如下:

<depend>tutorial_interfaces</depend>

构建包cpp_pubsub

进入工作空间根路径,执行下面命令进行构建:

$colcon build --packages-select cpp_pubsub

分别新开两个终端,各自source install/setup.bash,再运行订阅者节点再发布者节点,结果如下:

AddThreeInts.srv 

        测试新定义的服务,我们同样可以修改之前的例子(服务端/客户端),进入cpp_srvcli包,先修改add_two_ints_server.cpp:

#include "rclcpp/rclcpp.hpp"
#include "tutorial_interfaces/srv/add_three_ints.hpp"                                        // CHANGE#include <memory>void add(const std::shared_ptr<tutorial_interfaces::srv::AddThreeInts::Request> request,     // CHANGEstd::shared_ptr<tutorial_interfaces::srv::AddThreeInts::Response>       response)  // CHANGE
{response->sum = request->a + request->b + request->c;                                      // CHANGERCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld" " c: %ld",  // CHANGErequest->a, request->b, request->c);                                         // CHANGERCLCPP_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_three_ints_server");   // CHANGErclcpp::Service<tutorial_interfaces::srv::AddThreeInts>::SharedPtr service =               // CHANGEnode->create_service<tutorial_interfaces::srv::AddThreeInts>("add_three_ints",  &add);   // CHANGERCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add three ints.");                     // CHANGErclcpp::spin(node);rclcpp::shutdown();
}

再修改add_two_ints_client.cpp:

#include "rclcpp/rclcpp.hpp"
#include "tutorial_interfaces/srv/add_three_ints.hpp"                                       // CHANGE#include <chrono>
#include <cstdlib>
#include <memory>using namespace std::chrono_literals;int main(int argc, char **argv)
{rclcpp::init(argc, argv);if (argc != 4) { // CHANGERCLCPP_INFO(rclcpp::get_logger("rclcpp"), "usage: add_three_ints_client X Y Z");      // CHANGEreturn 1;}std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_three_ints_client");  // CHANGErclcpp::Client<tutorial_interfaces::srv::AddThreeInts>::SharedPtr client =                // CHANGEnode->create_client<tutorial_interfaces::srv::AddThreeInts>("add_three_ints");          // CHANGEauto request = std::make_shared<tutorial_interfaces::srv::AddThreeInts::Request>();       // CHANGErequest->a = atoll(argv[1]);request->b = atoll(argv[2]);request->c = atoll(argv[3]);                                                              // CHANGEwhile (!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_three_ints");    // CHANGE}rclcpp::shutdown();return 0;
}

由之前请求两个元素变成请求三个元素,不再赘述。

CMakeLists.txt

将std_msgs替换为新定义的tutorial_interfaces,

#...find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(tutorial_interfaces REQUIRED)         # CHANGEadd_executable(server src/add_two_ints_server.cpp)
ament_target_dependencies(serverrclcpp tutorial_interfaces)                      # CHANGEadd_executable(client src/add_two_ints_client.cpp)
ament_target_dependencies(clientrclcpp tutorial_interfaces)                      # CHANGEinstall(TARGETSserverclientDESTINATION lib/${PROJECT_NAME})ament_package()

package.xml

同样只需修改这一行

<depend>tutorial_interfaces</depend>

构建包cpp_srvcli

工作空间根路径下:

$colcon build --packages-select cpp_srvcli

同样开启两个终端,分别source install/setup.bash,然后先启动服务节点,然后启动客户端节点,结果如下:

 

从以上结果可以看出,我们的自定义msg和srv成功了!

本篇完。

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

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

相关文章

编程新手必看,学习python中列表数据类型内容(9)

Python中的列表是一种可变的、有序的数据结构&#xff0c;它允许存储不同类型的数据项&#xff0c;并支持多种操作。具体如下&#xff1a; 创建列表&#xff1a;可以通过一对方括号[]来创建一个空列表&#xff0c;或者在方括号内放入初始元素来创建一个非空列表。例如&#xff…

链表实验.

#include<stdio.h> #include<stdlib.h>// 定义单链表节点结构体 struct Node {int data;struct Node* next; };struct Node* initList() {struct Node* list (struct Node*)malloc(sizeof(struct Node));list->data 0;list->next NULL;return list; }void…

特征提取算法

特征提取算法 0. 写在前边1. Harris算法1.1 写在前面1.2 Harris算法的本质1.3 Harris算法的简化 2. Harris3D2.1 Harris3D算法问题定义2.2 Harris3D with intensity2.3 Harris3D without intensity 3. ISS特征点的应用 0. 写在前边 本篇将介绍几种特征提取算法&#xff0c;特征…

笔记 | 软件工程:需求分析

1 需求分析 #需求分析 1.1 需求分析概述 初步软件需求存在的问题&#xff1a;不具体&#xff0c;不清晰&#xff0c;关系不明朗&#xff0c;存在潜在缺陷&#xff0c;没有区分不同软件需求&#xff08;有必要鉴别不同软件需求项的重要性差别&#xff0c;区分不同软件需求的开…

【C语言】——指针八:指针运算笔试题解析

【C语言】——指针八&#xff1a;指针运算笔试题解析 一、题一二、题二三、题三四、题四五、题五六、题六七、题七 一、题一 //程序输出结果是什么 int main() {int a[5] { 1,2,3,4,5 };int* ptr (int*)(&a 1);printf("%d, %d", *(a 1), *(ptr - 1));return…

揭开“栈和队列”的神秘面纱

前言 在线性表中不止有顺序表和链表&#xff0c;今天的主角就如标题所说--->认识栈和队列。把他们俩放一起总结是有原因的&#xff0c;还请看官听我娓娓道来~ 什么是栈&#xff1f; 栈&#xff08;stack&#xff09;是限定仅在表尾进行插入和删除操作的线性表 咱可以把栈理…

Pro版 v3.0首页DIY热区神器,让图片更全能!

Pro版v3.0新增了很多新功能&#xff0c;为了方便大家能快速了解使用&#xff0c;今天&#xff0c;我们就开始来逐步了解这些新功能。 在此次更新中&#xff0c;Pro版的首页DIY功能实现了全新重构升级&#xff0c;新增了DIY热区、视频、排行榜、积分商城、预售、签到组件&#…

【Hadoop技术框架-MapReduce和Yarn的详细描述和部署】

前言&#xff1a; &#x1f49e;&#x1f49e;大家好&#xff0c;我是书生♡&#xff0c;今天的内容主要是Hadoop的后两个组件&#xff1a;MapReduce和yarn的相关内容。同时还有Hadoop的完整流程。希望对大家有所帮助。感谢大家关注点赞。 &#x1f49e;&#x1f49e;前路漫漫&…

深度学习实战73-基于多模态CLIP模型的实战项目,CLIP模型的架构介绍与代码实现

大家好,我是微学AI,今天给大家介绍一下深度学习实战73-基于多模态CLIP模型的实战项目,CLIP模型的架构介绍与代码实现。多模态CLIP(Contrastive Language-Image Pre-training)模型是一种深度学习模型,其核心设计理念是通过大规模的对比学习训练,实现图像与文本之间的跨模…

【JAVASE】面向对象程序三大特性之一( 封装)

✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609;\n &#x1f34e;个人主页&#xff1a;再无B&#xff5e;U&#xff5e;G-CSDN博客 目标&#xff1a; 1.包的使用 2.static关键字的使用 3.代码…

Python+Vuecil笔记

Nginx 进入目录: C:\nginx-1.20.2\nginx-1.20.2 start nginx 开始 nginx -s stop 停止 nginx -s quit 退出CSS 通过标签去写css 循环展示数据 JS 点击时执行事件 Django 配置media 在seetings里面修改 STATIC_URL /static/ MEDIA_URL /upload/ MEDIA_ROOT os.pat…

windows terminal美化教程

安装terminal 微软商店下载安装terminal 配置文件 进入terminal&#xff0c;打开设置。 {"$schema": "https://aka.ms/terminal-profiles-schema",// global settings"profiles": {// profile settings"defaults": {// default sett…

阿里云服务器租赁一年收费标准

阿里云服务器租用价格表2024年最新&#xff0c;云服务器ECS经济型e实例2核2G、3M固定带宽99元一年&#xff0c;轻量应用服务器2核2G3M带宽轻量服务器一年61元&#xff0c;ECS u1服务器2核4G5M固定带宽199元一年&#xff0c;2核4G4M带宽轻量服务器一年165元12个月&#xff0c;2核…

【亲测有效】微信公众号设置菜单栏显示,未开启自定义菜单,微信公众平台自定义菜单接口开发

微信公众平台自定义菜单接口开发 问题:运营人员在设置微信公众号设置菜单栏显示,未开启自定义菜单解决方案(微信公众平台自定义菜单接口开发):自定义菜单-创建接口请求链接完整代码第一步:在WeChat类里添加代码情况一:没有WeChat类情况,如果已有请看情况二情况二:已有…

【系统架构师】-软件架构设计

1、软件架构的概念 架构的本质 1、软件架构为软件系统提供了一个结构、行为和属性的高级抽象。 2、软件架构风格是特定应用领域的惯用模式&#xff0c;架构定义一个词汇表和一组约束。 架构的作用 1、软件架构是项目干系人进行交流的手段。 2、软件架构是可传递和可复用的模型…

番茄 短abogus补环境

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01;wx a15018601872 本文章…

端到端单倍型参考基因组揭示了三倍体香芽蕉型香蕉亚基因组的分歧和疾病抵抗力-文献精读-5

T2T基因组文献分享Telomere-to-telomere haplotype-resolved reference genome reveals subgenome divergence and disease resistance in triploid Cavendish banana 三倍体植物基因组的文献&#xff0c;各位同仁还有什么有特色的基因组评论区留言~ 摘要 香蕉是世界上最重要…

JavaEE——手把手教你实现简单的 servlet 项目

文章目录 一、什么是 Servlet二、创建一个简单的 Servlet 程序1. 创建项目2.引入依赖3. 创建目录4.编写代码5. 打包程序6. 部署7.验证整体过程总结 三、使用 Smart Tomcat 插件简化项目创建四、创建项目时可能遇到的几个问题。 一、什么是 Servlet Servlet 是一种实现 动态页面…

12、最小覆盖子串

如何想到这个解法 问题的特点&#xff1a; 首先&#xff0c;认识到这是一个关于子串的问题&#xff0c;而且需要考虑子串的最小长度。这提示我们可能需要使用一种方式来逐步探索不同的子串。滑动窗口的适用性&#xff1a;滑动窗口是处理子串问题的常用技巧&#xff0c;特别是当…

【图像分割】nnUnetV1与V2的Linux部署与应用命令

以前觉得麻烦&#xff0c;一直没用过nnunet&#xff0c;虽然知道它很火&#xff0c;最近一个契机&#xff0c;部署使用了一下nnunet&#xff0c;记录一下其部署和使用的方法与命令。 1、部署 首先&#xff0c;我有一个环境&#xff0c;这个环境可以是以前就有的&#xff0c;也可…