Background 做 ROS 相关开发的,应该都知道 ros msg 有个非常大的槽点: ros msg 扩展性较差,即如果 msg 的字段发生变化,则程序前后版本不兼容 因此,google 的 protobuf 相对就是一个更好的选
Background
做 ROS 相关开发的,应该都知道 ros msg 有个非常大的槽点:
ros msg 扩展性较差,即如果 msg 的字段发生变化,则程序前后版本不兼容
因此,google 的 protobuf 相对就是一个更好的选择。在拥有更好的扩展性的同时,还能给对数据进行压缩,减少 rosbag 的体积。
然而,ROS 的 topic 要求必须使用标准的 ros message,并不是很方便换成 protobuf。如果将 protobuf 转成 str 放在特定的 ros msg 中的话,则又失去了 msg 的类型信息。
在各种尝试下,我找了一种更好的方法,将 msg 替换成 protobuf,同时与 ROS 尽可能的兼容。
注意:本文只探讨 python 环境下的实现,c++ 版的实现由于是其他同事做的,因此不在本文的探讨范围内。
Solution
1. 通过 catkin_make 自动生成 protobuf 对应的代码
可以利用 catkin 的 add_custom_command, 来自动生成代码。这样会自动 install 到相应的目录。使用时,只要 source setup.bash 即可,完全符合 ROS 的推荐使用方式。
# 只节选最核心的部分 set(proto_dir ${PROJECT_SOURCE_DIR}) file(GLOB proto_files "${proto_dir}/*.proto") message(STATUS "Proto Source Dir: ${proto_dir}") message(STATUS "Proto Source Files: ${proto_files}") catkin_destinations() # 设置生成目标代码文件的路径 set(proto_gen_py_dir ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION}) file(MAKE_DIRECTORY ${proto_gen_py_dir}) # 这步很重要,让目标路径变为 python 的 package # 否则的话,会出现 import 异常 file(WRITE ${proto_gen_py_dir}/__init__.py) # Create lists of files to be generated. set(proto_gen_py_files "") foreach(proto_file ${proto_files}) get_filename_component(proto_name ${proto_file} NAME_WE) list(APPEND proto_gen_py_files ${proto_gen_py_dir}/${proto_name}_pb2.py) endforeach(proto_file ${proto_files}) message(STATUS "Generated proto files: ${proto_gen_py_files}") # Run protoc and generate language-specific headers. add_custom_command( OUTPUT ${proto_gen_py_files} COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --proto_path=${proto_dir} --python_out=${proto_gen_py_dir} ${proto_files} DEPENDS ${PROTOBUF_PROTOC_EXECUTABLE} ${proto_files} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) set_source_files_properties(${proto_gen_py_files} PROPERTIES GENERATED TRUE) # 将生成的 py 文件拷贝到 install 对应的路径下 install(DIRECTORY ${proto_gen_py_dir}/ DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION} FILES_MATCHING PATTERN "*.py" )
2. 实现一个 Adapter 类,来实现标准的 ROS message
随便找一个简单的 ros msg 所生成的 python 对应的代码,会发现其实主要做了以下几件事:
- 继承了 genpy.Message。在使用时会强制校验是否是 genpy.Message 的子类
- 实现了 serialize 方法,对内容进行序列化
- 实现了 deserialize 方法,对内容进行反序列化
因此,我们只要按照这种标准的方式,将 protobuf 格式的消息进行序列化或反序列化即可