个人在ROS工程使用Protobuf的模板和一点心得

从上周搞定ROS工程下使用Protbuf的问题后,陆续对原有工程进行了更改,在这一过程中,探索了下Protobuf与ROS工程结合的不同方式,暂时总结了一份模板,并在此记录下心得。

1. 使用Proto的ROS节点

在这贴下个人使用的范例:

.
├── CMakeLists.txt
├── include
│   └── map
│       ├── map.cpp
│       └── map.h
├── package.xml
├── proto
│   └── map.proto
└── src
    └── map_node.cpp

个人在尝试不同的组织结构的过程中,也试过把proto文件单独设置一个节点,后续节点只需要添加即可,不需要都更改CmakeLists.txt文件。但对比Apollo工程发现,这样做会使工程目录不够清晰,不能直观地看到工程中是否引用了proto文件。再三考虑后还是决定每个节点单独设置proto文件夹来存在proto文件,这样更清晰直观。


2. CmakeLists.txt模板

目前最终效果如下:

cmake_minimum_required(VERSION 2.8.3)

project(map)

find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
)

## System dependencies are found with CMake's conventions
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
include(FindProtobuf)
find_package(Protobuf REQUIRED)

set(proto_dir ${PROJECT_SOURCE_DIR}/proto)
file(GLOB proto_files "${proto_dir}/*.proto")

# Set up destination directories
catkin_destinations()
set(proto_gen_dir ${CATKIN_DEVEL_PREFIX}/${CATKIN_GLOBAL_INCLUDE_DESTINATION}/${PROJECT_NAME})
set(proto_gen_cpp_dir ${proto_gen_dir})
file(MAKE_DIRECTORY ${proto_gen_dir})
file(MAKE_DIRECTORY ${proto_gen_cpp_dir})
set(protogen_include_dirs ${proto_gen_cpp_dir}/../ ${proto_gen_python_dir})

# Create lists of files to be generated
set(proto_gen_cpp_files "")
foreach(proto_file ${proto_files})  
  get_filename_component(proto_name ${proto_file} NAME_WE)
  list(APPEND proto_gen_cpp_files 
       ${proto_gen_cpp_dir}/${proto_name}.pb.h 
       ${proto_gen_cpp_dir}/${proto_name}.pb.cc
  )
endforeach(proto_file ${proto_files}) 

# Run protoc and generate language-specific headers.
add_custom_command(
  OUTPUT ${proto_gen_cpp_files}
  COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --proto_path=${proto_dir} --cpp_out=${proto_gen_cpp_dir} ${proto_files}
  DEPENDS ${PROTOBUF_PROTOC_EXECUTABLE} ${proto_files}
  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

set_source_files_properties(${proto_gen_cpp_files} PROPERTIES GENERATED TRUE)

add_custom_target(${PROJECT_NAME}_generate_headers
  DEPENDS ${proto_gen_cpp_files} 
) 

# Create proto library for lining.
include_directories(${PROTOBUF_INCLUDE_DIR} ${PROTOBUF_INCLUDE_DIR}/../../)
add_library(${PROJECT_NAME}_proto ${proto_gen_cpp_files})
target_link_libraries(${PROJECT_NAME}_proto ${PROTOBUF_LIBRARY})
add_dependencies(${PROJECT_NAME}_proto ${PROJECT_NAME}_generate_headers)

catkin_package(
  INCLUDE_DIRS ${protogen_include_dirs}
  LIBRARIES ${PROJECT_NAME}_proto
)

install(TARGETS ${PROJECT_NAME}_proto
  ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

install(DIRECTORY ${proto_gen_cpp_dir}/
  DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
  FILES_MATCHING PATTERN "*.h"
)

catkin_package(
  INCLUDE_DIRS include 
)

include_directories(
  ${catkin_INCLUDE_DIRS}
  ${PROTOBUF_INCLUDE_DIR}
  ${CATKIN_DEVEL_PREFIX}
  include
)

add_library(${PROJECT_NAME}
  include/${PROJECT_NAME}/map.cpp
)

add_executable(map_node 
  src/map_node.cpp
  include/${PROJECT_NAME}/map.cpp
)

target_link_libraries(${PROJECT_NAME}_node 
  ${catkin_LIBRARIES}
  ${PROTOBUF_LIBRARIES}
  ${Boost_FILESYSTEM_LIBRARY}
  ${Boost_SYSTEM_LIBRARY}
)

在使用中,有以下几点需要注意:

  1. 使用时必须保证节点根目录下存在proto文件夹,并放置好proto文件,否则会提示无目标文件错误;
  2. 使用时需对应修改下与节点名称相关的变量
  3. 注意:生成的proto文件存在于devel目录inlude文件夹下,对应生成节点名称的文件夹,下面存在生成的变量。
  4. include_directories目录中包含${CATKIN_DEVEL_PREFIX}宏,之后才可以引用devel文件目录中的proto生成文件;