4. 工作空间与功能包:创建ROS2工作空间,使用colcon构建功能包,理解package.xml和setup.py

好,咱们正式开始动手了。

前面聊了那么多概念,什么节点、话题、服务……说白了,它们都得有个“家”才能住进去。这个“家”,就是工作空间和功能包。

我个人习惯,拿到一个新机器人或者新项目,第一件事就是搭好工作空间。就像程序员写代码前先建好文件夹一样,这是基本功。

4.1 什么是工作空间?

工作空间(Workspace),你可以把它想象成一个项目文件夹。里面装着你的代码、依赖的库、编译后的文件等等。

ROS2里最常用的是 colcon 工作空间。它的典型结构长这样:

ros2_ws/                  # 工作空间根目录
├── src/                  # 源代码目录(放功能包)
├── build/                # 编译中间文件(自动生成)
├── install/              # 安装文件(自动生成,可执行文件在这里)
└── log/                  # 编译日志(自动生成)

你只需要手动创建 src 文件夹,剩下的 build、install、log 都是 colcon build 命令自动生成的。

小技巧: 我习惯把工作空间命名为 ros2_ws,简单好记。你叫 my_robot_ws 也行,别用中文和空格。

4.2 创建你的第一个工作空间

打开终端,跟着我敲:

mkdir -p ~/ros2_ws/src
cd ~/ros2_ws

嗯,就两行。现在你有了一个空的工作空间。

但光有壳子不行,得往里面放功能包。

4.3 功能包:ROS2的最小单元

功能包(Package)是ROS2里组织代码的基本单位。一个功能包可以包含节点、配置文件、启动文件、消息定义等等。

你可以把它理解成一个独立的软件模块。比如一个“摄像头驱动包”、一个“激光雷达数据处理包”。

创建功能包,我推荐用 ros2 pkg create 命令。举个例子:

cd ~/ros2_ws/src
ros2 pkg create my_first_pkg \
  --build-type ament_python \
  --node-name my_first_node

这条命令干了三件事:

  • 创建了 my_first_pkg 文件夹
  • 指定了构建类型为 ament_python(Python包)
  • 自动生成了一个叫 my_first_node 的节点模板

来看看里面有什么:

my_first_pkg/
├── my_first_pkg/          # 同名的Python包目录
│   ├── __init__.py
│   └── my_first_node.py   # 节点文件
├── resource/
│   └── my_first_pkg
├── test/                  # 测试文件夹
│   ├── test_copyright.py
│   ├── test_flake8.py
│   └── test_pep257.py
├── package.xml            # 包描述文件(重要!)
├── setup.py               # 安装配置文件(重要!)
├── setup.cfg              # setup工具配置
└── package.xml            # 包描述文件
核心文件: 每个功能包必须有两个文件——package.xml 和 setup.py(Python包)或 CMakeLists.txt(C++包)。少了任何一个,colcon都会报错。

4.4 理解 package.xml:包的“身份证”

打开 package.xml,你会看到类似这样的内容:

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

  <exec_depend>rclpy</exec_depend>
  <exec_depend>std_msgs</exec_depend>

  <export>
    <build_type>ament_python</build_type>
  </export>
</package>

这个文件说白了就是告诉ROS2:

  • 你是谁(包名、版本)
  • 谁维护你(维护者信息)
  • 你依赖谁(运行时依赖,比如 rclpy)
  • 你怎么编译(构建类型)
注意: 我曾经遇到过一个问题——忘记在 package.xml 里声明依赖,结果编译通过,但运行时提示找不到模块。所以,每引入一个外部库,都要在 <exec_depend> 里加上。

4.5 理解 setup.py:Python包的“安装说明书”

对于Python功能包,setup.py 负责告诉系统:

  • 哪些文件需要被安装
  • 安装到哪个目录
  • 入口点(entry_points)在哪里

看一个典型的 setup.py:

from setuptools import find_packages, setup

package_name = 'my_first_pkg'

setup(
    name=package_name,
    version='0.0.0',
    packages=find_packages(exclude=['test']),
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='your_name',
    maintainer_email='your_email@example.com',
    description='我的第一个ROS2功能包',
    license='Apache-2.0',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
            'my_first_node = my_first_pkg.my_first_node:main',
        ],
    },
)

这里最关键的,是 entry_points 里的 console_scripts。它定义了可执行命令。

格式是:'命令名 = 包名.文件名:函数名'

比如上面这行,意思就是:你在终端输入 my_first_node,系统就会去执行 my_first_pkg.my_first_node 文件里的 main 函数。

我的习惯: 每次修改了 setup.py 或 package.xml,我都会重新编译一次。别偷懒,不然改了不生效,排查起来很头疼。

4.6 使用 colcon 构建功能包

文件写好了,怎么让它跑起来?用 colcon build。

cd ~/ros2_ws
colcon build --packages-select my_first_pkg

解释一下参数:

  • --packages-select:只编译指定的包,省时间
  • 如果不加这个参数,会编译整个工作空间的所有包

编译成功后,你会看到 build、install、log 三个文件夹出现。

但这时候还不能直接运行。你需要source环境:

source install/setup.bash

这条命令把编译好的包注册到ROS2环境里。之后你就可以运行节点了:

ros2 run my_first_pkg my_first_node
避坑指南: 每次打开新终端,都要重新 source install/setup.bash。我建议把它加到 ~/.bashrc 里,一劳永逸:
echo "source ~/ros2_ws/install/setup.bash" >> ~/.bashrc
source ~/.bashrc

4.7 知识体系总览

下面这张图,帮你理清工作空间和功能包的关系:

ROS2 工作空间与功能包结构 ros2_ws/(工作空间) src/(源代码) my_first_pkg/ ├── package.xml ├── setup.py └── my_first_pkg/ my_first_node.py (节点代码) colcon build build/ 编译中间文件 (自动生成) install/ 安装文件 (可执行文件) log/ 编译日志 source 运行 ros2 run my_first_pkg

从这张图可以看得很清楚:

  1. 你在 src 里写代码(功能包)
  2. 用 colcon build 编译,生成 build、install、log
  3. source install/setup.bash 让系统找到你的包
  4. 最后用 ros2 run 启动节点

嗯,流程就是这么简单。但很多新手会卡在“编译成功但找不到包”这一步。说白了,就是忘了 source。

4.8 本章小结

今天我们干了三件事:

  • 创建了工作空间——ROS2代码的根目录
  • 创建了功能包——用 ros2 pkg create 一键生成
  • 理解了核心配置文件——package.xml 管依赖,setup.py 管安装
  • 用 colcon 编译并运行——colcon build + source install/setup.bash

这些是ROS2开发的基石。后面的所有章节,都会建立在这个工作空间之上。所以,务必亲手敲一遍,别只看不练。

最后提醒: 如果你在编译时遇到 ModuleNotFoundError,先检查 package.xml 里有没有加依赖。如果遇到 command not found: ros2,说明ROS2环境没 source。这两个坑,我当年都踩过。