创建和编译Package
在ROS中,软件是以Package为基本单位组织的。一个Package中可能包含了可执行文件、配置文件、第三方的软件等等任何可以构成一个有用模块的东西。 把软件划分称为各个Package的一个主要目的是,降低系统的耦合度,提高代码的复用率。ROS的Package遵循如下的设计原则:
Enough functionality to be useful, but not too much that the package is heavyweight and difficult to use from other software.
虽然Package中可能包涵各种各样的文件,具体的组织方式也没有明确的说法。但是一个Package之所以是Package,是因为它里面至少包含了两个文件:package.xml和CMakeLists.txt。 package.xml描述了一个Package中的各种信息,包括作者信息,版权,依赖关系等。CMakeLists.txt则是ROS的编译系统catkin需要使用的文件,它描述了Package的各种编译规则, 需要的源文件、库文件、搜索路径等。一个Package都有用一个独立的目录,保存它的各种文件,package.xml和CMakeLists.txt则保存在它的根目录下。
我们可以手动创建一个Package,就是麻烦一点,不过有比较高的自定义特性。也可以用ROS的工具catkin_create_pkg来创建,比较简单方便。 在本文中,我们主要讲ROS系统的Package,所以先介绍通过工具创建的方法,以后再讲手动创建的方法。
1. 创建一个catkin package
首先,我们把工作目录切换到上一篇文章最后创建的工作空间下。 然后运行catkin_create_pkg创建一个叫做beginner_tutorials的Package,它依赖与std_msgs、rospy、roscpp三个包。
$ cd ~/catkin_ws/src
$ catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
Created file beginner_tutorials/package.xml
Created file beginner_tutorials/CMakeLists.txt
Created folder beginner_tutorials/include/beginner_tutorials
Created folder beginner_tutorials/src
Successfully created files in /home/gyc/catkin_ws/src/beginner_tutorials. Please adjust the values in package.xml.
根据提示信息,我们可以看到catkin_create_pkg创建了一个beginner_tutorials的目录,并在其中创建了package.xml和CMakeLists.txt两个文件,以及include和src两个子目录。
打开创建的那两个文件,可以发现catkin_create_pkg已经根据我们提供的信息,在package.xml和CMakeLists.txt中添加了一些内容,剩下的工作是根据我们的需要补充和修改一些信息就好了。
利用工具,我们仅用了一条语句就创建了一个Package,虽然现在还什么功能都还没有实现,但我们也可以进行编译。
$ cd ~/catkin_ws
$ catkin_make
然后添加工作空间的环境,我们就可以用ros提供的各种文件系统工具访问新创建的Package了。
$ source devel/setup.bash
$ rosls beginner_tutorials/
CMakeLists.txt include package.xml src
2. package.xml
package.xml中描述了一个Package的作者信息、版权、依赖关系等信息。下面是由catkin生成的package.xml的内容,我去除了其中的注释部分。
<?xml version="1.0"?>
<package>
<name>beginner_tutorials</name>
<version>0.0.0</version>
<description>The beginner_tutorials package</description>
<maintainer email="gyc@todo.todo">gyc</maintainer>
<license>TODO</license>
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>
<run_depend>roscpp</run_depend>
<run_depend>rospy</run_depend>
<run_depend>std_msgs</run_depend>
</package>
大体上,package.xml文件分为两个部分,其中name, version, description等部分就是对package的各种描述,他们对于怎么编译package不起任何作用,就是一些描述信息。
而buildtool_depend,build_depend, run_depend部分则描述了package对于其它工具的依赖关系。在编译和运行时,ros系统会根据这些描述信息,在系统中查找对应的工具包,
如果没有找到将会报错。
这里的依赖关系中,std_msgs, rospy和roscpp是我们在创建package时指定的依赖关系,运行指令rospack的depends1可以查看一个package的直接依赖关系:
$ rospack depends1 beginner_tutorials
roscpp
rospy
std_msgs
我们可以通过修改这个package文件删除或者添加依赖关系。比如说,我们把关于std_msgs的两个依赖删除,然后运行rospack,就可以看到它的直接依赖关系中少了std_msgs。
$ rospack depends1 beginner_tutorials
roscpp
rospy
一个package依赖于某个工具,而该工具则可能依赖于另外一些工具,这样就形成了一系列的间接依赖关系。通过rospack的depends可以查看package所依赖的所有工具:
$ rospack depends beginner_tutorials
cpp_common
rostime
roscpp_traits
roscpp_serialization
catkin
...
3. CMakeLists.txt
CMakeLists.txt是CMake编译系统下用于描述代码的编译规则的文件,熟悉CMake的读者应该对它的工作流程比较了解,只需要执行如下的一系列指令就可以完成一个项目的编译和安装:
# In a CMake project
$ mkdir build
$ cd build
$ cmake ..
$ make
$ make install # (optionally)
在上述的cmake工作流程中,我们需要保证在项目的根目录下有一个CMakeLists.txt的文件。再创建一个build目录用于存放编译过程中生成的各种中间文件,
在build目录下运行指令cmake ..解决编译过程中需要的查询路径、库文件,进而生成一个Makefile文件。其中..指的是build的父目录,即包含CMakeLists.txt的根目录。
然后运行make编译项目,必要的话还要运行make install安装项目。
在ROS的catkin_make编译环境下,对cmake的这一工作流程做了很多封装,以至于我们可以一键编译工作空间下的所有package。按照cmake的常规使用方式, 我们需要在package的根目录下创建一个CMakeLists.txt的文件。
在使用catkin_make编译时,我们需要先保证系统中已经安装了package的所有依赖,否则在编译过程中将因为没有找到相应的依赖而报错退出。 同时还需要保证已经导入了ROS的工作环境,如果没有则运行如下指令导入,然后在工作空间的根目录下运行catkin_make进行编译。
$ source /opt/ros/kinetic/setup.bash
$ cd ~/catkin_ws
$ catkin_make
上述指令,将编译在src目录下找到的所有package。如果我们根据需要把package放到了一个其它的目录下,通过参数--source指定package的路径即可。
$ catkin_make --source $PACKAGE_PATH
4. 总结
package是ROS系统中软件的基本组织形式。每一个package都有一个独立的目录,在这个目录中至少包含package.xml和CMakeLists.txt分别描述了package的依赖关系和编译规则。 我们可以用catkin_create_pkg自动的创建一个package,使用catkin_make对其进行编译。在没有指定参数的情况下,catkin_make会把其中src目录下见到的所有package都给编译的。