第2章 工作空间与功能包创建

说实话,很多刚接触ROS2的朋友,第一个拦路虎就是工作空间和功能包。你想想看,代码该放哪?怎么编译?为什么别人的项目一拉下来就能跑,我的一堆报错?

我个人习惯,先把工作空间的骨架搭好,再往里面填功能包。就像盖房子,地基和梁柱得先立起来。这一章我们就来聊聊工作空间的结构、colcon这个构建系统,以及功能包里的两个核心文件——package.xml和CMakeLists.txt。

工作空间与功能包 工作空间结构 src / build / install / log colcon 构建系统 colcon build / test / clean 创建功能包 ros2 pkg create package.xml 依赖、版本、维护者 CMakeLists.txt 编译规则、可执行目标

工作空间结构——其实就四个文件夹

一个标准的ROS2工作空间长这样:

my_robot_ws/
├── src/          # 源码目录,放功能包
├── build/        # 编译中间文件(别手动改)
├── install/      # 安装后的文件(setup.bash 在这里)
└── log/          # 编译日志

src 放你的功能包,build 和 install 由 colcon 自动生成。我记得第一次用ROS2时,总想进build目录里翻东西,后来发现完全没必要——你只需要关心 src 和 install 里的 setup 脚本。

💡 我的习惯: 每次新建工作空间,先在 src 里放一个 .gitignore,把 build、install、log 都忽略掉。不然一不小心把编译产物推上去,队友会骂人的。

colcon 构建系统——比 catkin 清爽多了

ROS1 时代我们用 catkin_make,ROS2 换成了 colcon。说白了,colcon 就是 catkin 的升级版,支持并行编译、混合语言包,而且对 CMake、Python 一视同仁。

最常用的几个命令:

  • colcon build —— 编译整个工作空间
  • colcon build --packages-select my_pkg —— 只编译某个包(省时间)
  • colcon test —— 跑测试
  • colcon clean —— 清空 build/install 目录

我个人特别喜欢 --symlink-install 这个选项。加上它之后,Python 脚本不用每次重新编译,改完就能跑。调试阶段简直不要太爽。

⚠️ 我曾经踩过的坑: 有一次我改了 package.xml 里的依赖,但忘了重新编译。结果 ros2 run 一直报找不到依赖。记住:改完 xml 或 CMakeLists 后,一定要 colcon build 一次,不然新依赖不会被安装。

创建功能包——用命令行一步到位

创建功能包不需要手写文件夹,ROS2 提供了 ros2 pkg create 命令。比如:

cd ~/my_robot_ws/src
ros2 pkg create my_first_pkg \
  --build-type ament_cmake \
  --dependencies rclcpp std_msgs

这条命令会生成一个完整的功能包骨架,包括 package.xml、CMakeLists.txt、src/ 文件夹等。你想想看,要是手写这些文件,光格式错误就够你折腾半天。

如果你用 Python 写节点,把 --build-type 换成 ament_python 就行。我个人建议新手先用 C++(ament_cmake),因为编译时能帮你检查出很多类型错误。

package.xml 解析——功能包的身份证

每个功能包都必须有一个 package.xml。它告诉 colcon 这个包叫什么、谁写的、依赖哪些库。来看一个典型的例子:

<?xml version="1.0"?>
<package format="3">
  <name>my_first_pkg</name>
  <version>0.0.1</version>
  <description>我的第一个ROS2功能包</description>
  <maintainer email="you@example.com">Your Name</maintainer>
  <license>Apache-2.0</license>

  <buildtool_depend>ament_cmake</buildtool_depend>
  <depend>rclcpp</depend>
  <depend>std_msgs</depend>
</package>

这里重点看 <depend> 标签。它表示「编译和运行都需要」。如果你只编译时需要,用 <build_depend>;只运行时需要,用 <exec_depend>。不过大部分情况用 <depend> 就够了,省心。

🔑 关键点: format="3" 表示使用最新的包格式,支持 <depend> 这种合并标签。ROS2 官方推荐都用 format 3。

CMakeLists.txt 解析——编译的施工图

对于 C++ 功能包,CMakeLists.txt 告诉编译器怎么找到头文件、链接哪些库、生成哪些可执行文件。下面是一个最小但完整的例子:

cmake_minimum_required(VERSION 3.8)
project(my_first_pkg)

# 使用C++17标准
set(CMAKE_CXX_STANDARD 17)

# 查找依赖包
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

# 添加可执行节点
add_executable(my_publisher src/my_publisher.cpp)
target_link_libraries(my_publisher ${rclcpp_LIBRARIES})

# 安装规则
install(TARGETS my_publisher DESTINATION lib/${PROJECT_NAME})

# 让ament识别这个包
ament_package()

这里我特别想提醒一点:ament_package() 必须放在最后,否则 colcon 会报一些莫名其妙的错。我曾经因为这个顺序问题折腾了半小时,最后发现只是位置不对。

另外,如果你有多个节点,每个节点都要单独 add_executable 和 install。别想着偷懒写一个通配符,ROS2 的构建系统没那么智能。

文件/命令作用个人备注
src/存放功能包源码每个包一个子文件夹
colcon build编译整个工作空间加 --symlink-install 更高效
ros2 pkg create自动生成功能包骨架指定 --build-type 和 --dependencies
package.xml声明元信息和依赖format="3" 最省事
CMakeLists.txt定义编译规则ament_package() 放最后

嗯,这一章的内容差不多就是这些。工作空间和功能包是ROS2开发的基石,虽然看起来琐碎,但只要你亲手创建两三次,后面就完全是肌肉记忆了。