第二章:ROS2基础与环境搭建

各位同学,欢迎来到第二章。上一章我们聊了课程的整体脉络,这一章咱们得把家伙事儿先支棱起来。

ROS2 Humble,是目前做机器人导航和机械臂控制最稳的版本之一。我个人习惯用Ubuntu 22.04搭配Humble,这套组合在工业项目里验证得最多,坑也最少。

2.1 ROS2 Humble 安装

安装其实不复杂,但有几个细节我吃过亏,得跟你念叨念叨。

⚠️ 系统版本核对
ROS2 Humble 只支持 Ubuntu 22.04 (Jammy)。如果你用的是20.04或者24.04,要么换系统,要么选对应的ROS2版本(Galactic或Rolling)。别硬装,会出各种奇奇怪怪的依赖问题。

安装步骤,我习惯分三步走:

  1. 设置语言环境:确保系统支持UTF-8。
  2. 添加ROS2源:把官方软件源加进来,顺便把公钥导进去。
  3. 安装核心包:我建议装 ros-humble-desktop,这个包包含了导航、可视化、仿真等常用工具。如果你硬盘吃紧,装 ros-humble-ros-base 也行,但后面很多例子跑不起来。
# 设置语言环境
sudo apt update && sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8

# 添加ROS2源
sudo apt install software-properties-common
sudo add-apt-repository universe
sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

# 安装ROS2 Humble桌面版
sudo apt update
sudo apt install ros-humble-desktop
💡 我的习惯
装完之后,记得把 source /opt/ros/humble/setup.bash 加到 ~/.bashrc 里。不然每次开终端都要手动source,很烦。我曾经因为这个忘了source,排查了半小时找不到节点,后来发现是环境变量没加载。

2.2 工作空间配置

工作空间,说白了就是你放代码、编译、运行的地方。ROS2里最常用的是colcon工作空间。

我一般这样建:

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

建好之后,你会看到三个新文件夹:build、install、log。

  • src:放你的源码包。
  • build:编译中间文件,别手动改。
  • install:编译产物,相当于安装目录。
  • log:编译日志,出错了来这里翻。
🔑 关键一步
每次编译完,记得 source install/setup.bash。不然你新写的节点ROS2找不到。我建议也把这个加到 ~/.bashrc 里,但要注意顺序:先source ROS2基础环境,再source你的工作空间。

2.3 Colcon编译工具

Colcon是ROS2的官方编译工具。它比ROS1时代的catkin快不少,而且支持并行编译。

常用命令就这几个:

命令作用我的备注
colcon build编译所有包最常用,不加参数就是全量编译
colcon build --packages-select 包名只编译指定包改了一个包就用这个,省时间
colcon build --symlink-install使用符号链接安装我强烈推荐!改Python脚本不用重新编译
colcon build --cmake-args -DCMAKE_BUILD_TYPE=ReleaseRelease模式编译性能敏感时用,比如导航算法
⚠️ 避坑指南
我曾经在编译一个大型项目时,直接 colcon build 跑了半小时。后来发现只需要改一个Python文件,但因为没有加 --symlink-install,每次都要重新编译。从那以后,我新建工作空间第一件事就是加这个参数。

2.4 关键通信机制

ROS2的核心,说白了就是节点之间怎么说话。主要有三种方式:话题、服务、动作。咱们后面做导航和MoveIt协同,这三种都会用到。

2.4.1 话题(Topic)

话题是ROS2里最常用的通信方式。它是异步、单向、多对多的。一个节点发布数据,其他节点订阅接收。

举个例子,激光雷达一直在发扫描数据,导航节点订阅了就能实时拿到。这种场景用话题最合适。

# 发布者示例(Python)
import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class Talker(Node):
    def __init__(self):
        super().__init__('talker')
        self.pub = self.create_publisher(String, 'chatter', 10)
        self.timer = self.create_timer(1.0, self.timer_callback)

    def timer_callback(self):
        msg = String()
        msg.data = 'Hello, ROS2!'
        self.pub.publish(msg)

# 订阅者示例(Python)
class Listener(Node):
    def __init__(self):
        super().__init__('listener')
        self.sub = self.create_subscription(String, 'chatter', self.callback, 10)

    def callback(self, msg):
        self.get_logger().info(f'I heard: {msg.data}')
💡 关于QoS
话题里的那个数字 10 是QoS深度。我建议别用默认值,要根据场景调。比如激光雷达数据丢了就丢了,用 SensorDataQoS;但控制指令必须可靠,用 ReliableQoS。这个细节在导航里特别重要,后面章节会展开。

2.4.2 服务(Service)

服务是同步、双向、一对一的。客户端发请求,服务端回响应。适合那种「问一下,等结果」的场景。

比如你让机械臂去抓一个物体,先问一下「当前关节角度是多少?」——这就是服务调用。

# 服务端示例
from example_interfaces.srv import AddTwoInts

class Adder(Node):
    def __init__(self):
        super().__init__('adder')
        self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_callback)

    def add_callback(self, request, response):
        response.sum = request.a + request.b
        return response

# 客户端示例
class AdderClient(Node):
    def __init__(self):
        super().__init__('adder_client')
        self.cli = self.create_client(AddTwoInts, 'add_two_ints')
        while not self.cli.wait_for_service(timeout_sec=1.0):
            self.get_logger().info('service not available, waiting...')
        req = AddTwoInts.Request()
        req.a = 3
        req.b = 5
        self.future = self.cli.call_async(req)
🔑 注意
服务调用是阻塞的。如果你在回调函数里调服务,小心死锁。我一般用 call_async 异步调用,配合 spin_until_future_complete 来等结果。

2.4.3 动作(Action)

动作是ROS2里最复杂的通信方式,但也是最强大的。它结合了话题和服务的特点:

  • 客户端发目标(Goal)
  • 服务端持续反馈状态(Feedback)
  • 完成后返回结果(Result)
  • 支持取消操作

你想想看,导航机器人从A点到B点,是不是正好符合这个模型?发一个目标点,途中不断反馈当前位置,到了告诉你结果。如果中途想改目的地,还能取消当前任务。

# 动作服务端骨架
from nav2_msgs.action import NavigateToPose

class NavActionServer(Node):
    def __init__(self):
        super().__init__('nav_action_server')
        self.action_server = ActionServer(
            self,
            NavigateToPose,
            'navigate_to_pose',
            self.execute_callback
        )

    async def execute_callback(self, goal_handle):
        # 接收目标
        target_pose = goal_handle.request.pose
        # 持续反馈
        feedback_msg = NavigateToPose.Feedback()
        feedback_msg.distance_remaining = 10.0
        goal_handle.publish_feedback(feedback_msg)
        # 返回结果
        result = NavigateToPose.Result()
        return result
⚠️ 动作 vs 服务
初学者容易搞混。记住一句话:需要反馈的用动作,不需要的用服务。比如「打开灯」用服务,因为开没开一下就知道了;「走到门口」用动作,因为路上需要知道走到哪了。

2.5 本章知识体系

下面这张图,把咱们这章的核心内容串起来了。你可以看到,从系统安装到工作空间,再到三种通信机制,它们之间是层层递进的关系。

ROS2基础与环境搭建 - 知识体系 Ubuntu 22.04 + ROS2 Humble Colcon工作空间 Colcon编译工具 ROS2 三大通信机制 话题 (Topic) 异步 / 单向 / 多对多 发布者 → 订阅者 适合:传感器数据 激光雷达 / 里程计 QoS 配置关键 服务 (Service) 同步 / 双向 / 一对一 客户端 → 服务端 适合:查询/指令 获取关节角度 注意异步调用 动作 (Action) 异步 / 带反馈 / 可取消 目标 → 反馈 → 结果 适合:长时间任务 导航 / 机械臂规划 MoveIt 核心接口 Nav2 导航主要使用话题和动作,MoveIt 主要使用服务和动作

嗯,这一章的内容就这些。环境搭好了,通信机制理解了,后面咱们就能直接上手做导航和机械臂的协同了。记住,动手敲代码比看十遍书都管用。去把环境装好,跑通那几个示例节点,咱们下章见。