3、工作空间与功能包:工作空间概念、创建与编译,功能包结构、创建命令与依赖管理
好,咱们进入第三章。这一章讲的是ROS2开发的基础设施——工作空间和功能包。说白了,这就是你写代码的“工地”和“工程单元”。我见过不少新手,代码写了一大堆,结果编译报错找半天,最后发现是工作空间没建对。嗯,咱们先把地基打牢。
3.1 工作空间:你的代码“工地”
工作空间(Workspace)在ROS2里,就是一个存放所有功能包的顶层目录。你所有的节点、消息定义、配置文件,都放在这个“工地”里。ROS2通过工作空间来管理编译和依赖。
我个人习惯,在~/ros2_ws下干活。当然,名字随你取,但路径里最好别有中文和空格,否则编译时你会哭的。
3.1.1 工作空间的结构
一个标准的ROS2工作空间长这样:
ros2_ws/
├── src/ # 源码目录,放你的功能包
├── build/ # 编译中间文件(自动生成)
├── install/ # 安装后的文件(自动生成)
└── log/ # 编译日志(自动生成)
你只需要手动创建src目录,其他三个是编译工具自动生成的。别手贱去改build和install里的东西,我刚开始时干过这事,结果编译全乱了。
3.1.2 创建工作空间
步骤很简单,就三条命令:
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
colcon build
等等,第一次建空工作空间时,colcon build会报个警告,说没找到功能包。别慌,这是正常的。等你在src里放了功能包再编译就行。
~/.bashrc 里加一行 source ~/ros2_ws/install/setup.bash,这样每次打开终端,工作空间的环境变量就自动加载了。省得每次都要手动 source。
3.2 功能包:ROS2的“工程单元”
功能包(Package)是ROS2里最小的可编译单元。一个功能包可以包含节点、消息定义、服务定义、配置文件等。你可以把它理解成一个“模块”或“组件”。
为什么要有功能包?你想想看,如果所有代码都堆在一个目录里,那得多乱。功能包帮你把不同功能的代码隔离开,方便复用和分发。
3.2.1 功能包的结构
一个典型的C++功能包结构如下:
my_package/
├── CMakeLists.txt # 编译规则(C++包用)
├── package.xml # 包描述文件(所有包都有)
├── src/ # 源码目录
│ └── my_node.cpp
├── include/ # 头文件目录
│ └── my_package/
│ └── my_node.hpp
├── msg/ # 自定义消息(可选)
├── srv/ # 自定义服务(可选)
├── launch/ # 启动文件(可选)
└── config/ # 配置文件(可选)
Python包的结构稍有不同,没有CMakeLists.txt,而是setup.py和setup.cfg。但package.xml是通用的,每个包都必须有。
package.xml 是功能包的“身份证”,里面记录了包名、版本、作者、许可证,以及最重要的——依赖关系。编译工具靠它来解析依赖。
3.2.2 创建功能包
ROS2提供了专门的命令来创建功能包,不用你手动去搭目录结构。我个人觉得这个设计很贴心,省了不少事。
创建C++功能包:
cd ~/ros2_ws/src
ros2 pkg create my_cpp_pkg --build-type ament_cmake --dependencies rclcpp std_msgs
创建Python功能包:
cd ~/ros2_ws/src
ros2 pkg create my_py_pkg --build-type ament_python --dependencies rclpy std_msgs
参数说明:
| 参数 | 说明 |
|---|---|
--build-type |
编译类型:ament_cmake(C++)或 ament_python(Python) |
--dependencies |
依赖的包名,多个用空格隔开 |
--node-name |
(可选)自动生成一个节点模板 |
3.3 依赖管理:别让你的包“断粮”
一个功能包很少是孤立的,它通常会依赖其他包。比如你的节点要用到rclcpp(ROS2客户端库)和std_msgs(标准消息类型)。依赖管理就是告诉编译工具:我的包需要哪些“粮食”。
3.3.1 声明依赖
依赖声明在两个地方:
package.xml:用<depend>标签声明运行时和编译时依赖。CMakeLists.txt(C++包)或setup.py(Python包):用find_package()或install_requires声明编译依赖。
举个例子,package.xml里的依赖声明:
<depend>rclcpp</depend>
<depend>std_msgs</depend>
<depend>geometry_msgs</depend>
对应的CMakeLists.txt里:
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(geometry_msgs REQUIRED)
这两个地方必须保持一致,否则编译会报错。我见过有人只改了package.xml,忘了改CMakeLists.txt,结果折腾了半天。
3.3.2 自动安装依赖
当你从别处拉了一个功能包,它可能依赖一些你没装的系统库。这时候可以用rosdep来自动安装:
cd ~/ros2_ws
rosdep install -i --from-path src --rosdistro humble -y
这个命令会扫描src下所有功能包的package.xml,自动安装缺失的依赖。嗯,省心不少。
rosdep报错说找不到某些包。后来发现是系统源没配好。建议先执行 sudo apt update,再跑rosdep。
3.4 编译工作空间
把功能包放进src后,就可以编译了。ROS2的编译工具是colcon,它比ROS1的catkin快不少。
3.4.1 常用编译命令
| 命令 | 作用 |
|---|---|
colcon build |
编译整个工作空间 |
colcon build --packages-select my_pkg |
只编译指定包(省时间) |
colcon build --symlink-install |
使用符号链接安装(Python包修改后不用重新编译) |
colcon build --cmake-args -DCMAKE_BUILD_TYPE=Debug |
Debug模式编译(方便调试) |
我个人最常用的是--symlink-install,尤其是开发Python节点时。改了代码直接运行,不用重新编译,爽得很。
3.4.2 编译后的环境配置
编译完成后,需要source一下才能使用新编译的包:
source ~/ros2_ws/install/setup.bash
如果你用的是zsh,就source setup.zsh。我建议把这个命令加到~/.bashrc里,一劳永逸。
--merge-install 选项合并安装。
3.5 知识体系总览
下面这张图把本章的核心逻辑串起来了,你可以对照着理解:
从这张图可以看得很清楚:工作空间是顶层容器,里面放功能包;功能包有自己的结构(package.xml、源码、消息定义等);依赖管理则把功能包和外部库连接起来。三者缺一不可。
好了,这一章的内容就到这。工作空间和功能包是ROS2开发的基石,你后面写的每个节点、每个通信逻辑,都跑不出这个框架。多建几个包练练手,很快就熟了。