详解ROS中的CMakeLists

详解ROS中的CMakeLists

在ROS(Robot Operating System)中,CMakeLists.txt文件是项目构建配置文件,它定义了如何构建ROS包中的可执行文件、库文件以及如何处理依赖关系等。CMakeLists.txt文件使用CMake语法编写,CMake是一个跨平台的构建系统,用于自动化编译、链接和打包过程。

下面我们详细介绍ROS中CMakeLists.txt的常用配置项:

  1. 最低CMake版本要求:
    1
    cmake_minimum_required(VERSION 2.8.3)
    这行代码定义了项目所需的最低CMake版本。在ROS中,通常需要CMake 2.8.3及以上版本
  2. 项目名称:
    1
    project(my_ros_package)
    这里定义了项目的名称,通常与ROS包名相同。
  3. 查找依赖包:
    1
    2
    3
    4
    5
    6
    find_package(catkin REQUIRED COMPONENTS
    roscpp
    rospy
    std_msgs
    message_generation
    )
    find_package()函数用于查找项目所需的依赖包。REQUIRED关键字表示必须找到这些依赖包,否则构建过程将失败。COMPONENTS关键字后面的参数是需要查找的依赖包列表。
  4. 声明构建依赖和运行时依赖:
    1
    2
    3
    4
    5
    catkin_package(
    INCLUDE_DIRS include
    LIBRARIES my_ros_package
    CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
    )
    catkin_package()函数用于声明项目的构建依赖和运行时依赖。INCLUDE_DIRS指定头文件的目录,LIBRARIES指定项目生成的库文件,CATKIN_DEPENDS声明了项目在构建和运行时所依赖的其他ROS包。
  5. 添加头文件目录:
    1
    2
    3
    4
    include_directories(
    include
    ${catkin_INCLUDE_DIRS}
    )
    include_directories()函数用于指定项目的头文件目录。这里包括了include目录和catkin_INCLUDE_DIRS(包含所有依赖包的头文件目录)。
  6. 添加可执行文件:
    1
    add_executable(my_node src/my_node.cpp)
    add_executable()函数用于添加可执行文件。第一个参数是可执行文件的名称,第二个参数是源文件路径。
  7. 链接库文件:
    1
    2
    3
    target_link_libraries(my_node
    ${catkin_LIBRARIES}
    )
    target_link_libraries()函数用于为指定的可执行文件链接库文件。在本例中,为my_node可执行文件链接catkin_LIBRARIES,即所有依赖包的库文件。
  8. 添加依赖:
    1
    add_dependencies(my_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
    add_dependencies()函数用于为指定的可执行文件添加依赖。在本例中,为my_node添加了项目导出目标和catkin_EXPORTED_TARGETS的依赖。
  9. 安装
    在CMakeLists.txt中,可以使用install()函数指定在构建过程中生成的可执行文件、库文件、头文件以及其他相关文件的安装路径。这些安装路径与ROS的devel和install目录有关。

例如,可以通过以下方式安装可执行文件、库文件和头文件:

1
2
3
4
5
6
7
8
9
10
11
12
install(TARGETS my_node
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

install(TARGETS my_library
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
)

install(DIRECTORY include/${PROJECT_NAME}/
DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
)

  1. 安装其他资源文件:
    除了可执行文件、库文件和头文件外,还可以安装其他资源文件,例如launch文件、配置文件和地图文件等。以下是一些示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    install(DIRECTORY launch/
    DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
    )

    install(DIRECTORY config/
    DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/config
    )

    install(DIRECTORY maps/
    DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/maps
    )
    这些配置选项为ROS项目中的CMakeLists.txt文件提供了基本框架。根据项目的具体需求,可以对这些选项进行调整和扩展。例如,可以为项目添加自定义消息、服务和动作定义,配置不同的库文件和可执行文件,以及安装其他所需资源。

综上所述,ROS中的CMakeLists.txt文件负责管理构建过程,包括依赖关系、可执行文件、库文件、头文件等。为了使项目正常运行,需要正确地配置CMakeLists.txt文件。

自定义消息、服务和动作定义的处理

接下来,我们将深入探讨ROS中的CMakeLists.txt文件,包括自定义消息、服务和动作定义的处理。

  1. 自定义消息(msg):
    要创建自定义消息,需要在项目的msg目录中定义.msg文件。接下来,在CMakeLists.txt中,将以下内容添加到find_package()函数中:
    1
    message_generation
    然后添加如下指令来指定要生成的自定义消息文件:
    1
    2
    3
    4
    add_message_files(
    FILES
    MyCustomMessage.msg
    )
    接下来,调用generate_messages()函数来生成消息文件:
    1
    2
    3
    4
    generate_messages(
    DEPENDENCIES
    std_msgs
    )
    这里,DEPENDENCIES指定了自定义消息所依赖的其他消息包。
  2. 自定义服务(srv):
    与自定义消息类似,自定义服务需要在项目的srv目录中定义.srv文件。然后,在CMakeLists.txt中,将以下内容添加到find_package()函数中:
    1
    2
    message_generation

    接着添加如下指令来指定要生成的自定义服务文件:
    1
    2
    3
    4
    5
    add_service_files(
    FILES
    MyCustomService.srv
    )

    接下来,调用generate_messages()函数来生成服务文件:
    1
    2
    3
    4
    5
    generate_messages(
    DEPENDENCIES
    std_msgs
    )

  3. 自定义动作(action):
    要创建自定义动作,需要在项目的action目录中定义.action文件。在CMakeLists.txt中,将以下内容添加到find_package()函数中:

    1
    2
    actionlib_msgs

    然后添加如下指令来指定要生成的自定义动作文件:

    1
    2
    3
    4
    5
    add_action_files(
    FILES
    MyCustomAction.action
    )

    接着,调用generate_messages()函数来生成动作文件:

    1
    2
    3
    4
    5
    generate_messages(
    DEPENDENCIES
    std_msgs actionlib_msgs
    )

    在处理自定义消息、服务和动作时,还需要在catkin_package()函数中添加CATKIN_DEPENDS message_runtime以声明运行时依赖:

    1
    2
    3
    4
    5
    catkin_package(
    ...
    CATKIN_DEPENDS message_runtime
    )

    总之,ROS的CMakeLists.txt文件涵盖了项目构建过程中的各个方面,包括依赖关系、可执行文件、库文件、头文件以及自定义消息、服务和动作。正确配置CMakeLists.txt文件,可确保ROS项目的顺利构建和运行。

    高级配置选项和技巧

    现在让我们继续深入探讨ROS中CMakeLists.txt的一些高级配置选项和技巧。

  4. 设置编译标志:

在CMakeLists.txt中,可以为项目设置编译标志。例如,可以使用以下指令为C++源文件启用C++11标准:

1
2
3
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)


如果要设置特定的编译器选项,可以使用set()函数:
1
2
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")


上述代码将为项目启用了所有警告(-Wall)以及额外警告(-Wextra)。

  1. 添加自定义CMake模块:

在某些情况下,可能需要使用自定义CMake模块来扩展项目的构建过程。为此,可以在项目的cmake目录中创建自定义CMake模块,并在CMakeLists.txt中使用list(APPEND …)和find_package()函数将其添加到CMAKE_MODULE_PATH:

1
2
3
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
find_package(MyCustomCMakeModule REQUIRED)

  1. 条件编译:

在某些情况下,可能需要根据特定条件启用或禁用项目中的某些功能。可以使用option()函数和if()语句来实现这一目标:

1
2
3
4
5
6
7
option(ENABLE_MY_FEATURE "Enable my custom feature" OFF)

if(ENABLE_MY_FEATURE)
# Add custom code or configuration for the feature
add_definitions(-DENABLE_MY_FEATURE)
endif()


上述代码定义了一个名为ENABLE_MY_FEATURE的选项,默认值为OFF。如果选项被启用,将会添加一个预处理器定义ENABLE_MY_FEATURE。

  1. 为项目添加测试:

在ROS项目中,可以使用catkin_add_gtest()函数和gtest库为项目添加单元测试。首先,确保在find_package()函数中添加了rostest组件:

1
2
3
4
5
find_package(catkin REQUIRED COMPONENTS
...
rostest
)


然后添加测试可执行文件和链接库:
1
2
3
catkin_add_gtest(my_test test/my_test.cpp)
target_link_libraries(my_test ${catkin_LIBRARIES} my_library)


以上示例中,my_test是测试的名称,test/my_test.cpp是测试源文件,而my_library是测试所需链接的库。

这些高级配置选项和技巧可以帮助你在ROS项目中实现更复杂的构建过程。理解这些选项的用途和功能,将有助于创建更可扩展和可维护的ROS项目。

实现一些实用功能和配置

接下来,我们将讨论如何在ROS中的CMakeLists.txt文件中实现一些实用功能和配置:

  1. 添加Python可执行文件:

在ROS项目中,你可能需要添加Python脚本作为可执行文件。首先,确保在find_package()函数中添加了rospy组件:

1
2
3
4
5
find_package(catkin REQUIRED COMPONENTS
...
rospy
)


然后,可以使用catkin_install_python()函数将Python脚本安装为可执行文件:
1
2
3
4
catkin_install_python(PROGRAMS scripts/my_python_script.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

  1. 添加动态链接库(动态库):

有时候,可能需要在ROS项目中创建动态链接库。为此,可以使用add_library()函数和SHARED关键字:

1
2
add_library(my_shared_library SHARED src/my_shared_library.cpp)


接着,为生成的库文件添加安装目标:
1
2
3
4
install(TARGETS my_shared_library
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
)

  1. 链接系统库:

在某些情况下,可能需要链接系统库。可以使用find_library()函数查找系统库,并使用target_link_libraries()函数将其链接到目标:

1
2
3
4
5
6
7
8
9
10
find_library(MY_SYSTEM_LIBRARY
NAMES my_system_library
PATHS /path/to/libraries
)

target_link_libraries(my_executable
${catkin_LIBRARIES}
${MY_SYSTEM_LIBRARY}
)

  1. 链接Boost库:
    在ROS项目中,可能需要使用Boost库。为此,首先确保在find_package()函数中添加了Boost组件:
    1
    2
    3
    4
    5
    find_package(Boost REQUIRED COMPONENTS
    system
    thread
    )

    然后,将Boost库添加到target_link_libraries()函数中:
    1
    2
    3
    4
    5
    target_link_libraries(my_executable
    ${catkin_LIBRARIES}
    ${Boost_LIBRARIES}
    )

  2. 使用环境变量:

在CMakeLists.txt中,可以通过$ENV{VAR_NAME}语法访问环境变量。例如,以下代码从环境变量MY_VAR获取值并将其存储在CMake变量my_var中:

1
2
set(my_var $ENV{MY_VAR})


了解这些实用功能和配置将有助于处理ROS项目中的各种需求。这些示例可以根据项目需求进行调整和扩展,以便更好地适应特定的构建环境和依赖关系。现在让我们继续探讨ROS中CMakeLists.txt文件的一些其他实用功能和技巧。

使用外部项目

在ROS项目中,可能需要使用外部项目的代码和资源。CMake具有一个名为ExternalProject的模块,可用于下载、构建和安装外部项目。首先,需要在CMakeLists.txt中包含ExternalProject模块:

1
2
include(ExternalProject)


接下来,使用ExternalProject_Add()函数定义外部项目:
1
2
3
4
5
6
ExternalProject_Add(my_external_project
GIT_REPOSITORY "https://github.com/user/my_external_project.git"
GIT_TAG "master"
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/external
)


这里,my_external_project是外部项目的名称,GIT_REPOSITORY和GIT_TAG指定了项目的源代码仓库和分支。CMAKE_ARGS用于传递给外部项目的CMake构建的参数,其中CMAKE_INSTALL_PREFIX指定了安装路径。

使用pkg-config:

有时,可能需要使用pkg-config工具查询库的编译和链接标志。CMake提供了一个名为PkgConfig的模块,可用于与pkg-config交互。首先,需要在CMakeLists.txt中包含PkgConfig模块:

1
2
find_package(PkgConfig REQUIRED)


然后,可以使用pkg_check_modules()函数查询库信息:
1
2
pkg_check_modules(MY_LIBRARY_PKGCONFIG REQUIRED my_library)


接下来,可以使用查询到的信息为目标设置编译和链接标志:
1
2
3
target_include_directories(my_executable PRIVATE ${MY_LIBRARY_PKGCONFIG_INCLUDE_DIRS})
target_link_libraries(my_executable ${catkin_LIBRARIES} ${MY_LIBRARY_PKGCONFIG_LIBRARIES})

在ROS中使用Eigen库:

在ROS项目中,可能需要使用Eigen库进行线性代数计算。首先,确保在find_package()函数中添加了Eigen3组件:

1
2
find_package(Eigen3 REQUIRED)


然后,将Eigen库添加到target_include_directories()函数中:
1
2
3
4
5
target_include_directories(my_executable
PRIVATE
${EIGEN3_INCLUDE_DIRS}
)


这些实用功能和技巧可以帮助您在ROS项目中处理各种需求和依赖项。了解如何使用这些功能可以使您的项目更具灵活性和可扩展性,同时确保与其他库和软件包的兼容性。请注意,这些示例可能需要根据项目的具体需求进行调整。

使用OpenCV库

在ROS项目中,可能需要使用OpenCV库进行计算机视觉处理。首先,在find_package()函数中添加OpenCV组件:

1
2
find_package(OpenCV REQUIRED)


接下来,在target_link_libraries()函数中链接OpenCV库:
1
2
target_link_libraries(my_executable ${catkin_LIBRARIES} ${OpenCV_LIBRARIES})

设置安装规则

在ROS项目中,通过使用install()函数,可以将构建的可执行文件、库文件、头文件和其他资源文件安装到目标目录,以方便分发和部署。下面是一些常见的安装规则示例:

安装可执行文件:

1
2
3
4
install(TARGETS my_executable
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

安装库文件:

1
2
3
4
5
install(TARGETS my_library
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
)

安装头文件:

1
2
3
4
install(DIRECTORY include/${PROJECT_NAME}/
DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
)


安装资源文件(如配置文件、launch文件等):
1
2
3
4
install(DIRECTORY config launch
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
)

在CMakeLists.txt中添加自定义命令

有时,可能需要在构建过程中执行一些自定义操作,如生成代码或处理资源文件。可以使用add_custom_command()函数在CMakeLists.txt中定义自定义命令:

1
2
3
4
5
6
add_custom_command(
OUTPUT generated_file.cpp
COMMAND my_generator_tool --input=input_file --output=generated_file.cpp
DEPENDS input_file
)


上述代码指定了一个自定义命令,它在generated_file.cpp文件不存在或input_file文件发生更改时,运行my_generator_tool工具。

使用message、service和action文件生成代码

ROS中的消息、服务和动作定义文件需要生成相应的C++、Python等语言的代码。在CMakeLists.txt中,可以使用以下函数生成代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Generate message code
add_message_files(
FILES
MyMessage.msg
)

# Generate service code
add_service_files(
FILES
MyService.srv
)

# Generate action code
add_action_files(
FILES
MyAction.action
)


生成代码之前,需要确保在find_package()函数中添加了message_generation、actionlib_msgs等组件:
1
2
3
4
5
6
find_package(catkin REQUIRED COMPONENTS
...
message_generation
actionlib_msgs
)


然后,使用generate_messages()函数生成代码:
1
2
3
4
5
6
generate_messages(
DEPENDENCIES
std_msgs
actionlib_msgs
)

使用message、service和action文件的依赖关系:

有时候,一个ROS软件包可能依赖于另一个软件包中定义的消息、服务或动作。在这种情况下,需要确保在find_package()函数中添加了相应的组件:

1
2
3
4
5
find_package(catkin REQUIRED COMPONENTS
...
other_package_msgs
)


此外,还需要将依赖关系添加到catkin_package()函数中:
1
2
3
4
5
catkin_package(
...
CATKIN_DEPENDS other_package_msgs
)

使用CMake的message()函数

在CMakeLists.txt文件中,可以使用message()函数打印调试信息。这对于调试构建过程中的问题很有帮助。message()函数接受不同的严重性级别,例如:STATUS、WARNING和FATAL_ERROR。

1
2
3
4
message(STATUS "This is a status message")
message(WARNING "This is a warning message")
message(FATAL_ERROR "This is a fatal error message")


通过掌握这些实用功能和技巧,可以帮助您在ROS项目中应对各种需求和依赖。了解如何使用这些功能可以使您的项目更具灵活性和可扩展性,同时确保与其他库和软件包的兼容性。请注意,这些示例可能需要根据项目的具体需求进行调整。


详解ROS中的CMakeLists
https://qiangsun89.github.io/2023/04/19/详解ROS中的CMakeLists/
作者
Qiang Sun
发布于
2023年4月19日
许可协议