这篇博客文章详细介绍了如何在ROS2中使用功能包来组织Python和C++节点。功能包是ROS2中的基本模块,用于封装相关代码、配置文件和工具,以便完成特定功能。文章首先解释了功能包的概念,并举例说明了如何通过功能包管理多个节点,节点之间通过话题或服务进行通信,并使用启动文件批量启动节点。接着,文章详细演示了如何创建和配置Python功能包,包括编写节点控制函数、添加依赖声明、编译功能包以及运行可执行文件。随后,文章还介绍了如何创建和配置C++功能包,包括编写C++节点、配置CMakeLists.txt文件、编译和运行节点。最后,文章提醒读者在代码路径中避免使用特殊字符,以免在编译时出现问题。通过这些步骤,读者可以更好地理解如何在ROS2中使用功能包来组织和管理节点。
什么是功能包?
简单来说,功能包就是一个“工具箱”,里面装了一堆相关的代码、配置文件、工具等,用来完成某个特定的功能。比如:
- 一个包可能专门处理机器人移动(比如控制轮子)。
- 另一个包可能专门处理摄像头图像识别。
每个包都是独立的模块,方便重复使用和管理。
节点是 ROS2 中的最小“工人”,每个节点只做一件具体的事(比如读取传感器数据、控制电机)。组织方式如下:
一个功能包可以包含多个节点
例如:一个“移动控制包”里可能有:
节点之间通过“话题(Topic)”或“服务(Service)”通信
比如节点1通过“指令话题”把指令发给节点2,节点2再控制轮子。
用启动文件(Launch File)批量启动节点
可以写一个“启动脚本”,一次性启动多个节点,还能设置参数。
一个功能包通常长这样:
my_package/
├── src/ # 源代码(比如Python或C++)
├── launch/ # 启动文件(.launch.py)
├── package.xml # 包的信息和依赖项
└── CMakeLists.txt # 编译配置(如果是C++代码)
假设你要让机器人巡逻:
- 创建一个
patrol_package
功能包。 在包里写两个节点:
- 节点A:探测障碍物(发布“障碍物位置”话题)。
- 节点B:根据障碍物位置规划路径(订阅话题并计算路径)。
- 写一个启动文件,同时启动这两个节点。
创建Python功能包示例
功能包语法
ros2 pkg create --build-type ament_python --license Apache-2.0 <package_name>
pkg create
是创建包的意思--build-type
用来指定该包的编译类型,一共有三个可选项ament_python、ament_cmake、cmake--license
用于制定证书,一般设置为Apache-2.0,如果不指定的话会弹出一个警告但是没有别的影响--dependencies
指的是这个功能包的依赖,这里使用的依赖是rclcpp
现在我用《重学ROS2:(4)编写ROS2中的第一个节点【C++/Python双语言版本】》中的python内容来创建一个功能包。例如创建一个python代码的功能包:
ros2 pkg create --build-type ament_python --license Apache-2.0 demo_python_pkg

配置python功能包
在setup.py
文件中,写入节点的控制函数,告诉功能包运行哪个函数。
'console_scripts': [
'python_node = demo_python_pkg.ros_python_node:main'
],

添加依赖声明
在py代码中,我们引入了rclpy的库,那么在xml文件中还需要添加依赖,以防止找不到库。
<depend>rclpy</depend>

编译功能包
在终端输入编译命令:
colcon build

运行可执行文件
生成的可执行文件在./install/demo_python_pkg/lib/demo_python_pkg
目录下:

直接运行这个可执行文件会报错,会报错raise PackageNotFoundError(name) importlib.metadata.PackageNotFoundError: No package metadata was found for demo-python-pkg
需要先通过setup.bash
来激活环境变量:
source install/setup.bash
然后再运行那个可执行文件python_node
。

也可以用ros2 run
来运行:
ros2 run demo_python_pkg python_node

创建C++功能包示例
功能包语法
现在我用《重学ROS2:(4)编写ROS2中的第一个节点【C++/Python双语言版本】》中的C++内容来创建一个功能包。
回顾一下基本语法:
ros2 pkg create <package_name> --build-type ament_cmake --license Apache-2.0 --dependencies rclcpp
例如创建个demo功能包:
ros2 pkg create demo_cpp_pkg --build-type ament_cmake --license Apache-2.0 --dependencies rclcpp

创建了功能包之后,在src中书写cpp文件:

配置CMakeLists.txt文件
在生成的CMakeLists.txt文件中,需要添加点内容:
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
# 根据实际文件名以及节点名进行修改
add_executable(cpp_node src/cpp_node.cpp)
# 查找依赖的ROS2包
message(STATUS "Found rclcpp: ${rclcpp_INCLUDE_DIRS}")
message(STATUS "Found rclcpp: ${rclcpp_LIBRARIES}")
message(STATUS "Found rclcpp: ${rclcpp_VERSION}")
target_include_directories(cpp_node PUBLIC ${rclcpp_INCLUDE_DIRS}) #头文件包含
target_link_libraries(cpp_node PUBLIC ${rclcpp_LIBRARIES}) #链接库

完整的cmakelist文件:
cmake_minimum_required(VERSION 3.8)
project(demo_cpp_pkg)
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(cpp_node src/cpp_node.cpp)
# 查找依赖的ROS2包
message(STATUS "Found rclcpp: ${rclcpp_INCLUDE_DIRS}")
message(STATUS "Found rclcpp: ${rclcpp_LIBRARIES}")
message(STATUS "Found rclcpp: ${rclcpp_VERSION}")
target_include_directories(cpp_node PUBLIC ${rclcpp_INCLUDE_DIRS}) #头文件包含
target_link_libraries(cpp_node PUBLIC ${rclcpp_LIBRARIES}) #链接库
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
事实上可以把头文件、链接库用一句命令来代替。也就是:
target_include_directories(cpp_node PUBLIC ${rclcpp_INCLUDE_DIRS}) #头文件包含
target_link_libraries(cpp_node PUBLIC ${rclcpp_LIBRARIES}) #链接库
换成
ament_target_dependencies(cpp_node rclcpp)
为了让生成的过程文件更加有层次,可以新建一个build文件夹,并且进入build文件夹进行编译/终端进入build文件夹:

之后进行make,获得可执行文件。
运行可执行文件即可启动节点:

此时已经成功启动了节点。
如果想要和python一样有install目录,需要手动在CMakeLists.txt里面添加install命令:
#安装 起到拷贝作用
install(TARGETS cpp_node
DESTINATION lib/${PROJECT_NAME}
)
然后在功能包下面进行编译构建:

一个坑
代码的路径不要使用特殊的字符,例如一开始我使用的是2.3,还有中文字符,在cmake的时候,就是报错。
当把路径改的简单一点就成功了!!
