[ROS] 手把手教你如何从无到有构建一个ROS软件包

1、一个ROS软件包由什么组成

  • 一个符合catkin规范的package.xml文件

    • 这个package.xml文件提供有关该软件包的元信息,包括作者信息、版权、依赖
  • 一个catkin版本的CMakeLists.txt文件

    • 如果ROS软件包是一个catkin元包的话,则需要有一个CMakeLists.txt文件的相关模板,是ROS的编译系统catkin需要使用的文件,描述了package的各种编译规则,需要的源文件、库文件、搜索路径等
  • 每个包必须有自己独立的目录,保存各种文件,package.xml和CMakeList.txt保存在根目录下

    • 这意味着在同一个目录下不能有嵌套的或者多个软件包存在
    ROS_package/
    	CMakeLists.txt
    	package.xml
    

2、catkin工作空间中的软件包

一个简单的catkin工作空间

catkin_workspace/               工作空间
	src/			   	      源码
		CMakeLists.txt         顶层CMake文件,由cmake产生
		ROS_package_1/         第一个ROS包
			CMakeLists.txt
			package.xml
		ROS_package_2/         第二个ROS包
			CMakeLists.txt
			package.xml

3、创建catkin软件包

  • 首先切换到catkin工作空间中源文件空间目录

    cd ~/catkin_ws/src
    
  • 使用catkin_create_pkg命令创建一个名为beginner_tutorials的新ROS软件包,这个软件包依赖于std_msgs、roscpp、rospy

    catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
    
  • 产生一个名为beginner_tutorials的文件夹,这个文件夹里面包含一个package.xml文件和一个CMakeLists.txt文件,这两个文件都已经部分填写了执行catkin_create_pkg命令时提供的信息

4、构建一个catkin工作区并生效配置文件

  • 构建软件包

    cd ~/catkin_ws
    catkin_make
    

    在devel子目录下创建了一个通常在/opt/ros/kinetic/下看到的目录结构类似的结构

  • 将这个工作空间添加到ROS环境中,通常需要source一下生成的配置文件

    source ~/catkin_ws/devel/setup.bash
    

5、软件依赖关系

  • 一级依赖

    在catkin_create_pkg时提供了几个软件包作为依赖关系,可以使用rospack命令工具来查找这些一级依赖包

    rospack depends1 beginner_tutorials
    # 如下列出了在运行catkin_create_pkg命令时作为参数的依赖包,这些依赖关系存储在package.xml中
    std_msgs
    rospy
    roscpp
    
  • 间接依赖

    一个依赖包还会有它自己的依赖关系,比如rospy就有其他依赖包

    rospack depends1 rospy
    # 以下列出rospy的依赖包
    genpy
    roscpp
    rosgraph
    rosgraph_msgs
    roslib
    std_msgs
    

6、自定义软件包

  • 自定义package.xml

    剖析一下自动生成的package.xml文件

    • 大体上,package.xml文件分为两个部分

      • 其中name,version,description等部分就是对package的各种描述,它们对于怎么编译package不起任何作用,就是一些描述信息
      • buildtool_depend,build_depend,run_depend部分则描述了package对于其他工具的依赖关系,在编译和运行时,ROS会根据这些描述信息,在系统中查找对应的工具包,如果没有找到将会报错
    • 描述标签

      description标签:

      <description>The beginner_tutorials packagedescription>
      

      可以将描述信息修改为任何你喜欢的内容,但是按照惯例,第一句话应该简短一些,因为它包含了软件包的范围。

    • 维护者标签

      maintainer标签:

       
      
      
      <maintainer email="[email protected]">usermaintainer>
      

      这是package.xml文件要求填写的一个重要标签,它能够让其他人联系到软件包的相关人员。

    • 许可证标签

      license标签:

      
      
      
      <license>TODOlicense>
      

      选择一个开源许可证填写到这里。常见的开源许可证:BSD、MIT、Boost Software License、GPLv2、GPLv3、LGPLv2.1和LGPLv3。

    • 依赖项目标签

      描述了依赖关系,这些依赖项分别为:build_dependbuildtool_dependrun_dependtest_depend

      除了catkin默认提供的buildtool_depend,所有我们列出的依赖包都已经被添加到build_depend标签中。

      <buildtool_depend>catkinbuildtool_depend>
      
      <build_depend>roscppbuild_depend>
      <build_depend>rospybuild_depend>
      <build_depend>std_msgsbuild_depend>
      
      <build_export_depend>roscppbuild_export_depend>
      <build_export_depend>rospybuild_export_depend>
      <build_export_depend>std_msgsbuild_export_depend>
      
      <exec_depend>roscppexec_depend>
      <exec_depend>rospyexec_depend>
      <exec_depend>std_msgsexec_depend>
      
    • 最终的package.xml

      
      <package format="2">
        <name>beginner_tutorialsname>
        <version>0.1.0version>
        <description>The beginner_tutorials packagedescription>
      
        <maintainer email="[email protected]">Your Namemaintainer>
        <license>BSDlicense>
        <url type="website">http://wiki.ros.org/beginner_tutorialsurl>
        <author email="[email protected]">Jane Doeauthor>
      
        <buildtool_depend>catkinbuildtool_depend>
      
        <build_depend>roscppbuild_depend>
        <build_depend>rospybuild_depend>
        <build_depend>std_msgsbuild_depend>
      
        <build_export_depend>roscppbuild_export_depend>
        <build_export_depend>rospybuild_export_depend>
        <build_export_depend>std_msgsbuild_export_depend>
      
        <exec_depend>roscppexec_depend>
        <exec_depend>rospyexec_depend>
        <exec_depend>std_msgsexec_depend>
      
      package>
      
  • 自定义CMakeLists.txt

    CMakeList.txt是CMake编译系统下用于描述代码的编译规则的文件,通过一系列的指令就可以完成一个项目的编译和安装,在ROS的catkin_make编译环境下,对cmake的这一工作流程做了很多封装

    在使用catkin_make编译时,需要先确保系统中已经安装了package的所有依赖,否则编译过程中将因为没有找到相应的依赖而报错退出,还需要保证已经导入了ROS工作环境

    这些文件描述了如何构建代码以及将代码安装到何处

    编写CMakeList.txt最常用的功能就是调用其他的.h头文件和.so/.a库文件,将.cpp/.c/.cc文件编译成可执行文件或者新的库文件

    • 整体结构和排序

      CMakeLists.txt文件必须遵循固定格式,顺序很重要:

      • 所需的CMake版本:cmake_minimum_required
      • 包名:project()
      • 查找构建所需的其他CMake/catkin包:find_package()
      • 启用python模块支持:catkin_python_setup()
      • 消息、服务、动作生成器:add_message_files(), add_service_files(), add_action_files()
      • 调用消息、服务、操作生成:generate_messages()
      • 指定包构建信息导出:catkin_package()
      • 需要构建的库/可执行文件:add_library()/add_executable()/target_link_libraries()
      • 要构建的测试:catkin_add_gtest()
      • 安装规则:install()
    • CMake版本

      每个CMakeLists.txt文件都必须以所需的CMake版本开头,catkin需要2.8.3或者更高版本的CMake

      cmake_minimum_required(VERSION 2.8.3)
      
    • 包名

      由CMake项目函数指定的包的名称,我以自己做的项目为例

      project(visual)
      

      会自动创建两个变量,PROJECT_SOURCE_DIR和PROJECT_NAME

      • PROJECT_SOURCE_DIR:本CMakeList.txt所在的文件夹路径
      • PROJECT_NAME:本CMakeList.txt的project名称
    • 查找构建所需的其他CMake/catkin包

      使用find_package()函数指定需要找到哪些CMake/catkin包来构建我的项目,总是至少依赖一种对catkin的依赖

      find_package(
        catkin REQUIRED COMPONENTS
        roscpp
        rospy
        sensor_msgs
        std_msgs
        tf
        cotek_msgs
        cv_bridge REQUIRED
        geometry_msgs REQUIRED
        image_geometry REQUIRED
        image_transport REQUIRED
        cotek_common
        pluginlib
        cotek_localizer
        tf
        message_generation 
      )
      
      • find_package()有什么作用?

        如果CMake通过find_package()找到一个包,则会导致创建多个CMake环境变量,提供有关找到的包的信息,这些环境变量可以在稍后的CMake脚本中使用,环境变量描述了包导出头文件的位置、源文件的位置、包依赖的库以及这些库的路径。

        名称规定:_

        • _FOUND	#如果找到库,则设置为true,否则为false
          _INCLUDE_DIRS 或 _INCLUDES	#包导出的包含路径
          _LIBRARIES 或 _LIBS	#包导出的库
          _DEFINITIONS
          
      • 为什么catkin包被指定为组件?

        catkin包并不是catin组件

        使用catkin前缀创建了一组环境变量

        • 假设代码中使用了nodelet包,查找包的方式是:

          find_package(catkin REQUIRED COMPONENTS nodelet)
          #这意味着 nodelet 导出的包含路径、库等也附加到 catkin_ 变量。例如,catkin_INCLUDE_DIRS 不仅包含 catkin 的包含路径,还包含 nodelet 的包含路径!这将在以后派上用场。
          
        • 自行find_package nodelet:

          find_package(nodelet)
          #这意味着不会将nodelet路径、库等添加到catkin_变量中
          
      • boost

        如果使用C++和boost,需要在 Boost 上调用 find_package() 并指定您使用 Boost 的哪些方面作为组件。 例如,如果你想使用 Boost 线程

        find_package(Boost REQUIRED COMPONENTS thread)
        
    • 指定包构建信息导出:catkin_package()

      catkin_package()是catkin提供的CMake宏。这是为构建系统指定特定于catkin的信息所必须的,该信息又用于生成pkgconfig()和CMake文件

      必须在使用add_library()add_executable()声明任何目标之前调用此函数,该函数有5个可选参数

      • INCLUDE_DIRS:包的导出,包含路径
      • LIBRARIES:从项目导出的库
      • CATKIN_DEPENDS:本项目依赖的其他catkin项目
      • DEPENDS:该项目依赖的非catkin CMake 项目
      • CFG_EXTRAS:附加配置选项
      catkin_package(
       INCLUDE_DIRS include
       LIBRARIES visual
       CATKIN_DEPENDS 
        roscpp 
        rospy 
        sensor_msgs 
        std_msgs 
        cotek_msgs 
        cv_bridge 
        geometry_msgs 
        image_transport 
        message_runtime 
        pluginlib 
        tf
       DEPENDS 
        OpenCV 
        apriltag
      )
      
      

      这表示包文件夹中的文件夹include是导出的头文件所在的位置,库名为visual,CATKIN_DEPENDS下的是构建/运行这个包需要存在的包,DEPENDS下面是构建/运行这个包需要存在的系统依赖项

    • 构建目标

      构建目标可以采用多种形式,通常有多种

      • 可执行文件,可以直接运行
      • 库目文件,可执行文件在构建和/或运行时可以使用的库

      目标名

      • 需要注意的是,无论构建/安装到哪个文件夹,catkin 中构建目标的名称都必须是唯一的。 这是 CMake 的要求。 但是,目标的唯一名称仅在 CMake 内部是必需的。 可以使用 set_target_properties() 函数将目标重命名为其他目标:

        set_target_properties(rviz_image_view
                              PROPERTIES OUTPUT_NAME image_view
                              PREFIX "")
        

        这将在构建和安装输出中将目标 rviz_image_view 的名称更改为 image_view。

      自定义输出目录

      可执行文件和库的默认输出目录通常设置为合理的值,但在某些情况下必须对其进行自定义。

      set_target_properties(python_module_library
        PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION})
      

      Include路径和Library路径

      在指定目标之前,您需要指定可以在何处找到这些目标的资源,特别是头文件和库:

      include_directories 的参数应该是由 find_package 调用生成的 *_INCLUDE_DIRS 变量以及需要包含的任何其他目录

      规定.h头文件路径

      include_directories(
        include
        include/visual
        include/node
        ${catkin_INCLUDE_DIRS}
        ${OpenCV_INCLUDE_DIRS}
        ${realsense2_INCLUDE_DIRS}
        ${apriltag_INCLUDE_DIRS}
        ${EIGEN3_INCLUDE_DIRS}
      )
      

      CMake link_directories() 函数可用于添加额外的库路径,但不建议这样做。 所有 catkin 和 CMake 包在 find_packaged 时都会自动添加链接信息。 只需链接到 target_link_libraries() 中的库

      规定.so/.a库文件路径

      link_directories(${apriltag_LIBDIR})
      

      生成可执行目标

      要指定一个必须构建的可执行目标,必须使用add executable() CMake函数

      将.cpp/.c/.cc文件生成可执行文件

      add_executable(${PROJECT_NAME}_node src/visual_node.cc)
      

      生成库目标

      add_library() CMake 函数用于指定要构建的库。 默认情况下,catkin 构建共享库

      将.cpp/.c/.cc文件生成.a静态库,库文件名通常为libxxx.so,这里只要写xxx即可

      add_library(${PROJECT_NAME}
        src/d435_driver.cc
        src/convert_image.cc
        src/visual_calibration.cc
        src/pedestrian_detect_driver.cc
        src/device_manager.cc
        src/pallet_avoid.cc
        src/config_helper.cc
        src/d435i_imu.cc
        src/apriltag_common_functions.cc
        src/apriltag_detector.cc
      )
      

      目标链接库

      使用 target_link_libraries() 函数指定可执行目标链接到哪些库。 这通常在 add_executable() 调用之后完成

      对add_library或者add_executable生成的库文件或者可执行文件进行链接操作

      #可执行文件进行链接
      target_link_libraries(${PROJECT_NAME}_node
        ${OpenCV_LIBS}
        ${PROJECT_NAME}
        ${catkin_LIBRARIES}
        ${realsense2_LIBRARY} 
        ${apriltag_LIBRARIES}
        ${YAML_CPP_LIBRARIES}
         -lpthread 
         -lm
      )
      #库文件进行链接
      target_link_libraries(${PROJECT_NAME} ${catkin_LIBRARIES} )
      
  • 消息、服务、动作

    ROS 中的消息 (.msg)、服务 (.srv) 和动作 (.action) 文件在被 ROS 包构建和使用之前需要一个特殊的预处理器构建步骤。 这些宏的目的是生成特定于编程语言的文件,以便人们可以使用他们选择的编程语言中的消息、服务和操作。 构建系统将使用所有可用的生成器(例如 gencpp、genpy、genlisp 等)生成绑定。

    提供了三个宏分别处理消息、服务和动作:

    add_message_files
    add_service_files
    add_action_files
    
  • 启用 Python 模块支持

    ROS 包提供了一些 Python 模块

    catkin_python_setup()
    

    在调用 generate_messages() 和 catkin_package() 之前

  • 单元测试

    有一个特定于 catkin 的宏用于处理基于 gtest 的单元测试,称为 catkin_add_gtest()。

    catkin_python_setup()
    

    在调用 generate_messages() 和 catkin_package() 之前

  • 可选步骤:指定可安装目标

    在构建时间之后,目标被放置到 catkin 工作区的开发空间中。 但是,通常我们希望将目标安装到系统(比如说我要把包安装在/opt/ros/kinetic/目录下),以便其他人可以使用它们或安装到本地文件夹以测试系统级安装。 换句话说,如果您希望能够对您的代码进行“make install”,您需要指定目标应该在哪里结束。

    这是使用 CMake install() 函数完成的,该函数将参数作为参数:

    TARGETS	#要安装哪些目标
    ARCHIVE DESTINATION	#静态库和 DLL (Windows) .lib 存根
    LIBRARY DESTINATION	#非 DLL 共享库和模块
    RUNTIME DESTINATION	#可执行目标和 DLL (Windows) 风格的共享库
    
    #标记可执行文件和/或库以供安装
    install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node
      ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
      LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
      RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
    )
    
    #标记cpp头文件进行安装
    install(DIRECTORY include/${PROJECT_NAME}/
      DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
      FILES_MATCHING PATTERN "*.h"
      PATTERN ".git" EXCLUDE
    )
    
    #安装配置文件
    install(FILES
      config/settings.yaml
      config/tags.yaml
      DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/config
    )
    
    #安装 roslaunch 文件或其他资源
    install(FILES
      launch/visual.launch
      DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
    )
    

你可能感兴趣的:(ros,cmake)