如何在ROS中发布与订阅二维字符串数组/列表?
问题描述
需要编写ROS节点发布如下字符串矩阵:
sherd_1::link goHome0 sherd_2::link goHome0 sherd_3::link goHome0 sherd_5::link goHome0 sherd_6::link goHome1 sherd_9::link goHome1 sherd_10::link goHome2 sherd_11::link goHome1 sherd_12::link goHome0 sherd_13::link goHome2
尝试过程与错误
- 首先定义
sherd_array.msg:
string[] sherds string[] homes
该消息可正常发布订阅。
- 尝试定义
matrix.msg为:
sherd_array[][] data
执行catkin_make时编译错误:
genmsg.base.InvalidMsgSpec: braccio_moveit_gazebo_noetic/custom_msgs/msg/matrix.msg: invalid field: Currently only support 1-dimensional array types: custom_msgs/sherd_array[][] make[2]: *** [braccio_moveit_gazebo_noetic/custom_msgs/CMakeFiles/_custom_msgs_generate_messages_check_deps_matrix.dir/build.make:70: braccio_moveit_gazebo_noetic/custom_msgs/CMakeFiles/_custom_msgs_generate_messages_check_deps_matrix] Errore 1 make[1]: *** [CMakeFiles/Makefile2:1023: braccio_moveit_gazebo_noetic/custom_msgs/CMakeFiles/_custom_msgs_generate_messages_check_deps_matrix.dir/all] Errore 2 make: *** [Makefile:146: all] Errore 2 Invoking "make -j8 -l8" failed
- 修改
matrix.msg为:
sherd_array[2] data
发布节点看似正常,但订阅时崩溃,报错:
line 60, in serialize length = len(val1.sherds) AttributeError: 'numpy.ndarray' object has no attribute 'sherds'
用rospy.loginfo输出的消息内容是正确的:
[INFO] [1697191139.662418, 201.596000]: data: - ['sherd_1::link' 'sherd_2::link' 'sherd_3::link' 'sherd_5::link' 'sherd_6::link' 'sherd_9::link' 'sherd_10::link' 'sherd_11::link' 'sherd_12::link' 'sherd_13::link'] - ['goHome0' 'goHome0' 'goHome0' 'goHome0' 'goHome1' 'goHome1' 'goHome2' 'goHome1' 'goHome0' 'goHome2']
解决方案
核心问题分析
ROS自定义消息不支持多维数组嵌套自定义消息(即sherd_array[][]这种形式);而修改为固定长度数组后出现的AttributeError,是因为直接将numpy数组赋值给了消息的data字段,但ROS消息期望的是sherd_array类型的对象列表,不是numpy数组。
正确实现方式
方式1:调整消息结构,贴合数据逻辑
如果你的矩阵是每行两个字符串(sherd名称和home目标),可以定义更贴合的基础消息:
# sherd_item.msg string sherd_name string home_target
再定义矩阵消息为一维数组:
# sherd_matrix.msg sherd_item[] items
发布时把每行数据封装成sherd_item对象,再放到数组里即可,完全符合ROS消息规范,无编译或运行问题。
方式2:修复现有消息的赋值逻辑
若坚持使用现有的sherd_array和matrix.msg(sherd_array[2] data),需确保赋值给data的是sherd_array对象,而非numpy数组:
import rospy from custom_msgs.msg import matrix, sherd_array def talker(): pub = rospy.Publisher('sherd_matrix', matrix, queue_size=10) rospy.init_node('matrix_publisher', anonymous=True) rate = rospy.Rate(10) # 原始数据 sherds = ['sherd_1::link', 'sherd_2::link', 'sherd_3::link', 'sherd_5::link', 'sherd_6::link', 'sherd_9::link', 'sherd_10::link', 'sherd_11::link', 'sherd_12::link', 'sherd_13::link'] homes = ['goHome0', 'goHome0', 'goHome0', 'goHome0', 'goHome1', 'goHome1', 'goHome2', 'goHome1', 'goHome0', 'goHome2'] while not rospy.is_shutdown(): # 创建matrix消息 matrix_msg = matrix() # 创建第一个sherd_array对象,赋值sherds列表 sa1 = sherd_array() sa1.sherds = sherds matrix_msg.data[0] = sa1 # 创建第二个sherd_array对象,赋值homes列表 sa2 = sherd_array() sa2.homes = homes matrix_msg.data[1] = sa2 pub.publish(matrix_msg) rospy.loginfo(matrix_msg) rate.sleep() if __name__ == '__main__': try: talker() except rospy.ROSInterruptException: pass
这样data里的每个元素都是标准的sherd_array消息对象,具备sherds和homes属性,不会出现属性错误。
方式3:用一维字符串数组序列化传输
若不想自定义多层消息,可把每行数据用分隔符拼接成单个字符串,再放到一维数组里发布:
# matrix_string.msg string[] rows
发布时每行拼接成sherd_1::link,goHome0这样的格式,订阅后用split(',')拆分即可还原二维数据,这种方式最简单,适合快速实现。
关键注意点
- ROS自定义消息仅支持一维数组,无论基础类型还是自定义消息类型,都不能直接定义多维数组。
- 赋值消息字段时,必须使用ROS自动生成的消息类对象,不能直接用numpy数组或原生列表(基础类型列表除外)。
内容的提问来源于stack exchange,提问作者caesar753
相关产品推荐
相关产品推荐

