ROS话题(Topic)通信:自定义msg - 例程与讲解

在 ROS 通信协议中,数据是以约定好的结构传输的,即数据类型,比如Topic使用的msg,Service使用的srv,ROS 中的 std_msgs 封装了一些原生的数据类型,比如:Bool、Char、Float32、Int64、String等,但这些类型结构简单,常常不能满足我们的需要,这时我们可以使用自定义的消息类型。

比如我们创建一个自定义消息,定义一个机器人的ID,位置(x, y)。

一、创建RobotPose.msg

我们仍然使用前文创建的 topic_hello_world 功能包,结构如下:

在这里插入图片描述

src的同级目录创建 msg 目录,在msg目录创建 RobotPose.msg 文件,内容如下:

string id
float64 x
float64 y
float64 angle

二、配置编译文件

需要对 CMakeLists.txt 作以下修改:

2.1 添加message_generation功能包

message_generation功能包,在构建时根据msgsrv生成消息和服务的接口文件(比如C++头文件和Python包),以便在 ROS 节点中使用。

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)

注意这里需要同时在package.xml中添加以下内容:

<build_depend>message_generation</build_depend>

2.2 添加msg文件

添加自定义msg,该函数依赖message_generation功能包。

add_message_files(
  FILES
  RobotPose.msg
)

2.3 配置依赖并生成接口文件

添加处理msgsrv所需要的依赖,并生成接口文件,该函数依赖message_generation功能包。

generate_messages(
  DEPENDENCIES
  std_msgs
)

2.4 添加message_runtime依赖

message_runtime 用于在运行时提供消息的序列化和反序列化支持。

这里注意,有时可能会看到没有显式添加 message_runtime 也能正常运行,这通常是因为其他依赖项(例如roscppstd_msgs)可能已经隐含地包含了 message_runtime。在这种情况下,构建系统已经处理了消息生成的任务。

然而,为了确保你的软件包在所有情况下都能正常工作,最好显示添加 message_runtime 作为你的软件包的依赖项。这样可以确保你的消息定义在构建和运行时得到正确处理。

需要对 CMakeLists.txt 作以下修改:

catkin_package(
  CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
)

同时在package.xml中添加以下内容:

<exec_depend>message_runtime</exec_depend>

节外生枝的小知识:

catkin_package 是在ROS软件包的 CMakeLists.txt 文件中用于配置Catkin软件包的一条命令。它主要用于描述ROS软件包的元信息,并在构建系统中定义软件包的依赖关系。以下是 catkin_package 的一般用途:

  1. 软件包元信息配置: catkin_package 允许你指定软件包的元信息,例如软件包的名称、版本、作者、描述等。这些信息将用于标识和描述你的ROS软件包。

    cmakeCopy codecatkin_package(
       NAME your_package_name
       VERSION 0.1.0
       DESCRIPTION "Your package description"
       AUTHOR "Your Name"
     )
    
  2. 设置软件包的依赖项: catkin_package 允许你指定你的软件包依赖于其他ROS软件包的哪些部分。这些依赖项将在构建和运行时被解析和满足。

    cmakeCopy codecatkin_package(
       ...
       CATKIN_DEPENDS roscpp std_msgs message_runtime
     )
    
  3. 导出软件包的目标: 通过 ${PROJECT_NAME}_EXPORTED_TARGETS 这样的参数,你可以导出软件包的目标,以便其他软件包能够正确地依赖你的软件包,并包含所有必要的目标。

    cmakeCopy codecatkin_package(
       ...
       EXPORTED_TARGETS ${PROJECT_NAME}_EXPORTED_TARGETS
     )
    

总体而言,catkin_package 提供了一个中心化的地方,用于指定ROS软件包的基本信息和配置,以便构建系统和其他软件包能够正确地使用和依赖你的软件包。在ROS中,它是配置软件包最重要的命令之一。

三、实现发布者与订阅者(C++版)

3.1 源码

在创建的 topic_hello_world 包路径的 src 目录中创建 user_msg_pub.cpp 以实现发布者,编辑内容如下:

#include <ros/ros.h>
#include "topic_hello_world/RobotPose.h"

int main(int argc, char **argv)
{
    ros::init(argc, argv, "user_msg_pub");
    ros::NodeHandle nh;

    ros::Publisher pose_pub = nh.advertise<topic_hello_world::RobotPose>("/robot_pose", 10);

    topic_hello_world::RobotPose pose;
    pose.id = "vbot";
    pose.x = 23.6;
    pose.y = 12.8;
    pose.angle = 90.0;

    while(ros::ok())
    {
        pose_pub.publish(pose);
        ROS_INFO("Pub robot: %s, pose(%lf, %lf, %lf)", pose.id.c_str(), pose.x, pose.y, pose.angle);
        ros::Duration(1).sleep();
        ros::spinOnce();
    }

    return 0;
}

创建 user_msg_sub.cpp 以实现订阅者,编辑内容如下:

#include <ros/ros.h>
#include "topic_hello_world/RobotPose.h"

void robotPoseCallback(const topic_hello_world::RobotPose::ConstPtr &pose)
{
    ROS_INFO("Sub robot: %s, pose(%lf, %lf, %lf)", pose->id.c_str(), pose->x, pose->y, pose->angle);
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "user_msg_sub");
    ros::NodeHandle nh;
    ros::Subscriber pose_sub = nh.subscribe<topic_hello_world::RobotPose>("/robot_pose", 10, robotPoseCallback);

    ros::spin();

    return 0;
}

修改 CMakeLists.txt ,只需添加如下内容:

add_executable(${PROJECT_NAME}_user_msg_pub src/user_msg_pub.cpp)
add_executable(${PROJECT_NAME}_user_msg_sub src/user_msg_sub.cpp)
    
target_link_libraries(${PROJECT_NAME}_user_msg_pub
  ${catkin_LIBRARIES}
)

target_link_libraries(${PROJECT_NAME}_user_msg_sub
  ${catkin_LIBRARIES}
)

3.2 编译运行

进入工作空间执行 catkin_make 命令编译工程,你可能会遇到如下错误:

在这里插入图片描述

这是因为上文提到的message_generation功能包,在它编译自定义msg生成对应接口文件之前,编译了c++源文件,但这时头文件RobotPose.h还没有生成,所以报错了。

到这里你有没有发现,如果各功能包间有依赖关系,他们的编译是有先后顺序的,那我们怎么控制这个先后顺序呢?答案是:不需要。哈哈,CMake已经替我们做了,我们只需告诉它哪个模块需要什么依赖,CMake内部会自动分析项目中的依赖关系,并根据这些依赖关系计算一个拓扑排序。然后,CMake会按照这个顺序处理各个功能包,以确保在构建过程中满足所有依赖关系。

我们可以在 CMakeLists.txt中使用 add_dependencies() 来添加这个依赖关系,语法如下:

add_dependencies(target_name dependency_target_name)

其中,target_name 是要添加依赖关系的目标名称,dependency_target_name 是要添加的依赖目标名称。

例如,如果你有一个名为 my_node 的目标,你想要添加一个名为 my_dependency 的库作为其依赖项,可以使用以下命令:

add_dependencies(my_node my_dependency)

所以,为解决上述报错,我们在 topic_hello_world/CMakeLists.txt中添加如下内容:

# 注意他们要放在add_executable之后,即先告诉CMake是哪个节点,再告诉CMake它需要什么依赖
add_dependencies(${PROJECT_NAME}_user_msg_pub ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(${PROJECT_NAME}_user_msg_sub ${PROJECT_NAME}_generate_messages_cpp)

其中,第一项是我们生成的节点,第二项 ${PROJECT_NAME}_generate_messages_cpp 是一个用于生成消息类型的C++文件的宏,它的作用是根据 .msg.srv 文件生成对应的 .h.cpp 文件。

3.3 节外生枝的小知识:

在ROS软件包的构建过程中,除了${PROJECT_NAME}_generate_messages_cpp,还有一些其他与消息生成和编译相关的宏。这些宏通常都是与 Catkin 工具链和 ROS 构建系统的一部分。

以下是一些常见的与消息生成相关的宏:

  1. ${PROJECT_NAME}_generate_messages 这个宏表示生成所有与消息相关的任务。通常,在调用 catkin_package(...) 时,CATKIN_DEPENDS 部分会包含 ${PROJECT_NAME}_generate_messages,以确保在构建软件包时执行消息生成任务。

    catkin_package(
      CATKIN_DEPENDS 
      roscpp 
      std_msgs 
      message_runtime
      ${PROJECT_NAME}_generate_messages
    )
    
  2. ${PROJECT_NAME}_generate_messages_py 类似于 ${PROJECT_NAME}_generate_messages_cpp,这个宏用于指定生成与消息相关的Python代码的路径。当你的ROS软件包包含使用Python编写的节点或服务时,可能会用到这个宏。

  3. ${PROJECT_NAME}_EXPORTED_TARGETS 这个宏用于导出所有与软件包相关的目标,包括消息生成任务。通常,在调用 catkin_package(...) 时,EXPORTED_TARGETS 部分会包含 ${PROJECT_NAME}_EXPORTED_TARGETS,以确保其他软件包能够正确地依赖你的软件包,并包括所有必要的目标。

    cmakeCopy codecatkin_package(
      ...
      INCLUDE_DIRS include
      LIBRARIES ${PROJECT_NAME}_library
      CATKIN_DEPENDS roscpp std_msgs message_runtime
      DEPENDS system_lib
      EXPORTED_TARGETS ${PROJECT_NAME}_EXPORTED_TARGETS
    )
    

请注意,具体的宏可能会受到ROS版本、Catkin工具链版本和软件包的配置选项的影响。上述宏的名称中的 ${PROJECT_NAME} 部分会根据你的软件包的名称而变化。

3.4 编译成功后,使用如下命令依次启动发布者和订阅者。

1. 启动ros master
roscore
2. 启动发布者
rosrun topic_hello_world topic_hello_world_user_msg_pub
3. 启动订阅者
rosrun topic_hello_world topic_hello_world_user_msg_sub

结果如下:

在这里插入图片描述

目前为止,Topic Hello World 的自定义msg已经成功了。

四、实现发布者与订阅者(Python版)

4.1 源码

topic_hello_world 包路径下的 scripts 目录中,创建 user_msg_pub.py 以实现发布者,编辑内容如下:

#! /usr/bin python

import rospy
from topic_hello_world.msg import RobotPose


def main():
    rospy.init_node("user_msg_pub")
    pub = rospy.Publisher("/robot_pose", RobotPose, queue_size=10)
    msg = RobotPose()
    msg.id = "vbot"
    msg.x = 52.1
    msg.y = 12.6
    msg.angle = 180.0

    while not rospy.is_shutdown():
        pub.publish(msg)
        rospy.loginfo("Pub robot: {}, pose({}, {}, {})".format(msg.id, msg.x, msg.y, msg.angle))
        rospy.sleep(1)


if __name__ == "__main__":
    main()

scrips中创建 user_msg_sub.py 以实现订阅者,编辑内容如下:

#! /usr/bin python

import rospy
from topic_hello_world.msg import RobotPose


def robotPoseCallback(msg):
    rospy.loginfo("Sub robot: {}, pose({}, {}, {})".format(msg.id, msg.x, msg.y, msg.angle))


def main():
    rospy.init_node("user_msg_sub")
    rospy.Subscriber("/robot_pose", RobotPose, robotPoseCallback, queue_size=10)

    rospy.spin()


if __name__ == "__main__":
    main()

修改 CMakeLists.txt ,只需添加如下内容:

catkin_install_python(PROGRAMS
  scripts/user_msg_pub.py
  scripts/user_msg_sub.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

4.2 编译运行

进入工作空间执行 catkin_make 命令编译工程,编译成功后,使用如下命令依次启动发布者和订阅者。

1. 启动ros master(如果已启动,无需再启动)
roscore
2. 启动发布者
rosrun topic_hello_world user_msg_pub.py
3. 启动订阅者
rosrun topic_hello_world user_msg_sub.py

结果如下:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C语言】深入解开指针(三)

&#x1f308;write in front :&#x1f50d;个人主页 &#xff1a; 啊森要自信的主页 真正相信奇迹的家伙&#xff0c;本身和奇迹一样了不起啊&#xff01; 欢迎大家关注&#x1f50d;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;>希望看完我的文章对你有小小的帮助&#x…

【JavaSE】基础笔记 - 图书管理系统(保姆教程,含源码)

目录 1、图书管理系统介绍 2、大致框架 3、代码实现步骤 3.1、Book图书类 3.2、BookList书架类 3.3、User用户类、AdminUser类、NormalUser类 3.4、IOperation操作接口 3.5、继承IOperation接口的操作类 3.6、完善User类 3.7、Mian类 4、完整代码 Java的三大特性是…

反转字符串中的单词

给你一个字符串 s &#xff0c;请你反转字符串中 单词 的顺序。 单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。 返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。 注意&#xff1a;输入字符串 s中可能会存在前导空格、尾随空格…

【MATLAB源码-第81期】基于matlab的polar码三种译码算法比较(SC,SCL,BP)。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 Polar码&#xff08;Polar Codes&#xff09;是一种前向纠错编码方法&#xff0c;被认为是实现信道容量极限的编码方法之一。它在某些场合下&#xff0c;如5G通信标准中得到了应用。Polar码的主要译码算法包括Successive Can…

短期经济波动:均衡国民收入决定理论(三)

短期经济波动&#xff1a;国民收入决定理论(三) 文章目录 短期经济波动&#xff1a;国民收入决定理论(三)[toc]1 总需求曲线及其变动1.1 总需求曲线含义1.2 总需求曲线推导1.2.1 代数推导1.2.2 几何推导 1.3 AD曲线及其变动1.3.1 扩张性财政政策1.3.2 扩张性货币政策 2 总供给曲…

面试其他注意事项

面试其他注意事项 一、面试反问 这个岗位的日常工作和主要职责是什么&#xff1f;咱们这边主要负责什么业务&#xff0c;用到了哪些技术呢&#xff1f;对于我们校招生有没有培养体系呢&#xff1f;脱产培训&#xff0c;还是边工作边熟悉&#xff1f;会有导师带嘛&#xff1f;…

【Spring】IoC容器的一些总结与补充

文章目录 1. 创建容器的两种方式相对路径导入绝对路径导入 2. 获取Bean的三种方式getBean后强转类型getBean内写明类别根据类别获取bean 3. 容器层次结构4. BeanFactory5. bean的总结6. 注入的总结 1. 创建容器的两种方式 相对路径导入 ApplicationContext ctx new ClassPat…

一篇文章让你彻底掌握 shell 语言

一篇文章让你彻底掌握 shell 语言 1. 前序2. shell介绍2.1. 什么是shell2.2. 什么是shell编程2.3. shell解释器3. 基本语法3.1 第一个shell脚本3.2 注释3.3. echo3.3.1 **输出字符串**3.3.2 **输出变量**3.3.3 **启用转义字符**3.3.4 **向文件添加内容**3.3.5 **输出命令执行结…

NET8 ORM 使用AOT SqlSugar

.NET AOT8 基本上能够免强使用了, SqlSugar ORM也支持了CRUD 能在AOT下运行了 Nuget安装 SqlSugarCore 具体代码 StaticConfig.EnableAot true;//启用AOT 程序启动执行一次就好了//用SqlSugarClient每次都new,不要用单例模式 var db new SqlSugarClient(new ConnectionC…

Mac M1 M1 pro安装 protobuf 2.5.0

因为项目中的protobuf是2.5.0版本&#xff0c;但是旧版本的protobuf 不支持M1&#xff0c;此时需要修改源码重新编译 操作步骤&#xff1a; 从git上面下载对应版本的protobuf&#xff0c;地址&#xff1a;Release Protocol Buffers v2.5.0 protocolbuffers/protobuf GitHub…

linux进程间通信之共享内存(mmap,shm_open)

共享内存&#xff0c;顾名思义就是允许两个不相关的进程访问同一个逻辑内存&#xff0c;共享内存是两个正在运行的进 程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内存连接到他们自己的地址空间中&#xff0c…

超详细的Jmeter接口测试教程以及接口测试流程

一、Jmeter简介 Jmeter是由Apache公司开发的一个纯Java的开源项目&#xff0c;即可以用于做接口测试也可以用于做性能测试。 Jmeter具备高移植性&#xff0c;可以实现跨平台运行。 Jmeter可以实现分布式负载。 Jmeter采用多线程&#xff0c;允许通过多个线程并发取样或通过…

Redis实战篇(1)

实战篇Redis 短信登录 这一块我们会使用redis共享session来实现 商户查询缓存 通过本章节&#xff0c;我们会理解缓存击穿&#xff0c;缓存穿透&#xff0c;缓存雪崩等问题&#xff0c;让小伙伴的对于这些概念的理解不仅仅是停留在概念上&#xff0c;更是能在代码中看到对应…

APP安全加固怎么做?加固技术、加固方法、加固方案

前面的文章中我们为大家介绍了移动应用安全检测的测试依据、测试方法、和测试内容&#xff0c;本文我们着重分享App安全加固的相关内容。 &#xff08;安全检测内容&#xff09; 通过前面的文章我们知道了app安全检测要去检测哪些内容&#xff0c;发现问题后我们如何去修复&am…

SpringCloud FeignClient声明式服务调用采坑记录(A调用服务B/C,B/C重启后必须重启A后才能成功调用配置项)

SpringCloud FeignClient声明式服务调用&#xff08;A调用服务B/C&#xff0c;B/C重启后必须重启A后才能成功调用配置项采坑记录&#xff09; 1. 报错&#xff08;info级别的警告信息&#xff09;2. 原因&#xff1a;使用了默认了cache负载均衡&#xff0c;或者禁用了ribbonLoa…

C语言之深入指针(三)(详细教程)

C语言之深入指针 在学习这篇博客之前建议先看看这篇博客C语言之深入指针&#xff08;二&#xff09; 里面详细介绍了指针的 传值调用和传址调用数组名的理解使用指针访问数组⼀维数组传参的本质 文章目录 C语言之深入指针1 二级指针1.1 二级指针的介绍1.2 二级指针的使用 2 指…

【LeetCode:2760. 最长奇偶子数组 | 模拟 双指针】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

【教3妹学编程-算法题】高访问员工

2哥 : 3妹&#xff0c;今天周五怎么还下班这么晚啊&#xff1f;这么晚了才回来 3妹&#xff1a;项目快上线了&#xff0c; 最近事情比较多&#xff0c;再累也要干&#xff0c; 撸起袖子加油干&#xff01; 2哥 : 辛苦辛苦&#xff0c; 哎&#xff0c;你看到王自如格力那个采访了…

京东推出数据平台云海 API接口将达700个

1月16日消息&#xff0c;继上周面对企业用户发布京东电商云解决方案后&#xff0c;日前&#xff0c;京东云平台又发布了全新的数据开放平台——“云海”&#xff0c;以开放商家、商品、点击流等相关数据。 在京东主办&#xff0c;思路网协办的京东开放云服务合作伙伴2014峰会&…

mysql中的各种日志文件redo log、undo log和binlog

mysql中的各种日志文件redo log、undo log和binlog mysql中的各种日志文件redo log、undo log和binlog1.MySQL日志文件类型2.redo log日志2.1 作用2.2工作原理&#xff1a;2.3详解 3.undo log日志4.binlog日志5.总结 mysql中的各种日志文件redo log、undo log和binlog 1.MySQL…