创建Packages
1、Packages组成
catkin工作区的包必须包含三部分:package.xml、CMakeLists.txt以及每个包单独的目录(文件夹)
最简单的包如下所示的结构:
my_package/
CMakeLists.txt
package.xml
2、CatkinWorkspace中的包
创建catkin软件包的可以使用catkin工作区自带方法,也可以独立构建catkin软件包。一个简单的工作空间如下所示:
workspace_folder/ -- WORKSPACE
src/ -- SOURCE SPACE
CMakeLists.txt -- 'Toplevel' CMake file, provided by catkin
package_1/
CMakeLists.txt -- CMakeLists.txt file for package_1
package.xml -- Package manifest for package_1
...
package_n/
CMakeLists.txt -- CMakeLists.txt file for package_n
package.xml -- Package manifest for package_n
在继续本教程之前,假设按照catkin教程创建一个空的catkin工作区。
3、创建catkin Package
本教程将演示使用catkin_create_pkg脚本创建一个新的catkin包,以及在创建之后可以用来做什么。
首先进入创建的catkin工作区的源空间(src)目录:
$ cd ~/catkin_ws/src
现在可以使用catkin_create_pkg脚本创建一个名为'beginner_tutorials'的新包,它依赖于std_msgs,roscpp和rospy:
$ catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
这将创建一个beginner_tutorials文件夹,其中包含package.xml和CMakeLists.txt,这些文件已填写了给出的依赖信息。
catkin_create_pkg需要提供package_name以及可选的依赖项列表:
# catkin_create_pkg <package_name> [depend1] [depend2] [depend3]
catkin_create_pkg还有更高级的功能,这些功能在catkin/commands/catkin_create_pkg中有描述。
4.构建catkin工作区及获取安装文件
首先您需要在catkin工作区中构建包:
$ cd ~/catkin_ws
$ catkin_make
在构建工作空间之后,在devel子文件夹中创建了一个类似的结构,类是/opt/ros/kinetic(或其他分支)下找到的那样。
其次使用生成的安装文件,将工作区添加到ROS环境:
$ . ~/catkin_ws/devel/setup.bash
5、package 依赖选项
5.1 一阶依赖
第4步使用catkin_create_pkg创建beginner_tutorials包,提供了一些依赖。可以使用rospack工具查看这些依赖:
$ rospack depends1 beginner_tutorials
roscpp
rospy
std_msgs
这些依赖关系存储在package.xml文件中:
$ roscd beginner_tutorials
$ cat package.xml
<package format="2">
...
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>
...
</package>
5.2 间接依赖
在许多情况下,依赖项也会有自己的依赖项。例如,rospy有其他依赖关系。
$ rospack depends1 rospy
genpy
roscpp
rosgraph
rosgraph_msgs
roslib
std_msgs
一个包有时候会有很多的间接依赖包。可以使用rospack递归地查看所有嵌套依赖项。
$ rospack depends beginner_tutorials
cpp_common
rostime
roscpp_traits
roscpp_serialization
catkin
genmsg
genpy
message_runtime
gencpp
geneus
gennodejs
genlisp
message_generation
rosbuild
rosconsole
std_msgs
rosgraph_msgs
xmlrpcpp
roscpp
rosgraph
ros_environment
rospack
roslib
rospy
6、自定义包
本部分将以分析catkin_create_pkg生成的每个文件,逐行解释这些文件的每个组件以及如何自定义包。
6.1 自定义package.xml
浏览package.xml,注意需要修改的元素。为了减少篇幅累赘,截取相关部分描述
6.1.1 描述标签
<description>The beginner_tutorials package</description>
可以将描述标签<description>...<description>的内容改为需要的内容,按照惯例,应该简短,同时涵盖包的范围。
6.1.2 维护者标签
<!-- One maintainer tag required, multiple allowed, one person per tag -->
<!-- Example: -->
<!-- <maintainer email="liqfqq@163.com">飞越疯人院</maintainer> -->
<maintainer email="liqfqq@163.com">飞越疯人院</maintainer>
这是package.xml的必需且重要的标记,因为它允许其他人知道有关该包的联系人。至少需要一个维护者,可以有很多。
6.1.3 许可证标签
<!-- One license tag required, multiple allowed, one license per tag -->
<!-- Commonly used license strings: -->
<!-- BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
<license>以上的许可即可</license>
6.1.4 依赖标签
标记描述了包的依赖关系。依赖项分为build_depend,buildtool_depend,exec_depend,test_depend。有关这些标记的更详细说明,请参阅有关Catkin依赖关系的文档。由于我们将std_msgs,roscpp和rospy作为catkin_create_pkg的参数传递,因此依赖关系将如下所示:
<!-- The *_depend tags are used to specify dependencies -->
<!-- Dependencies can be catkin packages or system dependencies -->
<!-- Examples: -->
<!-- Use build_depend for packages you need at compile time: -->
<!-- <build_depend>genmsg</build_depend> -->
<!-- Use buildtool_depend for build tool packages: -->
<!-- <buildtool_depend>catkin</buildtool_depend> -->
<!-- Use exec_depend for packages you need at runtime: -->
<!-- <exec_depend>python-yaml</exec_depend> -->
<!-- Use test_depend for packages you need only for testing: -->
<!-- <test_depend>gtest</test_depend> -->
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>
除了catkin上的默认buildtool_depend之外,我们列出的所有依赖项都已添加为build_depend。在这种情况下,我们希望所有指定的依赖项在构建和运行时都可用,因此我们也将为每个依赖项添加一个exec_depend标记:
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>rospy</exec_depend>
<exec_depend>std_msgs</exec_depend>
6.1.5 最后 package.xml
最终package.xml:
<?xml version="1.0"?>
<package format="2">
<name>beginner_tutorials</name>
<version>0.1.0</version>
<description>The beginner_tutorials package</description>
<maintainer email="liqfqq@163.com">飞越疯人院</maintainer>
<license>BSD</license>
<url type="website">http://wiki.ros.org</url>
<author email="liqfqq@163.com">飞越疯人院</author>
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>rospy</exec_depend>
<exec_depend>std_msgs</exec_depend>
</package>
6.2 自定义CMakeLists.txt
CMakeLists.txt是用于构建软件包的CMake构建系统的输入文件。任何符合CMake的包都包含一个或多个CMakeLists.txt文件,该文件描述了如何构建代码以及将代码安装到何处。
6.2.1 整体结构和自定义
CMakeLists.txt文件必须遵循此格式,否则您的包将无法正确构建。配置中按以下顺序配置:
-
Required CMake Version (cmake_minimum_required)
-
Package Name (project())
-
Find other CMake/Catkin packages needed for build (find_package())
-
Enable Python module support (catkin_python_setup())
-
Message/Service/Action Generators (add_message_files(), add_service_files(), add_action_files())
-
Invoke message/service/action generation (generate_messages())
-
Specify package build info export (catkin_package())
-
Libraries/Executables to build (add_library()/add_executable()/target_link_libraries())
-
Tests to build (catkin_add_gtest())
-
Install rules (install())
6.2.2 CMake版本
每个catkin的CMakeLists.txt文件必须以所需的CMake版本开头。Catkin需要cmake的版本至少是2.8.3或更高版本。
cmake_minimum_required(VERSION 2.8.3)
6.2.3 Package name
CMake项目函数指定的包的名称。现在以robot_brain的软件包为例:
project(robot_brain)
6.2.4 寻找相关的CMake包
使用find_package函数指定需要找到哪些包来构建项目。catkin总是至少有一个依赖:
find_package(catkin REQUIRED)
如果项目依赖于其他wet packages,它们会自动变成catkin的组件(就CMake而言)。而不是在这些包上使用find_package,如果将它们指定为组件,它将使构建系统快速构建。例如使用包nodelet:
find_package(catkin REQUIRED COMPONENTS nodelet)
相当于:这样是相当麻烦的
find_package(catkin REQUIRED)
find_package(nodelet REQUIRED)
6.2.4.1 find_package()的作用
如果CMake通过find_package找到包,则会导致创建多个CMake环境变量,这些变量提供有关包的信息。然后可以在CMake脚本中使用这些环境变量。
环境变量描述了包导出头文件所在的位置,源文件所在的位置,包所依赖的库以及这些库的路径。名称遵循<PACKAGE NAME> _ <PROPERTY>的约定:
-
<NAME>_FOUND - Set to true if the library is found, otherwise false
-
<NAME>_INCLUDE_DIRS or <NAME>_INCLUDES - The include paths exported by the package
-
<NAME>_LIBRARIES or <NAME>_LIBS - The libraries exported by the package
-
<NAME>_DEFINITIONS - ?
6.2.4.2 为什么Catkin包被指定为组件
Catkin包不是catkin的真正组成部分。相反,CMake的组件功能被用于catkin的设计,以节省大量的编程时间。
对于catkin包,如果使用find_package包含所需要的包,作为catkin的组件,这是有利的,因为使用catkin_前缀创建了一组环境变量,以便将这些包包含到环境变量中去,节省时间。例如,假设您在代码中使用了包nodelet。寻找包的推荐方法是:
find_package(catkin REQUIRED COMPONENTS nodelet)
这样做的好处是nodelet导出的包含路径,库等也会附加到catkin_变量。例如,catkin_INCLUDE_DIRS不仅包含catkin的包含路径,还包含nodelet的包含路径!这将在后续使用中提供便利。
当然我们也可以手动find_package nodelet:
find_package(nodelet)
如果手动添加的话不会将nodelet路径,库等添加到catkin_变量中。
6.2.4.3 Boost线程
如果使用C ++和Boost,则需要在Boost上调用find_package()并指定您正在使用的Boost的哪些方面作为组件。例如,如果你想使用Boost线程,例如:
find_package(Boost REQUIRED COMPONENTS thread)
6.2.5 catkin_package()
catkin_package()是一个catkin提供的CMake宏。为构建系统指定catkin信息所必需的,catkin又用于生成pkg-config和CMake文件。
在使用add_library()或add_executable()声明任何目标之前,必须调用此函数。该函数有5个可选参数:
-
INCLUDE_DIRS - The exported include paths (i.e. cflags) for the package
-
LIBRARIES - The exported libraries from the project
-
CATKIN_DEPENDS - Other catkin projects that this project depends on
-
DEPENDS - Non-catkin CMake projects that this project depends on. For a better understanding, see this explanation.
-
CFG_EXTRAS - Additional configuration options
-
完整的宏文档here.
catkin_package(
INCLUDE_DIRS include
LIBRARIES ${PROJECT_NAME}
CATKIN_DEPENDS roscpp nodelet
DEPENDS eigen opencv)
CMake环境变量$ {PROJECT_NAME}是之前传递给project()函数的内容
6.2.6 指定构建目标
构建目标可以采用多种形式,但通常它们代表两种可能性之一:
- Executable Target - programs we can run
- Library Target - libraries that can be used by executable targets at build and/or runtime
6.2.6.1 目标命名
catkin中的构建目标的名称必须是唯一的。这是CMake的要求。但是,目标的唯一名称仅在CMake内部是必需的。可以使用set_target_properties()函数将目标重命名为其他目标:
set_target_properties(rviz_image_view
PROPERTIES OUTPUT_NAME image_view
PREFIX "")
构建和安装输出中将目标rviz_image_view的名称更改为image_view。
6.2.6.2 自定义输出目录
一般可执行文件和库的默认输出目录通常设置为合理的值,特殊情况下必须自定义。如包含Python绑定的库必须放在不同的文件夹中,以便可以在Python中导入:
set_target_properties(python_module_library
PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION})
6.2.6.3 Include Paths and Library Paths
在编译目标之前,需要指定可以为所述目标找到资源的位置,特别是头文件和库:
- Include Paths - Where can header files be found for the code (most common in C/C++) being built
- Library Paths - Where are libraries located that executable target build against?
-
include_directories(<dir1>, <dir2>, ..., <dirN>)
-
link_directories(<dir1>, <dir2>, ..., <dirN>)
6.2.6.3.1 include_directories()
include_directories的参数是find_package调用生成的* _INCLUDE_DIRS变量以及需要包含的任何其他目录。如果使用catkin和Boost,则include_directories()调用应如下所示:
include_directories(include ${Boost_INCLUDE_DIRS} ${catkin_INCLUDE_DIRS})
第一个参数“include”表示包中的include /目录也是路径的一部分。
6.2.6.3.2 link_directories()
link_directories()函数可用于添加其他库路径,但不建议这样做。所有catkin和CMake软件包在find_packaged时自动添加其链接信息。只需链接到target_link_libraries()中的库
link_directories(~/my_libs)
Please see this cmake thread to see a detailed example of using target_link_libraries() over link_directories().
6.2.7 Executable Targets
构建可执行目标,必须使用add_executable()CMake函数
add_executable(myProgram src/main.cpp src/some_file.cpp src/another_file.cpp)
构建一个名为myProgram的目标可执行文件,它由3个源文件构成:src / main.cpp,src / some_file.cpp和src / another_file.cpp。
6.2.8 Library Targets
add_library()函数用于指定要构建的库。默认情况下,catkin构建共享库。
add_library(${PROJECT_NAME} ${${PROJECT_NAME}_SRCS})
6.2.9 target_link_libraries
使用target_link_libraries()函数指定可执行目标链接的库。这通常在add_executable()调用之后完成。添加$ {catkin_LIBRARIES}
Syntax:
target_link_libraries(<executableTargetName>, <lib1>, <lib2>, ... <libN>)
add_executable(foo src/foo.cpp)
add_library(moo src/moo.cpp)
target_link_libraries(foo moo) -- This links foo against libmoo.so
注意,在大多数用例中不需要使用link_directories(),因为该信息是通过find_package()自动引入的。
7、Messages, Services, and Action Targets
ROS中的消息(.msg),服务(.srv)和操作(.action)文件在构建和使用ROS包之前需要特殊的预处理器进行预处理,将这些宏生成特定于编程语言的文件,以便所选编程语言中可以使用这些消息,服务和操作。构建系统将使用所有可用的生成器(例如gencpp,genpy,genlisp等)生成绑定。提供了三个宏来分别处理消息,服务和操作:
-
add_message_files
-
add_service_files
-
add_action_files
必须在调用以上生成的宏之后,在调用下边的宏:
generate_messages()
7.1 Important Prerequisites/Constraints(重要的先决条件/限制)
以上4个宏必须在catkin_package()宏之前,以便正常工作。
find_package(catkin REQUIRED COMPONENTS ...)
add_message_files(...)
add_service_files(...)
add_action_files(...)
generate_messages(...)
catkin_package(...)
...
catkin_package( )宏必须对message_runtime具有CATKIN_DEPENDS依赖关系。
catkin_package(
...
CATKIN_DEPENDS message_runtime ...
...)
必须将find_package()包含messagesegeneration,可以单独使用,也可以作为catkin的一个组件使用:
find_package(catkin REQUIRED COMPONENTS message_generation)
package.xml文件必须包含对message_generation的构建依赖关系以及对message_runtime的运行时依赖性。如果依赖性是从其他包传递的,那么这不是必需。
构建的目标(或者传递)依赖于需要构建消息/服务/操作,则需要使用catkin_EXPORTED_TARGETS添加显式依赖项,以便它们以正确的顺序构建。除非软件包确实不使用ROS的任何部分,否则这种情况几乎总是适用。然而这种依赖关系不能自动传播。(some_target是add_executable()设置的目标的名称):
add_dependencies(some_target ${catkin_EXPORTED_TARGETS})
如果需要构建消息或服务的包以及使用这些消息的可执行文件,则需要在自动生成的消息目标上创建显式依赖项,以便以正确的顺序构建它们。(some_target是add_executable()设置的目标的名称):
add_dependencies(some_target ${${PROJECT_NAME}_EXPORTED_TARGETS})
如果包满足上述两个条件,则需要添加两个依赖项
add_dependencies(some_target ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
7.2 Example
软件包名为“msg”的目录中有名为“MyMessage1.msg”和“MyMessage2.msg”的两条消息,并且这些消息依赖于std_msgs和sensor_msgs;名为“srv”的目录中的有服务名为“MyService.srv”,定义使用这些消息和服务的有两个分别名为可执行message_program,以及可执行文件do_not_use_local_messages_program,它使用ROS的某些部分,但不包含此程序包中定义的消息/服务,那么您需要在CMakeLists.txt中使用以下内容:
# Get the information about this package's buildtime dependencies
find_package(catkin REQUIRED
COMPONENTS message_generation std_msgs sensor_msgs)
# Declare the message files to be built
add_message_files(FILES
MyMessage1.msg
MyMessage2.msg
)
# Declare the service files to be built
add_service_files(FILES
MyService.srv
)
# Actually generate the language-specific message and service files
generate_messages(DEPENDENCIES std_msgs sensor_msgs)
# Declare that this catkin package's runtime dependencies
catkin_package(
CATKIN_DEPENDS message_runtime std_msgs sensor_msgs
)
# define executable using MyMessage1 etc.
add_executable(message_program src/main.cpp)
add_dependencies(message_program ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
# define executable not using any messages/services provided by this package
add_executable(does_not_use_local_messages_program src/main.cpp)
add_dependencies(does_not_use_local_messages_program ${catkin_EXPORTED_TARGETS})
另外,如果要构建actionlib操作,并在“action”目录中有一个名为“MyAction.action”的操作规范文件,则必须将actionlib_msgs作为组件添加到find_packaged()列表中,并在generate_messages(...)之前添加以下调用:
add_action_files(FILES
MyAction.action
)
此外,包必须具有对actionlib_msgs的构建依赖性。
8、 Enabling Python module support
如果ROS包提供了一些Python模块,则创建一个setup.py文件并调用
catkin_python_setup()
应该放在generate_messages()和catkin_package()之前。
9、 Unit Tests
有一个特定于catkin的宏用于处理名为catkin_add_gtest()的基于gtest的单元测试。
if(CATKIN_ENABLE_TESTING)
catkin_add_gtest(myUnitTest test/utest.cpp)
endif()
10、Optional Step: Specifying Installable Targets
在以上构建时间之后,目标放置到catkin工作空间的开发空间中。通常希望在系统中安装目标,进行系统级安装测试。话句话说希望能够对代码进行“make install”,则需要指定目标应该结束的位置。使用CMake的install( )函数完成安装,该函数的参数
-
TARGETS - which targets to install
-
ARCHIVE DESTINATION - Static libraries and DLL (Windows) .lib stubs
-
LIBRARY DESTINATION - Non-DLL shared libraries and modules
-
RUNTIME DESTINATION - Executable targets and DLL (Windows) style shared libraries
install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} )
以上是标准安装,以下是特定安装Python:
install(TARGETS python_module_library ARCHIVE DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION} LIBRARY DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION} )
10.1 Installing Python Executable Scripts
对于Python代码,安装规则不同,没有使用add_library()和add_executable()函数,以便CMake确定哪些文件是目标以及是什么类型的目标。而是在CMakeLists.txt文件中使用以下内容:
catkin_install_python(PROGRAMS scripts/myscript
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
有关安装python脚本和模块的详细信息,以及文件夹布局的最佳实践,请参阅 catkin manual.。
10.2 Installing header files
头文件必须安装到“include”文件夹,通过安装整个文件夹的文件(可选择按文件名模式过滤并排除SVN子文件夹)来完成的。这可以通过如下安装规则来完成:
install(DIRECTORY include/${PROJECT_NAME}/
DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
PATTERN ".svn" EXCLUDE
)
或者如果include下的子文件夹与包名称不匹配:
install(DIRECTORY include/
DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION}
PATTERN ".svn" EXCLUDE
)
10.3 Installing roslaunch Files or Other Resources
Other resources like launchfiles can be installed to ${CATKIN_PACKAGE_SHARE_DESTINATION}:
install(DIRECTORY launch/
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
PATTERN ".svn" EXCLUDE)