引言
其实从前几篇文章中我也有提及过ROS 工作空间,而且代码也是在工作空间中编译、运行的。

其实在 ROS 中,我们创建的软件代码,都会被组织成功能包的形式,所以在我们开始代码之前,第一步是创建一个容纳我们的工作空间。

因此本文将介绍ROS工作空间、package 及 catkin 编译系统。

ROS系列文章

ROS 工作空间
简单地说,ROS 工作空间(ROS workspace)就是统一存放 ROS 文件的地方,这里的文件包括源码、功能包、编译产生的中间文件等,一个项目的所有 ROS 文件最好都放在一个 workspace 中,便于统一管理、编译和调用。

一般来说,对于初学者,ROS的工作空间不需要经常建立,因为很多代码只是demo,一般只建立一个就好了,但是如果你的工作涉及大量的功能包,或者涉及几个相互独立的的项目,还是建立多个工作空间比较好,这东西看情况使用就好了。

我们可以在任意位置创建工作目录,一般来说工作空间都有4个文件夹:

src:代码空间,是用来放置我们的功能包的,包括功能包的代码、功能包的配置文件配置等,这个功能包可以是自己写的,也可以是别人的功能包,总之都放在这。

build:编译空间,里面会有编译时产生的一些中间的文件,这个文件夹大家基本上是不用去关心的。

devel:开发空间,里面会放置我们编译生成的一些可执行文件,还包括一些库、一些脚本等等都放在这里。

install: 安装空间,在编译成功后,可以使用make install命令将可执行文件安装到该空间中,这个文件夹并不一定需要,实际上很多工作空间都不需要这个文件夹。

创建工作空间
在任意目录下创建一个文件夹,名字你随意起,我就起ros_ws,然后在这个文件夹下创建src文件夹:


ros017

然后在这个src文件夹下运行以下命令去初始化工作空间:

catkin_init_workspace

然后你就会发现在src目录下生成了一个CMakeLists.txt文件,这其实是CMake的构建规则的文件,这个文件包含了一系列的编译指令,包括应该生成哪种可执行文件,需要哪些源文件,以及在哪里可以找到所需的头文件和链接库。当然,这个文件表明 catkin 在内部使用了 CMake。有兴趣的可以去看看我以前的博文,就是讲解CMake的。

编译工作空间
工作空间里面虽然没有任何代码,但是你依然可以去编译啊,只不过是一个空的工程,那在这里我们一样可以针对ROS的空的空间做编译,大家可以来熟悉一下这个编译的方式,我们要回到你的这个工作空间的根目录ros_ws,在这个目录下进行编译。

cd ..

# 编译
catkin_make

# 输出内容

···
···
-- Using Python nosetests: /usr/bin/nosetests-2.7
-- catkin 0.7.23
-- BUILD_SHARED_LIBS is on
-- BUILD_SHARED_LIBS is on
-- Configuring done
-- Generating done
-- Build files have been written to: /home/jie/ros_ws/build

看到有对应的内容输出了,就表示编译成功,然后你的ros_ws目录下回多了两个文件夹,分别为build、devel,这两个文件夹的作用就是前面讲解的了。

导出环境变量
devel目录就是我们的环境变量,假设我们当前的工作空间有代码,那么编译成功后会产生可执行文件,那么ROS如何去找到这个可执行文件呢,它不是全能的,需要我们去指定这个可执行文件在哪里,它才能找到,因此我们需要将编译产生的可执行文件、脚本等内容都要告诉系统,我称之为导出环境变量。

如果你查看一下在devel文件夹里面你可以看到几个setup.sh文件。source这些文件中的任何一个都可以将当前工作空间设置在ROS工作环境的最顶层。

如果你的终端是bash(Ubuntu默认终端),那么就运行:

source devel/setup.bash 

我个人用的终端是zsh,我是修改过了的,我运行以下命令:

source devel/setup.zsh

然后用echo $ROS_PACKAGE_PATH命令去检查环境变量是否导出,当输出包含了你工作空间的目录就表示环境变量已经导出成功了:

➜  ros_ws echo $ROS_PACKAGE_PATH 

/home/jie/ros_ws/src:/opt/ros/melodic/share

创建功能包

写代码就要创建一个功能包,放着源码文件,在工作空间的src目录下可以直接运行catkin_create_pkg命令去创建功能包,这个命令有非常多的功能,你可以通过catkin_create_pkg -h去细看它的功能,而我就简单讲解一下:

简单的命令用法:

catkin_create_pkg [功能包名称] [依赖功能包1] [依赖功能包2] [依赖功能包n] 

创建mytopic001功能包,依赖roscpp、rospy、std_msgs这几个功能包,其实rospy不是必须的,我们目前写的是c++的代码,不过放进去都无所谓。

catkin_create_pkg mytopic001 roscpp rospy std_msgs

catkin_create_pkg命令在创建用户功能包时会生成catkin构建系统所需的CMakeLists.txt和package.xml文件,还有存放代码的src文件夹,和include文件夹,注意这里的src并不是工作目录下的src,而是功能包的src文件夹。

ros018

你可以手动去修改功能包的依赖关系,比如编辑package.xml文件:

ros019

添加源代码

我们将上一篇文章【ROS入门-4】嘴对嘴讲解ROS的核心概念——ROS话题通信机制的源代码添加到这个工作空间中,存放的位置是:ros_ws/src/mytopic001/src

ros020

修改构建规则

修改ros_ws/src/mytopic001目录下的CMakeLists.txt文件就是修改对应的构建规则,可以在这个文件中设置可执行文件的创建规则、依赖关系、连接关系等等。

add_executable(publisher_topic src/publisher_topic.cpp)
target_link_libraries(publisher_topic ${catkin_LIBRARIES})

add_executable(subscriber_topic src/subscriber_topic.cpp)
target_link_libraries(subscriber_topic ${catkin_LIBRARIES})

ros021

其实如果你会了CMake后,这东西是非常简单的。。。上面的操作起始就是编译两个可执行文件publisher_topic和subscriber_topic,分别执行发布与订阅的操作。

编译功能包
回到工作空间的根目录下ros_ws,运行catkin_make命令编译,当输出以下内容时,表示编译成功:

catkin_make

# 输出内容
···
···
Scanning dependencies of target subscriber_topic
Scanning dependencies of target publisher_topic
[ 50%] Building CXX object mytopic001/CMakeFiles/publisher_topic.dir/src/publisher_topic.cpp.o
[ 50%] Building CXX object mytopic001/CMakeFiles/subscriber_topic.dir/src/subscriber_topic.cpp.o
[ 75%] Linking CXX executable /home/jie/ros_ws/devel/lib/mytopic001/publisher_topic
[100%] Linking CXX executable /home/jie/ros_ws/devel/lib/mytopic001/subscriber_topic
[100%] Built target publisher_topic
[100%] Built target subscriber_topic

验证步骤如下:首先导出环境变量(注意根据你的终端配置运行命令)

source devel/setup.zsh

运行节点管理器

roscore

在新终端运行发布者节点,也是需要导出环境变量的,不然ROS找不到你的可执行程序位置

source ros_ws/devel/setup.zsh

rosrun mytopic001 publisher_topic 

在新终端运行订阅者者节点

source ros_ws/devel/setup.zsh

rosrun mytopic001 subscriber_topic 

最终效果:

ros022

参考

张老师的书籍《ROS_Robot_Programming》

CMake实战教程