当前位置: 首页 > news >正文

【ROS1】07-话题通信中使用自定义msg

目录

一、为什么需要自定义msg

二、使用自定义msg

2.1 定义msg文件

2.2 编辑配置文件

2.3 编译

三、自定义Msg案例

3.1 C++实现

3.1.1 配置 

3.1.2 发布方

3.1.3 订阅方

3.2 Python实现

3.2.1 配置

3.2.2 发布方

3.2.3 订阅方


一、为什么需要自定义msg

ROS提供的标准消息类型很方便,但它们是通用的。在实际项目中,你经常需要传递一组特定的、有业务逻辑关联的数据。

举个例子:
假设想创建一个节点来检测摄像头画面中的人,并把检测结果发布出去。通常需要传递以下信息:

  • 名字(string)

  • 年龄(int)

  • 在摄像头坐标系下的位置(geometry_msgs/Point)

  • 检测的可信度(float)

当然可以把这些信息拼接成一个长字符串,然后用std_msgs/String来发布,但这样做有几个巨大的缺点:

  • 解析困难:订阅方需要费力地分割和解析这个字符串,容易出错。

  • 效率低下:字符串处理比处理结构化数据慢得多。

  • 类型不安全:所有数据都是字符串,失去了原有的数据类型(如整数、浮点数)。

  • 可读性差:代码会变得非常混乱。

使用自定义消息就可以避免这些问题 我们可以创建一个消息,内部可以包含name, age, position, confidence这四个字段。这样,数据既结构化、又高效、还易于使用。

二、使用自定义msg

2.1 定义msg文件

在功能包中添加一个文件夹“msg”,用于存放自定义消息

新建一个msg文件,这里命名为“Person.msg”,然后在“Person.msg”中添加如下信息。这里定义了一个msg文件用于描述一个人的信息,该msg包含人的姓名、身高和年龄。

string name
int32 age
float32 height

2.2 编辑配置文件

打开功能包中的“package.xml”,添加如下部分

打开功能包中的“CMakeLists.txt”,添加如下内容:

配置msg源文件

生成消息时依赖std_msgs

执行时依赖

2.3 编译

Ctrl+Shift+B编译一下,没报错表示编译成功

编译后可以看到在“devel”目录下产生了如下中间文件

三、自定义Msg案例

3.1 C++实现

3.1.1 配置 

打开“c_cpp_properties.json”,添加如下内容,来配置头文件路径

3.1.2 发布方

在功能包的src目录中新建一个cpp文件,这里命名为“demo03_pub_person.cpp” 

添加如下代码

#include "ros/ros.h"
#include "plumbing_pub_sub/Person.h"/*
发布方实现:1. 包含头文件2. 初始化ROS节点3. 创建节点句柄4. 创建发布者对象5. 编写发布逻辑并发布数据
*/int main(int argc, char *argv[])
{setlocale(LC_ALL,"");//2. 初始化ROS节点ros::init(argc,argv,"pub_person");//3. 创建节点句柄ros::NodeHandle nh;//4. 创建发布者对象ros::Publisher pub = nh.advertise<plumbing_pub_sub::Person>("Topic3",10);//5. 编写发布逻辑并发布数据//5.1 创建被发布的数据plumbing_pub_sub::Person person;person.name = "张三";person.age = 18;person.height = 170;//5.2 设置发布频率为1秒1次ros::Rate rate(1);//5.3 循环发布数据while (ros::ok()){person.height += 0.01;  //修改被发布的数据pub.publish(person);  //发布数据rate.sleep();ros::spinOnce();}return 0;
}

打开功能包下的“CMakeLists.txt”,添加如下内容,其中第149行的作用是保证msg先被编译,再编译cpp文件

add_executable(demo03_pub_person src/demo03_pub_person.cpp)add_dependencies(demo03_pub_person ${PROJECT_NAME}_generate_messages_cpp)target_link_libraries(demo03_pub_person${catkin_LIBRARIES}
)

编译一下,然后开启3个终端窗口,分别用于启动roscore、启动发布方、打印发布信息

roscore  //启动ros核心cd demo02_ws/  //进入工作空间
source ./devel/setup.bash  //刷新环境变量
rosrun plumbing_pub_sub demo03_pub_person  //启动ros节点cd demo02_ws/
source ./devel/setup.bash
rostopic echo Topic3  //打印“Topic3”话题

执行后可以看到打印

如果想打印发布的信息,我们可以添加如下代码

再次编译后执行可以看到发布的信息打印:

3.1.3 订阅方

创建一个.cpp文件,这里命名为“demo04_sub_person.cpp” 

文件中添加如下代码:

#include "ros/ros.h"
#include "plumbing_pub_sub/Person.h"/*
订阅方实现:1. 包含头文件2. 初始化ROS节点3. 创建节点句柄4. 创建订阅者对象5. 处理订阅到的数据6. 调用spin()
*///5. 处理订阅到的数据
void call_doPerson(const plumbing_pub_sub::Person::ConstPtr& person){ROS_INFO("订阅的信息:%s,%d,%.2f", person->name.c_str(), person->age, person->height);
}int main(int argc, char *argv[])
{setlocale(LC_ALL, "");//2. 初始化ROS节点ros::init(argc, argv, "sub_person");//3. 创建节点句柄ros::NodeHandle nh;//4. 创建订阅者对象ros::Subscriber sub = nh.subscribe("Topic3",10,call_doPerson);//6. 调用spin()ros::spin();return 0;
}

打开功能包中的“CMakeLists.txt”添加如下内容

编译后执行

rosrun plumbing_pub_sub demo03_pub_person  //运行发布方rosrun plumbing_pub_sub demo04_sub_person  //运行订阅方

执行效果如下,可以看到订阅方正确打印了订阅到的信息 

3.2 Python实现

3.2.1 配置

打开“settings.json”,添加如下内容:

{"editor.tabSize": 4,"cmake.sourceDirectory": "/home/chaochao/demo02_ws/src/helloworld","files.associations": {"sstream": "cpp"},"python.autoComplete.extraPaths": ["/opt/ros/noetic/lib/python3/dist-packages","/home/chaochao/demo02_ws/devel/lib/python3/dist-packages"]
}
  • "/opt/ros/noetic/lib/python3/dist-packages"

将这个路径添加进去后,当在代码中写下 import rospy 或者 from std_msgs.msg import String 时,VS Code 就知道去哪里找到这些模块。这样,你就可以获得 rospy. 的方法提示(如 rospy.init_node),并且 VS Code 不会报错说“找不到模块”。

  • "/home/chaochao/demo02_ws/devel/lib/python3/dist-packages"

将这个路径添加进去,VS Code 才能识别自定义的模块。例如,如果你创建了一个名为 my_package 的功能包,里面有一个消息 MyStatus.msg,那么在代码中你就可以写 from my_package.msg import MyStatus,VS Code 会正确识别它,并为你提供字段提示,而不会报错。

3.2.2 发布方

在功能包的“scripts”目录中新建一个文件,这里命名为“py_pub_person.py”

在“py_pub_person.py”添加如下代码

#! /usr/bin/env python
# -*- coding: utf-8 -*-import rospy
from plumbing_pub_sub.msg import Person"""
发布方实现:1. 导包2. 初始化ROS节点3. 创建发布者对象4. 编写发布逻辑并发布数据
"""if __name__ == "__main__":#2. 初始化ROS节点rospy.init_node("pub_person")#3. 创建发布者对象pub = rospy.Publisher("Topic4", Person, queue_size=10)#4. 编写发布逻辑并发布数据#4.1 创建Person数据p = Person()p.name = "张三"p.age = 18p.height = 170#4.2 创建Rate对象rate = rospy.Rate(1)#4.3 循环发布数据while not rospy.is_shutdown():pub.publish(p)rospy.loginfo("发布的信息:%s,%d,%.2f", p.name, p.age, p.height)rate.sleep()

cd到“scripts”目录下,输入如下指令来给新创建的Python添加可执行权限

chmod +x *.py

打开“CMakeLists.txt”,添加如下内容

编译一下,然后在终端中运行发布方并通过命令“rostopic echo”查看发布信息

rosrun plumbing_pub_sub py_pub_person.pyrostopic echo Topic4

3.2.3 订阅方

在功能包的“scripts”目录下新建一个Python文件,这里命名为“py_sub_person.py” 

在“py_sub_person.py” 中添加如下代码:

#! /usr/bin/env python
# -*- coding: utf-8 -*-import rospy
from plumbing_pub_sub.msg import Person"""
订阅方实现:1. 导包2. 初始化ROS节点3. 创建订阅者对象4. 处理订阅到的数据5. spin()
"""# 4. 处理订阅到的数据
def call_doPerson(p):rospy.loginfo("订阅的数据:%s, %d, %.2f", p.name, p.age, p.height)if __name__ == "__main__":# 2. 初始化ROS节点rospy.init_node("sub_person")# 3. 创建订阅者对象sub = rospy.Subscriber("Topic4", Person, call_doPerson)# 5. spin()rospy.spin()

再次给新创建的Python文件添加可执行权限 

打开“CMakeLists.txt”,添加如下一行

编译后执行

rosrun plumbing_pub_sub py_pub_person.pyrosrun plumbing_pub_sub py_sub_person.py

http://www.dtcms.com/a/291196.html

相关文章:

  • (9)机器学习小白入门 YOLOv:YOLOv8-cls 技术解析与代码实现
  • 选择排序 冒泡排序
  • LinkedList与链表(单向)(Java实现)
  • android studio 远程库编译报错无法访问远程库如何解决
  • 算法提升之字符串回文问题-(马拉车算法)
  • Java基础教程(011):面向对象中的构造方法
  • 模拟高负载测试脚本
  • Flink框架:keyBy实现按键逻辑分区
  • 250kHz采样率下多信号参数设置
  • mysql-5.7 Linux安装教程
  • 无人机报警器技术要点与捕捉方式
  • Anaconda 路径精简后暴露 python 及工具到环境变量的配置记录 [二]
  • Linux学习之Linux系统权限
  • scratch音乐会开幕倒计时 2025年6月中国电子学会图形化编程 少儿编程 scratch编程等级考试一级真题和答案解析
  • Git核心功能简要学习
  • 知识 IP 的突围:从 “靠感觉” 到 “系统 + AI” 的变现跃迁
  • 网络编程及原理(八)网络层 IP 协议
  • 关于校准 ARM 开发板时间的步骤和常见问题:我应该是RTC电池没电了才导致我设置了重启开发板又变回去2025年的时间
  • Xilinx FPGA XCKU115‑2FLVA1517I AMD KintexUltraScale
  • 【Java EE】多线程-初阶-Thread 类及常见方法
  • Netty中CompositeByteBuf 的addComponents方法解析
  • PNP加速关断驱动电路
  • [数据结构]#4 用链表实现的栈结构
  • FastAPI 中,数据库模型(通常使用 SQLAlchemy 定义)和接口模型(使用 Pydantic 定义的 schemas)的差异
  • GraphRAG快速入门和原理理解
  • 在线教育如何设置视频问答/视频弹题?——重塑在线教育的互动体验
  • 元宇宙工厂漫游指南:VR可视化在设备巡检与远程运维中的沉浸式应用
  • Component cannot be used as a JSX component
  • 网络数据编码技术及其应用场景的全面解析
  • 基于Vue与CloudBase AI Toolkit的色觉识别Web应用开发报告:VibeCoding新范式实践