Using Apollo Algorithms in Your Normal ROS Package
This blog will introduce how the port_apollo repository converts an apollo module to a normal ROS package and uses Apollo algorithms in your own ros package in a native linux system (i.e. ubuntu 16.04 LTS) instead of docker environments.
The tested codes are based on the release apollo-v3.0.0.
The goal of this project is to convert a bazel project to a cmake project while without modifying the algorithm part of source codes in apollo and keeping the existing directory structure unchanged. But the #include
directories in the source files somehow need to be adapted according to the structure of cmake projects. I am not going the split the header files and source files of apollo like typical cmake
projects. I prefer keeping codes as it was to avoid copying files and moving directories. The rational behind this is that in this way I can easily checkout the contents of the module folder of new apollo release version and throw the existing CMakeLists.txt
and package.xml
to the updated codes and get a new working version with minimum effort.
So the include
directory won’t exist in every ROS package folder. But you can still include project header files like other normal ROS packages assuming the headers are there.
Structure of the port_apollo Repo
port_apollo
├── LICENSE
├── README.md
├── scripts
│ ├── build_pkg.sh # handy build script for ros workspace
│ ├── docker_build_image.sh # docker image for CI, you can use it locally as well
│ ├── docker_compile_pkgs.sh # repo-build script in docker container
│ ├── Dockerfile # base docker file to build a image: ubuntu 16.04 + ros kinetics
│ ├── docker_install_dependencies.sh # install dependencies in the docker container
│ ├── docker_run.sh # run the docker for the CI test
│ ├── installer # dependencies install scripts go here
│ └── tools # tools scripts
└── src
├── catkin_simple # dependencies, ros package
├── glog_catkin # dependencies, ros package
├── cmake # cmake module folder to hold FindProtobuf.cmake
├── common # apollo module, ros package
├── planning # apollo module, to be done
...
└── other_module_name # apollo module, to be done
Converting Steps
Let’s create a new branch named feature_catkinizing_module
to hold the all the changes we are going to make. Pick a module named module_name
from planning
,control
, routing
, perception
,…, etc. to convert.
1. Create a working branch based on the master branch
git checkout master && git pull # pull the latest codes
git checkout -b feature_catkinizing_module # create a new branch locally
git push --set-upstream origin feature_catkinizing_module # push to the remote
2. Get the source codes of a module:
- Checkout a module
This repo holds a copy of the source codes of apollo-v3.0.0 in branchapollo_3_0_0
. You can use the git command to checkout a folder from the branchapollo_3_0_0
.cd src && git checkout apollo_3_0_0 -- module_name # copy the module_name folder from apollo_3_0_0 to current working branch
- Modify the
#include
directories in the header and source files inmodule_name
directory
All the original files include themodules/module_name
in the#include
lines. But in cmake projects, the prefixmodules/module_name
is not needed. My solution is searching all the.h
and.cc
files formodules/module_name/
and replacing it with an empty string""
. Inport_apollo
repo, I provide a python script scripts/tools/content_hunter.py to do the work automatically. The usage is as below:python scripts/tools/content_hunter.py [module1_name] [module2_name]
The module names here can be
common
,planning
,control
,perception
etc.# for example python scripts/tools/content_hunter.py common
Note: The script only substitutes the string
modules/module_name/
. If the file includemodules/other_module_name
, you may need to remove it manually. Or improve the python script.
3. Add ROS-related files to the module_name
package
- Create a test main file called
module_name_tests.cc
in themodule_name
folder.
This file will call the test cases you are going to add in theCMakeLists.txt
file.#include <gtest/gtest.h> int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
-
Add
CMakeLists.txt
cmake_minimum_required(VERSION 3.0) project(module_name VERSION 0.0.1 LANGUAGES CXX) # enable c++11 feature set(CMAKE_CXX_STANDARD 11) # enable tests set(CATKIN_ENABLE_TESTING ON) # for protobuf to generated interfacing files list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake") ############### #### set the catkin dependend packages set(PKG_DEPS glog_catkin roscpp) find_package(catkin REQUIRED COMPONENTS ${PKG_DEPS}) find_package(Protobuf REQUIRED) # set the .proto definition files set(PROTOS path/to/file.proto # relative to module_name folder ) # call the protobuf command to generate the header and source files PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${PROTOS})
Note: I write a custom cmake module called
FindProtobuf.cmake
to find the protobuf library and related variables. It depends onpkg_check_modules
of the pkgconfig and protobuf.pc to find the right version and return below variablesPROTOBUF_INCLUDE_DIRS PROTOBUF_LIBRARIES RPOTOBUF_FOUND
It also provides a cutom cmake command
PROTOBUF_GENERATE_CPP
to generate the header and source files for the.proto
definition files before compiling the library. Currently, the script does not install.proto
files to the shared folder likecatkin_ws/devel/share
orcatkin_ws/install/share
. The generated files will mirror the.proto
directory structure inmodule_name
folder. So if you need to use the generated header file of some.proto
, just include thedirectory_to_proto
file but replace the extension.proto
with the.pb.h
. For example,#include "common/configs/proto/vehicle_config.pb.h"
forcommon/configs/proto/vehicle_config.proto
. Of course, in the same package, you don’t need the module namecommon
.# global include directories include_directories( ${catkin_INCLUDE_DIRS} ${PROTOBUF_INCLUDE_DIRS} ) # use catkin_package command to expose the header files and the same directory structure to other catkin packages catkin_package( ## export these folder sturcture in the package root folder to other catkin packages INCLUDE_DIRS ${CATKIN_DEVEL_PREFIX}/include LIBRARIES ${PROJECT_NAME} # we are going to wrap all the algorithms to one library named by the module_name CATKIN_DEPENDS ${PKG_DEPS}) # set the source files for the library, you can select the source files you want to test in the module. Be careful with the dependencies. You can check the bazel build files for the dependency tree. You may need other module to be a catkin package first. In that case, convert other module first. Then add other modules as catkin dependencies to PKG_DEPS set(SOURCES path/to/source1.cc path/to/source2.cc) # add library target add_library(${PROJECT_NAME} ${SOURCES} ${PROTO_HDRS} ${PROTO_SRCS}) # set the include directory only for the target target_include_directories(${PROJECT_NAME} PUBLIC $<BUILD_INTERFACE:${CATKIN_DEVEL_PREFIX}/include> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}> ) # link the library target_link_libraries(${PROJECT_NAME} ${catkin_LIBRARIES} ${PROTOBUF_LIBRARIES} ) if (CATKIN_ENABLE_TESTING) catkin_add_gtest(${PROJECT_NAME}_tests module_name_tests.cc path/to/test1.cc path/to/test2.cc) target_include_directories(${PROJECT_NAME}_tests PUBLIC $<BUILD_INTERFACE:${CATKIN_DEVEL_PREFIX}/include${PROJECT_NAME}> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}> ) target_link_libraries(${PROJECT_NAME}_tests ${catkin_LIBRARIES} ${PROJECT_NAME}) endif (CATKIN_ENABLE_TESTING) ############# ## Install ## ############# ## install the headers and directories ## don't miss any subfolders of the root, or you can't include the headers ## from other ros packages # take the `common` module for example set(DIRS proto math util time vehicle_state data configs adapters status transform_listener monitor_log kv_db filters) # install all the headers to devel space since we export ${CATKIN_DEVEL_PREFIX}/include/ # to catkin package instead of the include folder in the root install(DIRECTORY ${DIRS} DESTINATION ${CATKIN_DEVEL_PREFIX}/include/${PROJECT_NAME} FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE ) # install the header files in the module_name root folder install(FILES log.h macro.h DESTINATION ${CATKIN_DEVEL_PREFIX}/include/${PROJECT_NAME} ) # install all the headers to the install space, mirror the structure # install the generated headers install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME} DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION} FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE ) # install all the headers to install space install(DIRECTORY ${DIRS} DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE ) # install header in the module_name root folder to install space install(FILES log.h macro.h DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} )
-
Add
package.xml
, should match the ros packages defined byPKG_DEPS
inCMakeLists.txt
.<package format="2"> <name>module_name</name> <description>apollo module_name module</description> <maintainer email="abc@abc.com">abc</maintainer> <license>Apache 2.0</license> <version>3.0.0</version> <buildtool_depend>catkin</buildtool_depend> <depend>glog_catkin</depend> <depend>roscpp</depend> </package>
4. Compile and Test, in port_apollo
-
Build and run tests
catkin build catkin build --make-args tests ./devel/lib/module_name/module_name
5. Call the algorithms from the module_name
you just converted in your catkin package my_package
-
CMakeLists.txt
find_package(catkin REQUIRED COMPONENTS module_name) catkin_package( CATKIN_DEPENDS module_name )
-
package.xml
<package format="2"> <name>my_package</name> <description>my_package</description> <maintainer email="yu.zhang.bit@gmail.com">Yu Zhang</maintainer> <license>Apache 2.0</license> <version>0.0.1</version> <buildtool_depend>catkin</buildtool_depend> <depend>module_name</depend> </package>
-
Include headers from
module_name
package in the source files of your own package. Just follow the directory structure inmodule_name
package. You can also use clion IDE to help you find the headers automatically.#include <module_name/subfolder/some_header.h>
A working example could be found in package common.
Pull requests for port_apollo are welcome!
Leave a Comment