ROS工作区是用来存放ROS工作包的目录,这些工作包,包含了ROS程序的各种文件,包括源代码、配置文件、消息文件等。所以工作区的本质是一个文件夹,用来存放接下来将要介绍的包以及构建这些包所需的文件。ROS工作区可以新建很多,它们之间都是相互独立的,在各自的工作区运行着特定的程序,这样避免了不相关的程序放在一起,也遵循着松耦合的设计理念,这样做的好处是显而易见的,同样的功能放在一起,也便于调试。
1、ROS工作区
新建工作区(目录)
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
指定-p可以新建多个不存在的目录,也就是这条路径所有的目录,如果不存在都将新建。另外新建工作区,我们一般建立在当前用户的根目录下 ~/
初始化工作区
catkin_init_workspace
#Creating symlink "/home/yahboom/catkin_ws/src/CMakeLists.txt" pointing to "/opt/ros/melodic/share/catkin/cmake/toplevel.cmake"
这里实质是创建了一个指向toplevel.cmake的符号链接,这样做的结果,是让整个系统可见。
工作区创建并初始化好了之后,再来创建一些其他的工作区文件:
cd ~/catkin_ws
catkin_make
命令catkin_make会编译所有节点并保证所有依赖都是最新的。我们查看下src目录多出了哪些目录:ls
build devel src
多了一个build和devel的目录,其中build目录里面是使用C++时catkin存放库和可执行程序的地方,如果我们使用Python的情况就可以忽略build里面的内容,而devel里面主要关注setup.bash文件,运行这些文件,可以使系统使用这个工作区以及其中包含的代码。
通过下面的命令来配置,将setup.bash追加(>>)到.bashrc中去:
echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc
source ~/.bashrc
工作区就这么愉快的新建好了。 接下来为了熟悉全面点,使用C++来做一个编译,看下需要做哪些相关操作。
2、ROS包
“包”的名字,在Ubuntu中的软件也是以包的形式存在,但是ROS中的包,跟Ubuntu的包有区别,由于专门讲机器人操作系统,所以后期说的“包”都是指代ROS包,如果是Ubuntu包就会特别说明是Ubuntu包。
创建包的命令:
catkin_create_pkg tony_code rospy
这个catkin_create_pkg命令,在前面的文章, ROS机器人操作系统Catkin的编译与常用命令的使用介绍 ,也出现过,是一种很常见的命令。
这样就成功创建了一个名为tony_code的新包,里面有CMakeLists.txt和package.xml文件,还有一个src文件夹用来存放实际源代码,该包依赖已经存在的rospy包,如果你的包还依赖其他的包,可以在命令行中列出。
这个工作区的一个目录结构大致如下:
|---catkin_ws/
|------build/
|------devel/
|------src/
|----CMakeLists.txt
|----tony_code/
|----CMakeLists.txt package.xml src
|----Other/
|----CMakeLists.txt package.xml src
这里会出现几个CMakeLists.txt需要区分开来,在工作区的根目录的src目录下面有一个之外,每个包里面也都有一个CMakeLists.txt
2.1、CMakeLists.txt
在上面说了除了工作区一个CMakeLists.txt之外,每个包也有一个,我们在编译包的时候,进行修改的就是对应包下面的CMakeLists.txt
这个对于熟悉Make的读者来说,就很简单了,这个文件是定义如何编译工作包里面的源代码的。
我们来查看下新建包tony_code里面的CMakeLists.txt文件的内容:cat CMakeLists.txt
cmake_minimum_required(VERSION 3.0.2)
project(tony_code)## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
rospy
)## System dependencies are found with CMake's conventions
# find_package(Boost REQUIRED COMPONENTS system)
## Uncomment this if the package has a setup.py. This macro ensures
## modules and global scripts declared therein get installed
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
# catkin_python_setup()################################################
## Declare ROS messages, services and actions ##
################################################## To declare and build messages, services or actions from within this
## package, follow these steps:
## * Let MSG_DEP_SET be the set of packages whose message types you use in
## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
## * In the file package.xml:
## * add a build_depend tag for "message_generation"
## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
## * If MSG_DEP_SET isn't empty the following dependency has been pulled in
## but can be declared for certainty nonetheless:
## * add a exec_depend tag for "message_runtime"
## * In this file (CMakeLists.txt):
## * add "message_generation" and every package in MSG_DEP_SET to
## find_package(catkin REQUIRED COMPONENTS ...)
## * add "message_runtime" and every package in MSG_DEP_SET to
## catkin_package(CATKIN_DEPENDS ...)
## * uncomment the add_*_files sections below as needed
## and list every .msg/.srv/.action file to be processed
## * uncomment the generate_messages entry below
## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)## Generate messages in the 'msg' folder
# add_message_files(
# FILES
# Message1.msg
# Message2.msg
# )## Generate services in the 'srv' folder
# add_service_files(
# FILES
# Service1.srv
# Service2.srv
# )## Generate actions in the 'action' folder
# add_action_files(
# FILES
# Action1.action
# Action2.action
# )## Generate added messages and services with any dependencies listed here
# generate_messages(
# DEPENDENCIES
# std_msgs # Or other packages containing msgs
# )################################################
## Declare ROS dynamic reconfigure parameters ##
################################################## To declare and build dynamic reconfigure parameters within this
## package, follow these steps:
## * In the file package.xml:
## * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
## * In this file (CMakeLists.txt):
## * add "dynamic_reconfigure" to
## find_package(catkin REQUIRED COMPONENTS ...)
## * uncomment the "generate_dynamic_reconfigure_options" section below
## and list every .cfg file to be processed## Generate dynamic reconfigure parameters in the 'cfg' folder
# generate_dynamic_reconfigure_options(
# cfg/DynReconf1.cfg
# cfg/DynReconf2.cfg
# )###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES tony_code
# CATKIN_DEPENDS rospy
# DEPENDS system_lib
)###########
## Build ##
############# Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
${catkin_INCLUDE_DIRS}
)## Declare a C++ library
# add_library(${PROJECT_NAME}
# src/${PROJECT_NAME}/tony_code.cpp
# )## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
# add_executable(${PROJECT_NAME}_node src/tony_code_node.cpp)## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})## Specify libraries to link a library or executable target against
# target_link_libraries(${PROJECT_NAME}_node
# ${catkin_LIBRARIES}
# )#############
## Install ##
############## all install targets should use catkin DESTINATION variables
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
# catkin_install_python(PROGRAMS
# scripts/my_python_script
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )## Mark executables for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
# install(TARGETS ${PROJECT_NAME}_node
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )## Mark libraries for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
# install(TARGETS ${PROJECT_NAME}
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
# )## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
# FILES_MATCHING PATTERN "*.h"
# PATTERN ".svn" EXCLUDE
# )## Mark other files for installation (e.g. launch and bag files, etc.)
# install(FILES
# # myfile1
# # myfile2
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )#############
## Testing ##
############### Add gtest based cpp test target and link libraries
# catkin_add_gtest(${PROJECT_NAME}-test test/test_tony_code.cpp)
# if(TARGET ${PROJECT_NAME}-test)
# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()## Add folders to be run by python nosetests
# catkin_add_nosetests(test)
从CMakeLists.txt文本内容我们也大概了解到,是用于描述构建一个或多个软件项目的配置信息,通过编辑配置,CMake就可以根据项目的配置信息自动生成构建脚本,以编译、测试和安装软件项目。
按照自己的需求,需要用到什么就去掉注释,比如配置中,需要添加头文件的目录,就开启include_directories
在使用opencv时需要包含/usr/local/include/opencv/cv.h这个头文件。我们将它的目录包含进来:include_directories(/usr/local/include) 在调用函数时添加: #include "opencv/cv.h"
其他的配置,将在后面需要用到的时候,进行说明。
2.2、package.xml
这个package.xml文件包含了工作包的元信息,如程序包名称、版本号、依赖关系等。
我们查看下这个文件的内容:cat package.xml
<?xml version="1.0"?>
<package format="2">
<name>tony_code</name>
<version>0.0.0</version>
<description>The tony_code package</description>
<!-- One maintainer tag required, multiple allowed, one person per tag -->
<!-- Example: -->
<!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
<maintainer email="yahboom@todo.todo">yahboom</maintainer>
<!-- 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>TODO</license>
<!-- Url tags are optional, but multiple are allowed, one per tag -->
<!-- Optional attribute type can be: website, bugtracker, or repository -->
<!-- Example: -->
<!-- <url type="website">http://wiki.ros.org/tony_code</url> -->
<!-- Author tags are optional, multiple are allowed, one per tag -->
<!-- Authors do not have to be maintainers, but could be -->
<!-- Example: -->
<!-- <author email="jane.doe@example.com">Jane Doe</author> -->
<!-- The *depend tags are used to specify dependencies -->
<!-- Dependencies can be catkin packages or system dependencies -->
<!-- Examples: -->
<!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
<!-- <depend>roscpp</depend> -->
<!-- Note that this is equivalent to the following: -->
<!-- <build_depend>roscpp</build_depend> -->
<!-- <exec_depend>roscpp</exec_depend> -->
<!-- Use build_depend for packages you need at compile time: -->
<!-- <build_depend>message_generation</build_depend> -->
<!-- Use build_export_depend for packages you need in order to build against this package: -->
<!-- <build_export_depend>message_generation</build_export_depend> -->
<!-- Use buildtool_depend for build tool packages: -->
<!-- <buildtool_depend>catkin</buildtool_depend> -->
<!-- Use exec_depend for packages you need at runtime: -->
<!-- <exec_depend>message_runtime</exec_depend> -->
<!-- Use test_depend for packages you need only for testing: -->
<!-- <test_depend>gtest</test_depend> -->
<!-- Use doc_depend for packages you need only for building documentation: -->
<!-- <doc_depend>doxygen</doc_depend> -->
<buildtool_depend>catkin</buildtool_depend>
<build_depend>rospy</build_depend>
<build_export_depend>rospy</build_export_depend>
<exec_depend>rospy</exec_depend>
<!-- The export tag contains other, unspecified, tags -->
<export>
<!-- Other tools can request additional information be placed here -->
</export>
</package>
这个文件包含了一组和新包有关的元数据。分别来介绍下其中的节点:
name:程序包的名字,这个节点不能修改!!
version:程序包的版本号
description:程序包功能的简短描述
maintainer:程序包的维护者和修复bug的,可以多人
license:许可证
url:程序包的URL
author:程序包的作者
buildtool_depend:构建工具,在ROS里目前基本都是catkin
build_depend:依赖项
export:catkin以外的其他工具需要的信息
3、编译C++
有了上面的知识铺垫,我们用C++来写一个最简单的节点,然后对其进行编译,熟悉整个过程以及遇到的错误。
3.1、新建minimal包
按照上述介绍的,新建一个新的包,这里名字叫minimal
cd ~/catkin_ws/src
catkin_create_pkg minimal rospy
3.2、C++代码
创建包之后,我们在这个minimal包下面的src目录里建一个C++文件
cd minimal/src
gedit minimal.cpp
minimal.cpp内容如下
#include <ros/ros.h>
int main(int argc,char **argv)
{
ros::init(argc,argv,"minimal");
ros::NodeHandle n;
ros::spin();
return 0;
}
这个是最简单的C++写的一个ROS节点,包含一个ROS头文件。
ros::init(argc,argv,"minimal"); 初始化节点,并命名为minimal,相当于是命名空间
ros::NodeHandle n; 创建一个节点句柄,用来创建话题、服务和动作。在Python中不需要这样显式地创建节点句柄,因为ROS的Python接口可以隐式地实现
ros::spin(); 一直等待ROS的调度,简单理解的话,比如订阅的时候处理回调函数,会一直循环进行订阅的意思。当然后期会遇到有ros::spinOnce();这样一个函数,意思是一样的,只不过这个一般是放在循环中。
3.3、修改CMakeLists.txt
然后就是对配置文件进行修改了,这里依然需要注意,使用的是minimal这个包下面的CMakeLists.txt文件
cd ~/catkin_ws/src/minimal
gedit CMakeLists.txt
修改下面三处地方,并将注释去掉,使其生效:
find_package(catkin REQUIRED rospy)
add_executable(minimal src/minimal.cpp)
target_link_libraries(minimal ${catkin_LIBRARIES})
3.4、编译
配置好了之后,我们就开始编译:
cd ~/catkin_ws
catkin_make
这里编译的时候,需要注意的是,要回到工作区的根目录进行编译。
虽然三处地方修改了,但编译还是报如下的错误,找不到头文件ros/ros.h,这是一个很常见的错误:
Scanning dependencies of target minimal
[ 50%] Building CXX object minimal/CMakeFiles/minimal.dir/src/minimal.cpp.o
/home/yahboom/catkin_ws/src/minimal/src/minimal.cpp:1:10: fatal error: ros/ros.h: No such file or directory
#include <ros/ros.h>
^~~~~~~~~~~
compilation terminated.
minimal/CMakeFiles/minimal.dir/build.make:62: recipe for target 'minimal/CMakeFiles/minimal.dir/src/minimal.cpp.o' failed
make[2]: *** [minimal/CMakeFiles/minimal.dir/src/minimal.cpp.o] Error 1
CMakeFiles/Makefile2:431: recipe for target 'minimal/CMakeFiles/minimal.dir/all' failed
make[1]: *** [minimal/CMakeFiles/minimal.dir/all] Error 2
Makefile:140: recipe for target 'all' failed
make: *** [all] Error 2
Invoking "make -j2 -l2" failed
3.5、编译错误处理
针对上面编译的错误,在上面也有介绍,这里出现了头文件,所以需要打开include_directories
include_directories(include ${catkin_INCLUDE_DIRS})
然后再次编译,依然出错,启动节点管理器roscore,是可以正常启动的,所以环境变量配置是没有问题的。原因在于这里用了C++,所以需要这个roscpp依赖包,修改如下:
find_package(catkin REQUIRED COMPONENTS rospy roscpp)
然后再次编译就没有问题了。如图:
这样就将编译的minimal可执行程序,放在了~/catkin_ws/devel/lib/minimal/minimal中。