在 ROS 中使用 Protobuf 替代 ros msg

libitum發表於2019-02-16

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 對應的程式碼,會發現其實主要做了以下幾件事:

  1. 繼承了 genpy.Message。在使用時會強制校驗是否是 genpy.Message 的子類
  2. 實現了 serialize 方法,對內容進行序列化
  3. 實現了 deserialize 方法,對內容進行反序列化

因此,我們只要按照這種標準的方式,將 protobuf 格式的訊息進行序列化或反序列化即可

Summary

Reference

相關文章