ROS2学习(三)colcon编译某个包并提供给其他包依赖使用

    简单的ROS2的例子学习可能不会遇到这个问题。但是我们仍然难免会思考为什么ROS2官方提供的包比如rclcpp,这些包我们使用的时候可以直接添加依赖,使用find_package(rclcpp REQUIRED),就可以找到。我们自己编译的包是否也可以提供给其他包使用。
    如果稍微大一些的工程,会有很多人开发,那么就有可能自己开发的包是一个共通的包,希望提供您给其他人使用,如何才能做的那?
    本文通过一个例子来说明如何解决这种依赖问题。例子是在一个ROS2的工程中做了两个包一个common包,一个sample包,sample包依赖于common的库。

要实现这样的效果,就要考虑几个问题。

  1. 如何将common的包编译通过
  2. ROS2并行编译,如何保证common包在sample包之前编译出来
  3. 在sample包使用的时候如何知道应该包含common包的头文件路径和动态库路径

     上面的第一个问题,也就是最基本的,我们要提供给别人使用,肯定是要自己编译通过才可以。

     第二个问题,如果在稍微大一些得工程,有很多个包的时候,使用colcon build 就会看到,几个包会同时编译,这样是为了编译的更快。但是这就没有办法保证自己的包会在其他人使用之前编译出来。当然即使不是并行编译,使用默认的方法,也没有办法保证所有的包能够按顺序的编译。
     为解决这个问题,就需要在包的package.xml中添加依赖关系。比如sample包依赖common包,那么就在sample包的package.xml中添加

<depend>common</depend>

    告诉编译器,编译B包的时候依赖A包,A包需要先编译出来。其实从package.xml中也可以看到依赖rclcpp

  <depend>rclcpp</depend>

只不过rclcpp早就编译好,安装到系统里了。

    对于第三个问题,因为sample包里的实现是需要包含common包中的头文件和链接动态库,如何让sample包知道common包的头文件路径和动态库路径?对于CMake有所了解的人,肯定会知道,要么通过下面这种方式直接设置变量解决。

set(COMMON_INCLUDE_DIR xxx/xxx/include)
set(COMMON_LIBRARY_PATH xxx/xxx/lib)

    如果对Cmake比较了解,并且对工程的相对目录比较了解的话,是可以写出来的。不过感觉这种方式比较粗暴。而且往往会写成绝对路径,当目录一变化就找不到了。

    Cmake提供了find_package()函数可以找到对应的包,并把相关的头文件和库的环境变量加进去。
    find_package(xxxx)如何实现的那?原理是在CMake的查找目录下找到xxx包的的cmake文件夹,一般都会存在,xxxConfig.cmake,或者Findxxx.cmake文件,只要找到其中一个文件就能够定位xxx所有的环境变量,然后加载到cmake中。详细的细节不在这里说了,知道大概原理即可,因为我们也很少会自己去写一个这样的Findxxx.cmake文件,大部分我们安装的库,执行install的时候都会生成这个文件。
使用find_package()的方式感觉上比直接设置环境变量更优雅一些,而且我们依赖ROS2库的时候,也是这样做的,比如我们写ROS2的程序的时候会经常看到下面这样使用

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)

    现在我们要思考的就是如何把自己的包打包成一个像rclcpp一样的包,能够让其他人使用的时候直接使用find_package()就可以找到。
    其实道理也很简单,就是怎么导出头文件路径和动态库路径。这样其他包就能够使用了。
这里用到了ROS2的ament两个函数。

ament_export_include_directories(include)
ament_export_libraries(your_library)

    这两个函数一个是导出头文件的,一个是导出动态库的。这样colcon编译这个包的时候,就会生成xxxConfig.cmake文件。其他包就可以通过find_package()找到了。

实现原理讲清楚了,下面就是例子。
代码结构如下,

crabe@crabe-virtual-machine:~/Desktop/sample$ tree sample2/
sample2/
└── src
    ├── common
    │   ├── CMakeLists.txt
    │   ├── include
    │   │   └── common
    │   │       └── common.h
    │   ├── package.xml
    │   └── src
    │       └── common.cpp
    └── sample
        ├── CMakeLists.txt
        ├── include
        │   └── sample
        │       └── sample.h
        ├── package.xml
        └── src
            └── sample.cpp

就是建了两个包,一个common,一个sample。
common包里的cpp只做了一个简单的实现如下,头文件是这个类的定义:

#include "common/common.h"
#include <iostream>
using namespace std;

CommonLib::CommonLib()
{
    
    
        cout << "construct.."<<endl;
}

CommonLib::~CommonLib()
{
    
    
}

void CommonLib::Publish()
{
    
    
        cout << "this is common package func.."<< endl;
}

common包的CmakeList.txt如下:

cmake_minimum_required(VERSION 3.5)
project(common)

# Default to C99
if(NOT CMAKE_C_STANDARD)
  set(CMAKE_C_STANDARD 99)
endif()

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)

#add_executable(common src/common.cpp)
add_library(common SHARED ${CMAKE_CURRENT_SOURCE_DIR}/src/common.cpp)

target_include_directories(common PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>)
ament_target_dependencies(
  common
  "rclcpp"
)

ament_export_include_directories(include)
install(DIRECTORY include/
  DESTINATION include
)

install(
        TARGETS common
        LIBRARY DESTINATION lib
        )

ament_export_libraries(${PROJECT_NAME})

#ament_export_targets(common)

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # uncomment the line when a copyright and license is not present in all source files
  #set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # uncomment the line when this package is not in a git repo
  #set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()

这个CMakeList.txt主要做了两个install,将头文件和动态库安装到了install目录。

install(DIRECTORY include/
  DESTINATION include
)
install(
        TARGETS common
        LIBRARY DESTINATION lib
        )

然后通过ament_export将目录导出到commonConfig.cmake中,这样,sample包就可以使用了。

ament_export_include_directories(include)
ament_export_libraries(${PROJECT_NAME})

sample包的写法

#include "sample/sample.h"
#include <iostream>

using namespace std;

Sample::Sample()
{
    
    
        lib_ = make_shared<CommonLib>();
}

Sample::~Sample()
{
    
    
}

void Sample::Publish()
{
    
    
        lib_->Publish();
}

int main(int argc, char ** argv)
{
    
    

  Sample sample;
  sample.Publish();

  return 0;
}

sample包,主要包含了头文件

#include "sample/sample.h"

还有使用了common中定义的类CommonLib,这些都是很常见的写法。
然后看sample的CMakeList.txt如何写

cmake_minimum_required(VERSION 3.5)
project(sample)

# Default to C99
if(NOT CMAKE_C_STANDARD)
  set(CMAKE_C_STANDARD 99)
endif()

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic -std=c++11)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(common REQUIRED)

if(common_FOUND)
        message("find the lib common")
else()
        message("not find lib common")
endif()

add_executable(sample src/sample.cpp)
target_include_directories(sample PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>
  ${common_INCLUDE_DIRS})
ament_target_dependencies(
  sample
  "rclcpp"
  common
)

install(TARGETS sample
  DESTINATION lib/${PROJECT_NAME})

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # uncomment the line when a copyright and license is not present in all source files
  #set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # uncomment the line when this package is not in a git repo
  #set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()

这里面就直接使用了

find_package(common REQUIRED)

来找common包,通过变量common_FOUND就可以知道是否找到包,然后下面使用的时候就可以添加 ${common_INCLUDE_DIRS}来扎到头文件路径,在链接依赖ament_target_dependencies中增加common,就可以正常链接了。
最后再sample的package.xml中如下增加依赖,这样就能保证编译的先后顺序了。

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>sample</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="[email protected]">yangcb</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <depend>rclcpp</depend>
  <depend>common</depend>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

猜你喜欢

转载自blog.csdn.net/yangcunbiao/article/details/131969985