第2章 工作空间与功能包创建
说实话,很多刚接触ROS2的朋友,第一个拦路虎就是工作空间和功能包。你想想看,代码该放哪?怎么编译?为什么别人的项目一拉下来就能跑,我的一堆报错?
我个人习惯,先把工作空间的骨架搭好,再往里面填功能包。就像盖房子,地基和梁柱得先立起来。这一章我们就来聊聊工作空间的结构、colcon这个构建系统,以及功能包里的两个核心文件——package.xml和CMakeLists.txt。
工作空间结构——其实就四个文件夹
一个标准的ROS2工作空间长这样:
my_robot_ws/
├── src/ # 源码目录,放功能包
├── build/ # 编译中间文件(别手动改)
├── install/ # 安装后的文件(setup.bash 在这里)
└── log/ # 编译日志
src 放你的功能包,build 和 install 由 colcon 自动生成。我记得第一次用ROS2时,总想进build目录里翻东西,后来发现完全没必要——你只需要关心 src 和 install 里的 setup 脚本。
.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开发的基石,虽然看起来琐碎,但只要你亲手创建两三次,后面就完全是肌肉记忆了。