3、工作空间与功能包:从零搭建你的ROS2项目骨架
好,咱们正式开始动手了。
前两章我们把ROS2的哲学和工具链聊了个遍,但说实话,光说不练假把式。这一章,咱们要亲手创建ROS2项目最基础的两个单元——工作空间(Workspace)和功能包(Package)。
我见过不少新手,一上来就急着写代码,结果编译报错找半天,发现是工作空间没建对。嗯,磨刀不误砍柴工,咱们先把地基打牢。
3.1 工作空间:你的代码大本营
工作空间,说白了就是一个顶层目录。ROS2的所有代码、依赖、编译产物都放在这里面。我个人习惯把它叫做 ros2_ws,当然你也可以叫 my_robot_ws,随你高兴。
一个标准的工作空间长这样:
ros2_ws/
├── src/ # 源代码目录,放你的功能包
├── build/ # 编译中间文件(自动生成)
├── install/ # 安装后的文件(自动生成)
└── log/ # 编译日志(自动生成)
你只需要手动创建 src 目录,其他三个是编译时自动生成的。
src 目录下再建一个 third_party 文件夹,专门放第三方的功能包。这样自己的代码和别人的代码分得清清楚楚,找问题的时候不头疼。
3.1.1 创建工作空间的命令
操作很简单,就两行命令:
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
colcon build
等等,先别急着敲 colcon build。第一次建空工作空间时,我建议你先在 src 里放一个功能包再编译。否则编译完你会发现 install 目录里空空如也,容易让人心里发虚。
source ~/ros2_ws/install/setup.bash
或者把它加到 ~/.bashrc 里,省得每次手动敲。
3.2 功能包:ROS2的最小组织单元
功能包(Package)是ROS2里代码组织的基本单位。一个功能包可以包含节点、消息定义、配置文件、启动文件等等。
ROS2支持两种功能包:
- CMake 包:用CMakeLists.txt管理,适合C++项目
- Python 包:用setup.py管理,适合Python项目
我个人更推荐C++项目用CMake包,Python项目用Python包。混着用也不是不行,但维护起来有点别扭。
3.2.1 创建功能包的命令
创建功能包用 ros2 pkg create 命令。举个例子:
cd ~/ros2_ws/src
ros2 pkg create my_robot_pkg \
--build-type ament_cmake \
--dependencies rclcpp std_msgs
这条命令会创建一个名为 my_robot_pkg 的CMake功能包,并自动添加 rclcpp 和 std_msgs 作为依赖。
如果你用Python:
ros2 pkg create my_python_pkg \
--build-type ament_python \
--dependencies rclpy std_msgs
--dependencies 参数,结果后面手动改CMakeLists.txt改得手忙脚乱。建议创建时就指定好依赖,省得后面补。
3.3 理解package.xml:功能包的身份证
每个功能包根目录下都有一个 package.xml 文件。它记录了功能包的元信息,比如名字、版本、作者、许可证、依赖等。
一个典型的 package.xml 长这样:
<?xml version="1.0"?>
<package format="3">
<name>my_robot_pkg</name>
<version>0.0.1</version>
<description>我的第一个ROS2功能包</description>
<maintainer email="your_email@example.com">your_name</maintainer>
<license>Apache-2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>std_msgs</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
这里有几个关键点:
- <name>:必须和文件夹名字一致,否则编译会报错
- <version>:建议遵循语义化版本号,比如0.1.0
- <depend>:运行时和编译时都需要依赖的包
- <build_depend>:仅编译时需要
- <exec_depend>:仅运行时需要
<depend> 就够了。它同时覆盖了编译和运行依赖,省心。只有当你明确知道某个包只在编译时需要(比如测试框架),才用 <test_depend>。
3.4 理解CMakeLists.txt:编译的指挥中心
对于CMake功能包,CMakeLists.txt 是编译的核心。它告诉编译器:源文件在哪、依赖在哪、要生成什么目标。
一个基础的 CMakeLists.txt 结构如下:
cmake_minimum_required(VERSION 3.8)
project(my_robot_pkg)
# 查找依赖
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
# 添加可执行文件
add_executable(my_node src/my_node.cpp)
# 链接库
ament_target_dependencies(my_node rclcpp std_msgs)
# 安装规则
install(TARGETS my_node DESTINATION lib/${PROJECT_NAME})
# 编译后处理
ament_package()
咱们一行行拆开看:
| 命令 | 作用 |
|---|---|
cmake_minimum_required |
指定最低CMake版本,ROS2 Humble要求3.8以上 |
project() |
定义项目名,必须和package.xml一致 |
find_package() |
查找并加载依赖包 |
add_executable() |
从源文件生成可执行文件 |
ament_target_dependencies() |
链接依赖库,ROS2特有的方式 |
install() |
指定安装路径 |
ament_package() |
必须放在最后,完成编译后处理 |
ament_package()!我刚开始学ROS2时,有一次写CMakeLists.txt漏了这一行,编译死活不通过,查了半天才发现。这个函数会生成一些必要的安装文件,少了它你的功能包就没办法被其他包引用。
3.5 编译与测试
功能包写好了,怎么编译?回到工作空间根目录:
cd ~/ros2_ws
colcon build --packages-select my_robot_pkg
--packages-select 参数只编译指定的包,省时间。如果你改了很多包,可以不加这个参数,全部重新编译。
编译完成后,记得source环境:
source install/setup.bash
然后运行你的节点:
ros2 run my_robot_pkg my_node
package.xml 和 CMakeLists.txt 里的包名是否一致。我遇到过好几次,就是因为名字拼写不一致,编译报找不到包。另外,colcon build 加上 --symlink-install 参数,可以让Python脚本的修改即时生效,不用每次重新编译。
3.6 本章小结
咱们这一章干了三件事:
- 创建了工作空间,理解了它的目录结构
- 创建了功能包,区分了CMake包和Python包
- 读懂了
package.xml和CMakeLists.txt的核心结构
说白了,工作空间是你的项目文件夹,功能包是你的代码模块。package.xml 是身份证,CMakeLists.txt 是施工图。这三样东西搞明白了,后面写节点、发消息、搭系统,就顺理成章了。
下一章,咱们要写第一个真正的ROS2节点。到时候你会看到,今天建的这个骨架,是怎么长出肌肉来的。