ROS新建工作区(workspace)与包(package)编译的实践(C++示例)

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

多了一个builddevel的目录,其中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.txtpackage.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="[email protected]">Jane Doe</maintainer> -->
  <maintainer email="[email protected]">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="[email protected]">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中。 

猜你喜欢

转载自blog.csdn.net/weixin_41896770/article/details/132273124