精通ROS 2机器人编程——ROS 2编程入门

301 阅读29分钟

在上一章中,我们讨论了ROS框架的基础知识及其重要特性。我们详细比较了ROS 1和ROS 2的架构,并探讨了为什么必须从ROS 1迁移到ROS 2。我们还了解了DDS的作用,作为ROS 2的核心部分。我们列举了流行的DDS厂商以及连接ROS 2与DDS的不同层次。希望本章能为你提供ROS 2架构的整体概览及其在机器人软件中的重要性。

本章将为你提供一个实用的概览,介绍如何在任意电脑上快速安装Ubuntu 24.04版的ROS 2并开始使用。我们将探索多种在电脑上搭建ROS 2的方法。完成安装后,将开始熟悉ROS 2的基础知识,接着通过工具、包、工作空间以及Turtlesim(一款可用于学习ROS 2概念的二维模拟器)来学习ROS 2相关概念。最终,我们将详细讨论ROS 2客户端库,它帮助开发者使用多种编程语言编写基于ROS 2的应用程序。理解客户端库后,我们将学习如何开始使用ROS 2开发机器人软件。

本章主要讨论的主题包括:

  • 掌握ROS 2的安装
  • 使用Turtlesim掌握ROS 2的工具和概念

技术要求

跟随本章内容学习,建议使用已安装Ubuntu 24.04 LTS或其他Ubuntu版本的电脑或嵌入式开发板(如Raspberry Pi、Jetson板等)。如果你的设备未安装Ubuntu,请确保拥有Windows 11/10或macOS系统。

本章参考资料可在以下GitHub仓库的Chapter02文件夹中找到:github.com/PacktPublis…

掌握ROS 2的安装

当你看到本节标题时,可能会想知道安装软件到底有什么需要“掌握”的。我相信读完本节,你会明白其中的要点。

大多数机器人的计算机运行Linux发行版,最常见的是Ubuntu。机器人计算机的选择基于机器人应用需求,可能是基于x86_64架构的工业PC,也可能是基于ARM64的计算模块,如Jetson Orin或树莓派。

正如你所知,机器人应用会部署在机器人计算机上,但机器人软件开发主要发生在开发者的工作站或笔记本上,这些设备可能运行Windows、Ubuntu或macOS。

ROS 2能否安装在所有平台?答案是可以。借助Docker、VirtualBox、VMware、UTM等工具,我们可以在多种环境中安装ROS 2。接下来,我们将探讨在机器人和开发机上安装ROS 2的多种方法。

安装Ubuntu 24.04 LTS和ROS 2 Jazzy

如第一章所述,本书主要聚焦于ROS 2 Jazzy版本,它主要兼容Ubuntu 24.04 LTS(LTS代表长期支持)【1】。ROS 2 Jazzy不支持安装在除24.04外的其他Ubuntu版本上。下面介绍如何在开发机和机器人上搭建Ubuntu 24.04。

安装Ubuntu 24.04 LTS

安装Ubuntu 24.04有多种方法,以下是一些参考,帮助你开始为ROS 2 Jazzy搭建环境:

  • 在开发工作站安装Ubuntu(x86_64/ARM64架构)

    • 双系统安装:如果你有基于Intel或AMD x86_64架构的开发工作站,可以从Ubuntu官网下载官方ISO镜像,制作启动U盘,在已有Windows或其他操作系统的电脑上安装。若电脑已有Windows,可以参照此教程进行双系统安装【2】。请确保选择手动安装,方便格式化指定分区。直接在物理机安装Ubuntu性能优于虚拟化方案,因为无需额外软件层干预。
    • macOS搭载Apple Silicon:如果你使用搭载M1系列芯片的苹果Mac,可以试试UTM【3】,一款简易的虚拟化软件,支持在Mac上安装Ubuntu 24.04完整桌面版,无需重启即可运行多个虚拟系统,轻松在Windows/macOS和虚拟Ubuntu间切换。
  • 在VirtualBox/VMware/UTM/WSL 2上安装Ubuntu

    • 直接在物理机安装有风险,尤其是初学者,操作失误可能导致系统崩溃无法启动。
    • 若你习惯使用Windows 10/11或macOS,可通过VirtualBox、VMware等虚拟化软件安装Ubuntu。这类软件称为虚拟机软件。VirtualBox免费下载使用【4】,VMware Workstation个人免费但商业收费【5】。VirtualBox安装Ubuntu 24.04的详细教程见【6】,VMware相关教程见【7】。
    • macOS Apple Silicon用户可用UTM安装Ubuntu 24.04,优势同上【8】。
    • Windows 10/11自带Windows子系统Linux(WSL),允许开发者无需虚拟机即可运行Ubuntu等Linux环境。Ubuntu 24.04在WSL 2中的安装教程见【9】。WSL 2适合学习和实验,不必设置双系统。
  • 在Docker上安装Ubuntu

    • Docker【10】是一种操作系统级虚拟化平台。与VirtualBox等虚拟机软件不同,后者虚拟完整操作系统,Docker容器则虚拟运行在宿主操作系统之上的应用层。Docker容器隔离应用及其依赖,轻量且便携,共享宿主OS内核。Docker需安装在Ubuntu、Windows或macOS等宿主OS上。
    • Docker较轻量,适合在机器人上部署ROS 2应用。Docker Hub【11】公开托管了大量Docker镜像,镜像包含所有库和依赖,容器则是镜像的运行实例。通过Docker命令行工具,我们可以下载、管理和运行各种镜像。
    • 接下来将讲解Docker的安装和配置,展示如何高效部署基于ROS 2的机器人应用。

多数机器人搭载x86_64或ARM64架构的主板,如树莓派、Jetson Orin或搭载Intel/AMD处理器的工业PC。我们可以在这些设备上安装Ubuntu。

  • 若为x86_64机器,安装步骤与上文类似。
  • 若为嵌入式主板(如树莓派或Jetson系列),安装流程不同。Ubuntu对这些板子有专门定制。树莓派5安装Ubuntu 24.04的教程见【12】,Jetson系列Ubuntu安装教程见【13】。

我们已了解了在机器人和开发工作站安装Ubuntu 24.04 LTS的多种方案。接下来,看看如何在Ubuntu上安装ROS 2 Jazzy。

在Ubuntu 24.04 LTS上安装ROS 2 Jazzy

本节提供了在Ubuntu 24.04 LTS上安装ROS 2 Jazzy的详细步骤,适用于x86_64和ARM64架构的机器。

为了简化安装过程,你还可以使用自动化的shell脚本完成安装和卸载。安装脚本ros2_install_jazzy.sh和卸载脚本ros2_uninstall_jazzy.sh均位于Chapter02文件夹中。

Chapter02文件夹中,你可以通过以下命令运行自动安装脚本:

chmod +x ros2_install_jazzy.sh
./ros2_install_jazzy.sh

未来若需卸载ROS 2 Jazzy,可以使用以下命令:

chmod +x ros2_uninstall_jazzy.sh
./ros2_uninstall_jazzy.sh

这是官方提供的Ubuntu、RHEL和Windows 10上安装ROS 2 Jazzy的链接【14】。推荐的操作系统版本是Ubuntu 24.04 Noble Numbat。为了便捷安装,建议通过Debian包管理器进行安装。

下一节将详细讲解ROS 2 Jazzy安装脚本中的各个步骤。

设置系统的区域设置(Locale)

在安装ROS 2之前,设置Ubuntu系统的语言和字符编码非常重要。这是因为DDS使用UTF-8(Unicode转换格式8位)作为IDL类型字符串的字符编码。你可以通过在Ubuntu中运行locale命令来检查Ubuntu 24.04的UTF-8编码。locale命令用于显示和设置系统的区域设置信息,定义了基于地区和语言偏好数据(如日期、时间、数字、货币和文本)的格式和显示方式。安装过程中,Ubuntu会根据我们提供的语言偏好自动生成区域设置。

以下是设置系统区域设置的步骤:

  1. 打开Ubuntu终端,执行以下命令查看当前区域设置:

    locale
    

    如果UTF-8设置正确,输出的第一行应为:

    LANG=en_US.UTF-8
    ...
    
  2. 如果已经正确设置,则无需继续执行下一步;否则,需执行以下命令来安装和更新相关包:

    sudo apt update && sudo apt install locales
    
  3. 生成基础的en_USen_US.UTF-8区域设置:

    sudo locale-gen en_US en_US.UTF-8
    
  4. 设置系统范围的区域设置:

    sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
    export LANG=en_US.UTF-8
    
  5. 完成设置后,再次执行locale命令验证:

    locale  # 验证设置
    

设置完系统区域后,下一步是配置Ubuntu的软件仓库。Ubuntu提供了多个软件包供下载,其中有免费软件,也有受限软件。这些软件包分布在四个主要仓库:

  • main:由Ubuntu的创建者和维护者Canonical官方支持,包含免费且开源的软件包。
  • universe:社区维护的免费开源软件仓库。
  • restricted:包含非开源但可自由分发的专有驱动或固件,通常用于支持特定硬件功能。
  • multiverse:包含因版权、许可或法律限制而可能不能在所有地区或所有用途下自由使用的软件。

ROS 2 Jazzy的软件包位于Ubuntu的universe仓库,因此我们必须先启用universe仓库。

在Ubuntu中启用软件仓库

Ubuntu提供了管理和配置软件源(仓库)的工具。这些工具帮助添加或删除第三方软件仓库,管理这些仓库的GNU隐私保护(GPG)密钥【15】,以及配置系统的软件源。我们需要先安装software-properties-common包来启用universe仓库。

可以通过以下命令安装software-properties-common包,以获得管理Ubuntu中任何仓库的工具:

sudo apt install software-properties-common

安装完成后,我们会获得一个名为add-apt-repository的命令行工具。该命令可以轻松添加或移除仓库或个人软件包存档(PPA)。PPA类似于个人的软件商店,开发者可在其中上传他们的软件【16】。

以下是使用add-apt-repository命令启用universe仓库的方法:

sudo add-apt-repository universe

启用universe仓库后,接下来我们将添加ROS 2的GPG密钥和apt源列表。

添加ROS 2 Jazzy的GPG密钥和apt源列表

GPG密钥类似于数字锁,用于保障软件安全。它们用来验证下载软件包的真实性和完整性,确保你下载的软件(例如来自PPA)确实是由开发者或公司发布,且未被他人篡改。

apt源列表是Ubuntu系统中的一个文件,包含了系统可用的软件仓库信息,供apt包管理器下载和安装软件包。该文件告诉Ubuntu去哪里查找软件更新和新软件。源列表文件的位置为/etc/apt/sources.list

ROS 2提供了一个名为ros2-apt-source的软件包,用于配置ROS各个仓库的密钥和apt源。安装该包后,系统中会自动配置ROS 2仓库,仓库配置在新软件包发布时也会自动更新。我们使用名为curl的软件工具下载该软件包并保存到指定路径。

首先,执行以下命令更新Ubuntu软件包列表并安装curl,更新时会包含universe仓库的软件包列表:

sudo apt update && sudo apt install curl -y

安装curl后,使用以下命令获取最新版本的ros2-apt-source包的版本号:

export ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F "tag_name" | awk -F'"' '{print $4}')

获取版本号后,使用curl命令下载最新的ros2-apt-source包:

curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo $VERSION_CODENAME)_all.deb"

接着,使用以下命令安装ros2-apt-source包:

sudo apt install /tmp/ros2-apt-source.deb

至此,我们已完成ROS 2 Jazzy的GPG密钥和apt源列表的保存。下一节将介绍如何安装ROS 2开发工具。

安装开发工具

在ROS 2中安装开发工具,可以帮助自动格式化用C++或Python编写的代码,在编译或运行前分析潜在错误或效率问题,提供用于配置和构建ROS 2包的构建工具和脚本,最后还提供调试工具和单元测试支持,以便发现程序中的bug。

在Ubuntu 24.04上,可以通过以下命令安装这些工具:

sudo apt update && sudo apt install ros-dev-tools

安装完ROS开发工具后,我们接着安装ROS 2 Jazzy。

安装ROS 2 Jazzy

在安装ROS 2 Jazzy软件包之前,第一步是更新软件包列表并升级已安装的软件包。以下命令将更新最新的ROS 2软件包并升级所有Ubuntu软件包,确保系统软件为最新状态:

sudo apt update
sudo apt upgrade

完成更新和升级操作后,就可以安装ROS 2 Jazzy了。安装有两个选项:

  • ros-jazzy-desktop:包含ROS 2的大部分功能,推荐安装此版本。
  • ros-jazzy-base:仅包含核心通信库,不含GUI工具,主要是通信库、消息包和命令行工具。

以下是两种安装方式的命令,你可以选择其中之一:

桌面版安装:

sudo apt install ros-jazzy-desktop

ROS-Base安装:

sudo apt install ros-jazzy-ros-base

桌面版安装所需时间较长,具体取决于网络速度和电脑配置;而ROS-Base安装更快,更适合嵌入式开发板。

恭喜你,ROS 2 Jazzy安装完成了。但这还不是全部!要开始使用ROS 2命令和工具,必须执行以下命令加载环境变量:

source /opt/ros/jazzy/setup.bash

加载此文件后,当前终端即可识别ROS 2工具和包。此命令非常重要,否则ROS 2命令将不可用。

为了每次打开新终端都自动加载环境,可以执行:

echo "source /opt/ros/jazzy/setup.bash" >> /home/$USER/.bashrc

这会将加载命令写入~/.bashrc(Linux终端配置文件),每次打开新终端时自动执行,无需手动加载。

完成Ubuntu 24.04上的ROS 2 Jazzy安装后,可以运行以下测试确保环境配置正确:

  1. 打开Ubuntu终端,新增一个标签页。

  2. 在第一个标签页运行:

    ros2 run demo_nodes_cpp talker
    
  3. 在第二个标签页运行:

    ros2 run demo_nodes_py listener
    

如果一切配置正确,终端将显示相应的通信输出,如下图所示。

image.png

上面的示例只是一个使用ROS 2的发布者/订阅者演示。如果该演示能正常工作,说明安装和配置成功。如果出现问题或报错,请确认ROS 2环境是否正确加载。你可以按Ctrl + C来停止运行中的节点。

本节讨论了在Ubuntu 24.04 LTS上安装ROS 2 Jazzy。接下来,我们将探讨如何在其他操作系统(如Windows和macOS)上搭建ROS 2 Jazzy,主要面向开发环境。

在Windows 11/10上安装ROS 2 Jazzy

我们可以通过多种方式在Windows上安装ROS 2 Jazzy,主要方法如下:

  • 在VirtualBox/VMware中安装ROS 2 Jazzy:之前已介绍如何在VirtualBox等虚拟机软件上安装Ubuntu 24.04 LTS,安装ROS 2 Jazzy可参照同样步骤。此法易于配置,适合ROS 2实验,即使虚拟机系统崩溃,也不会影响主机Windows系统。
  • 在WSL 2中安装ROS 2 Jazzy:WSL是Windows 10/11中的一项功能,允许在Windows上运行Linux环境,无需虚拟机或双系统。官方启用WSL教程见【17】。WSL分WSL 1和功能更强的WSL 2,我们可通过WSL 2安装Ubuntu 24.04或22.04。ROS 2 Jazzy的Ubuntu安装步骤同样适用WSL 2。WSL不仅支持命令行,也支持GUI工具,且支持Windows与WSL间的文件共享。
  • 在Windows 10(无虚拟化)上原生安装ROS 2 Jazzy:仅支持Windows 10,不支持Windows 11。此方法不使用WSL,ROS 2完全原生运行,无中间层。安装步骤较繁琐且耗时,但如果需要将ROS 2与Windows原生应用深度集成,这是唯一途径。安装指南见【18】。

以上介绍了在Windows 10/11上搭建ROS 2 Jazzy的方法,接下来讨论macOS上原生和虚拟化安装的方法。

在macOS上安装ROS 2 Jazzy

macOS安装ROS 2 Jazzy有多种方式,主要包括:

  • 在VirtualBox/VMware/UTM中安装:如果你使用基于Intel处理器的Mac,可以安装VirtualBox或VMware,继而安装Ubuntu 24.04 LTS和ROS 2 Jazzy。若使用搭载M1/M2/M3苹果芯片的Mac,则推荐用VMware或UTM【19】安装Ubuntu 24.04 LTS。
  • 在macOS上编译运行ROS 2源码:若希望在macOS上原生运行ROS 2,无需虚拟化,可下载源码编译运行,具体流程见【20】。步骤较复杂,但若应用需要原生支持,这是推荐方法。
  • 在Mac上双系统安装Ubuntu:此方案不在macOS内安装,而是与macOS并存双启动。基于Intel的Mac可以直接使用USB启动盘安装Ubuntu 24.04 LTS【21】,然后按照ROS 2 Jazzy安装说明继续操作。搭载苹果芯片的机器可参考Ubuntu Asahi项目【22】,该社区项目致力于将Asahi Linux移植至Ubuntu,目标是实现苹果芯片硬件的稳定和功能完备支持。

我们已经了解了Windows、Linux和macOS上安装ROS 2 Jazzy的相关教程和参考资料。接下来介绍一种适用于所有操作系统的快速搭建ROS 2 Jazzy的方法。

在Docker中安装ROS 2 Jazzy

Docker【23】是一项开源技术,帮助软件开发者快速开发和部署Windows、Linux及macOS应用程序。Docker也被广泛应用于机器人软件的开发和部署。使用Docker的主要优势在于,我们可以快速在任何ROS发行版和Linux发行版中开发和部署基于ROS的应用。即使你的宿主操作系统是Ubuntu 20.04,也可以利用Docker在ROS 2 Jazzy(基于Ubuntu 24.04)环境中开发ROS 2应用。这有助于在不同ROS 2发行版中构建和测试ROS 2应用。唯一的前提是你的操作系统中安装了Docker软件。Docker是本书重点介绍的一项技术,本节将详细讲解Docker。

Docker是一款通过“容器”技术来创建、部署和运行应用的软件工具。Docker中的每个容器都包含运行应用所需的轻量级软件环境实例,该环境包括代码、库和依赖,保证容器能在不同环境中运行。与之前看到的虚拟机不同,容器不包含独立的操作系统,而是与宿主Linux内核并行运行,通过抽象层来支持多环境运行。因此,像虚拟机那样,无需安装完整操作系统即可运行应用。

在深入了解Docker之前,先介绍如何在Ubuntu 24.04 LTS主机上安装Docker。

Docker的官方安装说明见其官网【24】。本书提供了自动化脚本,实现Ubuntu 24.04上Docker及NVIDIA容器工具包(NVIDIA Container Toolkit)的安装【25】。NVIDIA容器工具包支持构建和运行GPU加速容器,与Docker协同工作。

安装Docker和NVIDIA容器工具包

安装步骤如下:

  1. 打开本书GitHub仓库,进入路径:

    Chapter02/ros2_jazzy_docker/docker_setup_scripts
    
  2. 找到脚本文件setup_docker_ubuntu.sh,在该文件夹中打开终端,执行:

    chmod +x setup_docker_ubuntu.sh
    ./setup_docker_ubuntu.sh
    

此脚本会安装Docker的所有依赖。如果你有正确安装NVIDIA显卡及驱动,脚本还会安装NVIDIA容器工具包,为容器提供图形加速支持。

安装完成后,可以用以下命令检查Docker是否正在后台运行:

systemctl status docker

如果设置正确,你将看到如下输出:

image.png

如果Docker状态显示为

active

那么你可以在终端运行以下命令,确认Docker运行正常:

docker info

你将会看到如下输出,包含了Docker的所有系统详细信息:

image.png

确保电脑环境正常后,我们就可以开始使用Docker运行ROS 2 Jazzy,并在过程中了解Docker的相关概念。

使用Docker运行ROS 2 Jazzy

在Ubuntu 24.04 LTS上使用ROS 2 Jazzy之前,需要完成以下几个步骤。下面各小节将详细讲解。

第一步:拉取基础镜像并构建自定义Docker镜像

使用Docker的第一步是从Docker Hub或其他来源拉取基础镜像。先来了解Docker镜像和基础镜像的概念。

Docker镜像是包含应用代码、依赖和运行环境的文件,可以看作是创建Docker容器的模板。基础镜像就是用来构建你Docker镜像的基础。例如,你可以选择ROS 2 Jazzy的Docker镜像作为基础镜像,再基于它创建带有所有依赖的自定义镜像。

Docker Hub【26】为开发者提供了免费访问公共Docker镜像的服务,并允许上传自有镜像。开发者可以用这些镜像作为基础,创建容器。

在机器上安装Docker后,可以直接从Docker Hub拉取ROS 2 Jazzy的镜像。Open Robotics在其账号中发布了所有ROS 1和ROS 2的Docker镜像【27】,你可以拉取任意镜像。

以下命令用于拉取Jazzy的基础镜像,这样无需安装任何软件就能进入ROS 2 Jazzy环境:

docker pull osrf/ros:jazzy-desktop-full

执行后,镜像开始下载,下载时间视网络速度而定。下载完成后,你就拥有了ROS 2 Jazzy的基础镜像。下一步是用该镜像创建容器。正如之前所述,Docker镜像类似模板,启动容器后,容器内即为ROS 2 Jazzy环境。通常我们通过shell在容器内工作,因为大部分基础镜像都是轻量级环境,没有完整的桌面环境,区别于Ubuntu 24.04 LTS。

第二步:基于ROS 2 Jazzy镜像创建容器

使用以下命令创建容器:

docker run -it --name master_ros2 osrf/ros:jazzy-desktop-full bash

在终端运行后,你会看到一个新的命令行界面,显示为类似如下的root用户提示符:

root@eafa922c9072:/#

这就是名为master_ros2的ROS 2 Jazzy容器的shell。上述命令解释如下:

  • docker run用于创建并启动容器。
  • osrf/ros:jazzy-desktop-full是所用的镜像名称。
  • --name master_ros2为容器指定名称,便于启动、停止和管理(该参数可选,不指定时Docker会自动分配一个随机名称)。
  • -it表示以交互模式运行容器,并通过终端shell与容器交互。
  • bash命令告诉容器启动后运行bash shell。

容器创建完成后,即可获得ROS 2 Jazzy环境,容器内你可以执行任何操作。删除容器后,所有改动会丢失。容器中所做的更改会被缓存,支持启动和停止时数据保留,除非你重新构建容器且未将数据挂载到宿主系统。

测试ROS 2 Jazzy是否正常运行

执行一个示例发布者节点,发布“Hello World”字符串。该命令应在Docker容器终端执行:

ros2 run demo_nodes_cpp talker

若你看到类似如下输出,说明环境运行正常:

[INFO] [1726068567.949579583] [talker]: Publishing: 'Hello World: 1'
[INFO] [1726068568.949567985] [talker]: Publishing: 'Hello World: 2'
[INFO] [1726068569.949575774] [talker]: Publishing: 'Hello World: 3'

接下来,我们可以执行订阅该消息的命令。要在Docker中开启新的shell,请参考下一节内容。

第三步:在ROS 2 Jazzy容器中运行新命令

创建容器并进入shell后,我们已经执行了ROS 2的发布者程序,并确认其正常运行。接下来,如何访问该容器的另一个终端并运行订阅者代码呢?这时就用到docker exec命令了。

docker exec命令允许我们在同一个容器中运行另一个程序或命令。打开宿主操作系统的新终端,执行以下命令以访问该容器终端:

docker exec -it master_ros2 bash

此命令会为正在运行的容器附加一个新的shell。进入后,你可以用下面的命令加载ROS 2 Jazzy环境,使当前终端可用ROS 2工具:

root@eafa922c9072:/# source /opt/ros/jazzy/setup.bash

加载完成后,即可运行订阅者节点:

root@eafa922c9072:/# ros2 run demo_nodes_cpp listener

如果一切正常,会看到如下输出:

[INFO] [1726069883.429240302] [listener]: I heard: [Hello World: 3]
[INFO] [1726069884.429221835] [listener]: I heard: [Hello World: 4]
[INFO] [1726069885.429212317] [listener]: I heard: [Hello World: 5]

按Ctrl + C可终止运行中的节点,按Ctrl + D退出shell。退出后容器可能仍在后台运行,可以使用下一节命令停止容器。

第四步:启动、停止和删除容器

停止正在运行的容器命令:

docker stop master_ros2

查看电脑中所有容器状态命令:

docker ps -a

该命令显示所有容器及其状态(停止或运行中)。停止容器后,如需重新启动,执行:

docker start master_ros2

启动容器后,可用docker exec附加shell并访问容器:

docker exec -it master_ros2 bash

因此,容器创建后,除非Docker镜像有改动,否则无需重复创建容器。可根据需要随时启动和停止容器。

若要删除当前容器,先停止它:

docker stop master_ros2
docker rm master_ros2

删除容器后,需重新用docker run命令创建新容器。

我们已经介绍了Docker的基础命令。以下是一个Docker命令速查表的参考,方便你学习更多命令【28】。接下来,让我们讨论Docker中的另一个重要概念——Dockerfile。

ROS 2 Jazzy的Dockerfile

Dockerfile是一个文本文件,包含用于构建自定义Docker镜像的指令。它定义了如何构建该镜像。那么,为什么我们需要构建自定义Docker镜像呢?假设你使用ROS 2开发了一些机器人软件,运行这些软件可能需要安装一些依赖。之前使用的ROS 2 Jazzy基础镜像可能未包含所有依赖项。在这种情况下,我们可以编写一个Dockerfile,以ROS 2 Jazzy为基础镜像,添加所需依赖和环境,从而创建一个新的自定义Docker镜像。Dockerfile就像一份“食谱”,是一系列用于生成镜像的指令。我们可以使用Dockerfile专用指令编写该文件,并通过docker build命令来构建它。

下面是一个基本的ROS 2 Jazzy Dockerfile示例:

# 基础镜像
FROM osrf/ros:jazzy-desktop-full

# 更新并升级Ubuntu软件包,安装pip
RUN apt update && apt upgrade -y
RUN apt install -y python3-pip

# 创建容器时执行bash命令
CMD ["/bin/bash"]

你可以切换到Chapter02/ros2_jazzy_docker/docker_basics文件夹,找到该文件,名称默认是Dockerfile。如果需要管理多个Dockerfile,也可以自定义命名,例如Dockerfile.basicDockerfile.master_ros2等。构建镜像时,可以指定Dockerfile文件名以构建对应的镜像。

下面的命令用于根据Dockerfile构建自定义镜像。打开包含Dockerfile的文件夹终端,执行:

docker build -f Dockerfile -t test_ros2:v0.1 .

命令解析:

  • -f Dockerfile:指定要使用的Dockerfile文件。
  • -t test_ros2:v0.1:指定镜像名称及标签(tag)。标签可选,但建议为每个镜像加标签,以便版本管理。
  • 末尾的.代表当前目录,Dockerfile会基于此目录查找需要复制到容器的文件。

构建成功后,终端会显示类似如下的提示信息:

image.png

构建镜像后,我们可以像之前一样使用以下命令创建容器:

docker run -it --name test_ros_dev test_ros2:v0.1

创建容器后,可以使用docker exec命令访问更多的shell。

现在你已经了解了如何通过编写Dockerfile来构建ROS 2 Jazzy镜像,接下来我们详细解析上述Dockerfile。

该Dockerfile以ROS 2 Jazzy基础镜像为起点,FROM指令用于指定所用基础镜像。接着,使用RUN指令对基础镜像的软件包进行更新和升级。通过RUN指令,我们可以在基础镜像中安装软件包。更新升级操作本身就使基础镜像变成了自定义镜像,因为新的镜像包含了最新的软件包;然后使用RUN指令安装了一个新的Ubuntu软件包python3-pip。最后,CMD指令指定了容器启动时默认执行的命令。

在执行docker run命令时,可以通过在命令末尾添加命令来覆盖CMD指定的默认命令。Dockerfile中还有一个类似的指令叫ENTRYPOINT,作用类似,但该命令无法被docker run命令覆盖。

接下来,我们来看一些更复杂的用于ROS 2开发的Dockerfile。你可以在Chapter02/ros2_jazzy_docker/docker_basics文件夹中找到Dockerfile.basic,这是一个适合开始ROS 2开发的基础Dockerfile。

ROS 2 Jazzy基础镜像中的默认用户是root用户。该Dockerfile会创建一个新用户,限制权限,比使用root用户更安全。因为root用户在Docker中拥有完全权限,开发时建议使用普通用户,并通过sudо命令临时获取root权限,这样更为安全。

构建并使用该Dockerfile的方法,与前面示例中的Docker命令相同。

到目前为止,我们看到的都是通过shell命令与Docker容器交互。那么在Docker容器中使用带GUI的应用程序呢?答案是可以的。下一节,我们将学习如何运行支持GUI的ROS 2工具。

在ROS 2 Jazzy容器中启用GUI

本节将介绍如何在Docker中启用GUI支持。启用GUI后,我们可以使用ROS 2 Jazzy的图形界面和命令行工具。让我们开始吧。

进入目录:

Chapter02/ros2_jazzy_docker/docker_gui

在这里你会发现一个与Dockerfile.basic类似的Dockerfile,名为Dockerfile.master_ros2。这两个文件几乎完全相同,主要区别在于docker run命令的不同。你还会看到与Dockerfile.master_ros2配套的shell脚本,用于构建镜像、创建、启动、停止和删除Docker容器。这些脚本简化了操作,无需记忆所有Docker命令。脚本还支持命令行参数,可以指定Dockerfile、容器名等,用于构建镜像和运行容器。

构建Docker镜像

我们有一个名为build_image.sh的脚本,接受多个参数,如Docker镜像名、用户名等,并使用docker build命令构建Dockerfile。

先讲讲脚本用法。在运行脚本前,先在你的主目录下创建一个用于存放ROS 2包的文件夹:

mkdir -p ~/master_ros2_ws/src

创建完成后,确保当前路径在docker_gui文件夹,使用以下命令构建Docker镜像:

./build_image.sh ros2_gui:v0.1 master_ros2_ws robot

此脚本的第一个参数是自定义镜像名称及版本号,第二个参数是ROS 2工作空间名称(将在后续章节详细介绍),第三个参数是你希望在Docker内创建的用户名。

镜像构建完成后,可以用以下脚本创建容器:

./create_container.sh ros2_gui:v0.1 master_ros2_ws ros2_dev

运行此脚本时需提供刚创建的镜像名、宿主机上的ROS 2工作空间名,以及容器名称。脚本会检测你的Ubuntu系统是否安装了NVIDIA显卡及驱动,若已安装,将启用NVIDIA显卡的图形加速。脚本内部使用了docker run命令,并带有多种参数,用于图形加速、挂载数据卷以及挂载宿主机上的ROS 2工作空间。这样创建的容器允许你从终端运行GUI程序,界面会直接弹出。

创建容器后,你将进入shell环境。运行以下命令,检查容器内是否启用了GUI:

robot@robot-pc:~/master_ros2_ws$ rviz2

该命令将启动RViz2,这是ROS 2中非常流行的图形界面工具。你会在屏幕上看到类似如下的RViz2界面:

image.png

恭喜你!我们已经成功创建了一个启用GUI的Docker容器。脚本的其余部分用于启动、停止和删除容器。接下来我们看看如何使用它们。

运行以下命令打开已运行容器的新终端,参数是容器名称,这里是:

./start_container.sh ros2_dev

启动后,你会发现终端的路径是在宿主机上创建的ROS 2工作空间目录:

robot@robot-pc:~/master_ros2_ws$

这里的master_ros2_ws文件夹不在Docker容器内,而是从本地机器挂载进来的。如果你还记得,我们在本地机器上创建了ROS 2工作空间,这个文件夹正是我们在Docker内访问的目录。实现方式是通过docker run命令在创建容器时挂载宿主机文件夹(volume mounting)。

这种方式非常适合利用Docker抽象,在同一工作空间上使用不同ROS 2发行版(如Jazzy、Iron、Humble)进行开发。源码存放在宿主操作系统中,但可以挂载到容器里构建和运行。

你甚至可以用VS Code等IDE,通过Docker容器做远程开发。简言之,Docker为机器人开发者提供了快速创建和测试ROS 2应用于任意ROS发行版的便利。

若要停止运行的容器ros2_dev,可在docker_gui文件夹下执行:

./stop_container.sh ros2_dev

运行该脚本并传入容器名,即可停止对应容器。

若需删除容器,运行:

./remove_container.sh ros2_dev

创建Docker容器新终端后,可以执行以下shell脚本,该脚本已包含在ROS 2 Jazzy基础镜像中,用于加载ROS 2环境:

source /ros_entrypoint.sh

我们已经介绍了Docker中一些重要的操作,这些内容将在后续章节中广泛使用。你也可以参考【29】【39】获取更多关于ROS 2 Docker配置的信息。

到目前为止,我们了解了如何操作单容器应用。接下来,我们将学习多容器应用的运行方式,即ROS 2节点分别运行在不同容器中,并且这些容器间的节点可以相互通信。

使用Docker Compose运行ROS 2 Jazzy

Docker Compose【30】是Docker中针对机器人应用的另一个实用功能。之前我们用Docker命令行工具操作单个容器,那么如何同时管理多个容器呢?举例来说,一个机器人应用可能包含一个基于JavaScript框架的前端应用、一个ROS 2导航应用,以及一个与导航应用交互的深度学习应用。在这种情况下,将这三类应用分别放在不同的Docker镜像中并分别运行在独立容器内,通过容器间通信协调工作会更合理。

针对这种多容器场景,docker-compose是更佳选择,它能用一条命令创建和管理多个容器。我们还可以配置Docker网络,使得这些容器通过ROS 2的DDS协议实现通信。docker-compose其实是Docker命令行工具的一个插件,已包含在我们的Docker安装脚本中。

我们可以写一个docker-compose配置文件,包含多个容器的配置,比如Docker镜像、容器名称及docker run命令中的其他参数。写好配置文件后,只需一条命令即可启动所有容器。

你可以在项目的路径:

Chapter02/ros2_jazzy_docker/docker_compose

中找到一套Docker Compose配置文件。默认配置文件名为docker-compose.yml。这里是一个基础的示例内容:

version: '3.8'
services:
  talker:
    image: osrf/ros:jazzy-desktop-full
    command: ros2 run demo_nodes_cpp talker
  listener:
    image: osrf/ros:jazzy-desktop-full
    command: ros2 run demo_nodes_cpp listener
    depends_on:
      - talker

文件开头定义了Docker Compose的版本。services部分定义了要启动的多个容器服务。本例中有两个服务:talkerlistener,均基于ros-jazzy-desktop-full镜像,分别运行ROS 2中的发布者和订阅者节点用于测试。depends_on标签表明listener依赖于talker,因此docker-compose会先启动talker再启动listener

确保当前路径在docker_compose文件夹下,执行命令:

docker compose up

该命令会启动并运行docker-compose.yml中定义的所有服务。通过在一个YAML文件中定义服务、网络和数据卷,docker-compose简化了多容器Docker应用的管理。

执行该命令后,你将看到类似如下的输出:

image.png

你可以看到talkerlistener两个服务分别在不同的容器中相互通信。

如果你想停止这些服务,可以执行:

docker compose down

请确保你的终端当前位于docker_compose文件夹内。

在同一文件夹中,你还可以找到一个更详细的docker-compose配置文件,名为docker-compose1.yml。该文件展示了配置文件中可使用的更多参数。你可以将此文件重命名为docker-compose.yml,然后使用docker compose updocker compose down命令来启动或停止服务。

在机器人中安装ROS 2 Jazzy

我们已经了解了如何在开发机器上搭建ROS 2 Jazzy,接下来看看如何在机器人上安装。

机器人中通常使用单板计算机(SBC)或定制的计算模块。流行的SBC和计算模块中既有x86_64架构,也有ARM64架构。你可以在这些平台上安装Ubuntu并使用Docker。两款常见的机器人开发板是Raspberry Pi和NVIDIA Jetson系列。

最新的Raspberry Pi板和模块可以在【31】找到,最新的Jetson系列板和模块见【32】。

最新的Raspberry Pi支持安装64位的Ubuntu 24.04 LTS,这属于ROS 2 Jazzy的一级支持操作系统(tier 1 OS),你可以直接用ROS 2安装脚本(本章GitHub第2章代码文件夹中提供)从二进制安装ROS 2。ARM 32位操作系统是三级支持(tier 3),需要从源码构建安装。Raspberry Pi默认系统是Raspberry Pi OS,基于Debian,仅提供三级支持。不过你可以先在Raspberry Pi OS上用Docker安装ROS 2 Jazzy,运行Ubuntu 24.04(一级支持系统),具体可用Docker安装脚本完成。

安装Ubuntu 24.04 LTS到Raspberry Pi的参考见【33】。

我们也可以用以下命令在Raspberry Pi上通过Docker搭建ROS 2 Jazzy,正如之前介绍的:

docker pull ros:jazzy-ros-core
docker run -it --rm ros:jazzy-ros-core

Jetson系列板默认搭载定制的Ubuntu,内置NVIDIA驱动和Jetpack SDK【34】。NVIDIA维护ROS 2的Debian软件包,你可以在宿主系统上直接安装ROS 2 Jazzy,或用Docker运行。NVIDIA还提供了jetson-containers【35】,可用于构建带有多种库的Docker镜像,包括AI库、ROS、视觉库等,你可以按需选择组合,并利用NVIDIA提供的脚本构建,非常适合机器人应用。

例如,使用jetson-containers构建包含机器人和AI多种库的镜像命令如下:

jetson-containers build --name=my_container pytorch transformers ros:jazzy-desktop

在机器人电脑上使用Docker是更好的方案,因为它简化了机器人软件的部署,无需担心依赖问题,同时通过拉取新镜像也能轻松完成软件更新。

我们已经介绍了在开发机和机器人电脑上安装ROS 2 Jazzy的多种方法。下一节将讲解ROS 2中的各种概念和工具学习。

掌握ROS 2工具和概念

本节我们将通过实践操作来探讨ROS 2中的重要概念。在开始使用ROS 2 Jazzy编程之前,必须理解这些概念。我们会结合不同的ROS 2工具,辅以示例来讲解每个概念。

在深入概念之前,我们先了解为什么选择ROS 2进行机器人编程。ROS 2是一个软件框架,提供了一整套库、工具和功能,帮助你构建机器人应用。那么,ROS 2为机器人编程带来的核心特性是什么?答案是进程间通信(Inter-Process Communication,IPC),也就是操作系统中不同进程之间的通信。

正如你所知,机器人可能包含多个传感器、执行器和计算单元。机器人传感器数据需要被采集和处理,再生成控制信号来驱动执行器。通常,这些操作不会集中在单一进程中完成,原因是如果某个操作失败,可能导致整个进程崩溃。因此,我们会用多个进程协同完成工作。ROS 2正是在这里发挥作用,它为C++/Python程序提供了多样的API,帮助它们以多种方式交换数据。数据可以持续发送给另一个程序,类似请求-响应的交互。这是ROS 2赋予机器人开发者的核心功能,其他功能都是基于此核心构建的。现在,我们正式开始了解ROS 2的各个概念。

如何运行ROS 2程序

这里我们不是编写新的程序,而是运行ROS 2安装时自带的已有程序。

使用ROS 2,你可以创建自己的机器人节点或应用来执行特定任务。接下来,我们看看具体如何运行这些节点:

ROS 2提供了一个命令行工具ros2,帮助我们在ROS 2框架中执行多种操作。首先,在你安装了ROS 2的机器上(可以是宿主操作系统,也可以是Docker容器)打开终端。

确保已经加载了ROS 2环境。这里以之前创建的带GUI的Docker容器为例,进入docker_gui文件夹,运行:

./start_container.sh ros2_dev

然后使用以下命令加载ROS 2环境:

source /ros_entrypoint.sh

在终端执行ros2命令:

$ ros2

如果ROS 2环境配置正确,终端将显示如下输出:

image.png

ros2命令是ROS 2中的主命令,它包含多个子命令,如图2.7所示。你可以看到诸如run这样的子命令,它用于运行指定的ROS 2节点。从上述列表中,你可以了解每个子命令的用途。只需在终端输入相应子命令,就能查看该命令的使用说明。例如,输入ros2 run,终端会显示必须传递的参数说明。ros2命令是ROS 2命令行界面(CLI)的核心命令。

要运行某个具体的ROS 2程序或可执行文件,使用以下命令:

ros2 run ros_pkg_name executable_name

例如:

ros2 run demo_nodes_cpp talker

在这个例子中,demo_nodes_cpp是一个ROS 2包,talker是该包内的C++程序可执行文件名称。demo_nodes_cpp随ROS 2 Jazzy一起安装。

你可能会想知道什么是ROS 2包,接下来我们先了解ROS 2包的概念,再继续介绍ros2 run命令的用法。

什么是ROS 2包?

ROS 2包是ROS 2框架中的软件组织单位。它包含实现机器人应用中特定功能所需的代码和文件。通常,一个ROS 2包由节点(执行计算的程序)、库文件、配置文件、启动文件,以及诸如消息和服务定义等资源组成。

在ROS 2中,一切功能都被封装在包内。包其实就是一个文件夹,我们可以用ros2 pkg命令创建,它内部会包含package.xmlCMakeLists.txt等文件,用于维护包的身份信息。包名、依赖关系等信息都写在这些文件里。

安装ROS 2时,会默认安装其核心包。社区还贡献了大量ROS 2包,这些包可以通过Git轻松分发,或添加到ROS 2官方仓库,并可直接安装其二进制文件。

ROS 2的一个有用特性是,可以方便地复用他人开发的包。每个包针对特定应用,比如ROS 2中的demo_nodes_cpp包就包含了C++示例程序。

后续章节我们将详细介绍ROS 2包的结构。

回到ros2 run命令,第一参数是包名,第二参数是可执行文件名。如果你用ROS 2写C++程序,需要先编译生成可执行文件,这个命令中的执行文件名就是指编译出来的那个可执行程序。示例中的这些演示程序已随ROS 2安装。

如果你自己创建了包含C++代码的ROS 2包,编译后也会生成可执行文件。下一章我们将学习如何创建包并将C++代码放入其中。

运行示例命令时,会看到执行文件开始运行,输出如下:

[INFO] [1726326377.958376860] [talker]: Publishing: 'Hello World: 1'
[INFO] [1726326378.958355664] [talker]: Publishing: 'Hello World: 2'
[INFO] [1726326379.958358086] [talker]: Publishing: 'Hello World: 3'

一旦执行文件启动,一个ROS 2节点就被创建了。

什么是ROS节点?

当你启动一个ROS 2可执行文件时,它会运行并成为一个进程,这个进程称为ROS 2节点(node)。也就是说,当你使用ROS 2的C++/Python API编写程序,编译构建并运行这个可执行文件时,就会产生一个ROS 2节点。ROS 2节点是执行某种计算任务的单个进程,比如采集传感器数据、处理数据,或者在机器人计算机或操作员计算机上执行其他操作。

ROS 2中的节点可以借助底层的DDS中间件发布和订阅各种数据类型。一个机器人应用通常由一组不同用途的ROS 2节点组成。每个节点可以通过ros2 run命令单独启动;也可以使用ros2 launch文件等方式,通过一个命令同时启动多个节点。

我们可以使用ros2 node命令查看节点的详细信息。该命令不仅能显示指定节点的信息,还能列出操作系统中正在运行的所有节点。

在一个新终端执行以下命令:

ros2 node list

它会列出当前运行的节点列表,比如输出:

/talker

/talker不是我们的可执行文件名,而是代码内部定义的节点名称,目前对我们不可见。下一章我们将演示如何定义节点名称。每个节点名称都是唯一的,你可以自由命名节点,确保不同节点间名称不重复。

执行以下命令可以查看节点的详细信息,包括订阅者(Subscribers)、发布者(Publishers)、服务(Services)和动作(Actions):

ros2 node info /talker

示例输出如下:

Subscribers:
Publishers:
  /chatter: std_msgs/msg/String
Service Servers:
Service Clients:
Action Servers:
Action Clients:

在这个例子中,没有订阅者、服务服务器、客户端或动作服务器,但可以看到一个发布者/chatter。目前我们还不了解ROS 2中的发布者、订阅者、服务和动作,它们都是不同节点之间的通信模式。理解这些概念后,再回来看这部分内容会更清晰。

什么是ROS话题(Topic)?

在上面的命令输出中,我们看到了“发布者”(Publisher)和“订阅者”(Subscriber)这两个术语。节点中的发布者负责发送某种类型的数据,订阅者则接收相应类型的数据。节点通过ROS话题(topic)发布和订阅各种类型的数据。ROS话题是不同节点之间通信的媒介,它被称为命名数据总线,节点可以通过它交换各种类型的数据。

我们可以在ROS节点中创建任意数量的话题,发送各种类型的数据,比如整数、浮点数、数组、图像等;同样,也可以在节点中订阅这些不同类型的数据。ROS话题是ROS节点中常用的一种通信方式。与ROS话题的通信是异步的,这意味着发布者可以连续发送数据而不必等待接收节点的响应,发布者甚至不必知道数据被谁接收。可以把它看作是N对M的通信机制。

在上面的示例中,发布数据的话题是/chatter(std_msgs/msg/String),除了话题名称外,还显示了发布的数据类型:字符串。下一节我们将学习更多不同的ROS数据类型。

我们现在可以启动一个名为listener的订阅节点,它订阅了同样的/chatter话题。运行该节点的命令是:

ros2 run demo_nodes_cpp listener

请确保你在新终端中执行此命令。同时不要混淆可执行文件名和节点名。节点名是在代码中定义的,而可执行文件只是二进制文件。在这个例子中,节点名和可执行文件名相同,但在其他情况下可能不同。

启动talkerlistener节点后,你会看到如下输出:

[INFO] [1726333039.114079927] [listener]: I heard: [Hello World: 1]
[INFO] [1726333040.113948341] [listener]: I heard: [Hello World: 2]
[INFO] [1726333041.113945517] [listener]: I heard: [Hello World: 3]

随后,你可以运行以下命令查看当前运行的节点:

ros2 node list

输出将会是:

/listener
/talker

这表明系统中当前有两个节点正在运行。如果你运行:

ros2 node info /listener

它将显示/listener节点所订阅的话题:

Subscribers:
  /chatter: std_msgs/msg/String
Publishers:

如果想查看节点间的通信情况,可以使用ROS 2的图形界面工具rqt。打开新终端,输入:

rqt

你将看到一个空白窗口,接着依次选择菜单:Plugins | Introspection | Node Graph,就能看到类似下面的输出图示。

image.png

箭头表示talker节点正在发布名为/chatter的话题,而/listener节点正在订阅这个话题。这个可视化图被称为节点图(node graph)或ROS计算图(ROS computation graph)。查看完图后,你可以关闭窗口,或者在终端按Ctrl + C关闭它。

理解ROS计算图的概念非常重要,下面让我们进一步了解。

什么是ROS计算图?

ROS计算图指的是一组点对点(peer-to-peer)网络的ROS进程,这些进程共同处理数据。它展示了系统中各个组件(如节点、话题、服务和动作)之间如何相互关联和交换数据。计算图也是ROS系统架构中最不显眼的层,它揭示了信息的流动方式以及机器人上运行的软件各部分是如何相互作用的。

可视化计算图有助于我们理解节点之间的通信方式,并发现通信中的潜在问题。

假设talkerlistener分别运行在两个终端中,打开第三个终端,执行以下命令查看当前ROS 2的话题:

ros2 topic list

该命令会列出当前正在发布或订阅的话题,输出示例如下:

/chatter

当前只有一个活跃话题:/chatter

另一个有用的命令是:

ros2 topic echo /chatter

该命令订阅/chatter话题,并打印出接收到的数据。echo命令会显示指定话题的消息,效果类似于打印输出。

示例输出如下:

data: 'Hello World: 1'
---
data: 'Hello World: 2'
---

下面的命令会显示给定话题的详细信息,包括发布者和订阅者的数量,以及话题的数据类型:

ros2 topic info /chatter

示例输出:

Type: std_msgs/msg/String
Publisher count: 1
Subscription count: 2

你还可以通过命令创建话题并发布数据,示例如下:

ros2 topic pub /chatter std_msgs/msg/String "data: 'Hello, ROS 2'" -r 1

该命令指定了话题名/chatter,消息类型std_msgs/msg/String,以及发布的字符串数据Hello, ROS 2!-r 1表示以1Hz频率发布。

如果你正在运行talker节点,它已经在发布字符串数据到/chatter话题。如果你在同一话题发布另一条字符串数据,这两个字符串都会出现在/chatter话题中。订阅该话题时,你会看到这两条数据同时被接收。

listener节点的终端中,你会看到如下类似信息:

[INFO] [1726339920.694865075] [listener]: I heard: [Hello, ROS 2]
[INFO] [1726339920.853698114] [listener]: I heard: [Hello World: 1]
[INFO] [1726339921.694919145] [listener]: I heard: [Hello, ROS 2]
[INFO] [1726339921.853678698] [listener]: I heard: [Hello World: 2]

你可以在同一话题上发布不同类型的消息,订阅者只会显示它订阅的消息类型。

例如,下面的命令允许你向/chatter话题发布字符串和整数两种消息类型:

ros2 topic pub /chatter std_msgs/msg/String "data: 'Hello, ROS 2'" -r 1
ros2 topic pub /chatter std_msgs/msg/Int32 "data: '2'" -r 1

在两个终端分别运行上述命令后,再运行listener节点:

ros2 run demo_nodes_cpp listener

你会发现它只订阅到了字符串类型的数据。如果你创建一个专门订阅Int32类型的节点,它将只订阅发布的整数数据。由于存在多种消息类型,ros2 topic echo命令将无法正常工作。建议不要在同一话题上混用不同的消息类型,否则可能会导致运行时错误。

你可以在[36]中阅读关于ros2 topic命令更多的用法介绍。

接下来,我们将讨论ROS消息(ROS messages),它们是ROS话题中使用的数据格式。

什么是ROS消息?

在ROS 2中,消息是节点间通信使用的数据结构。消息通过话题(用于发布/订阅通信)、服务(用于请求/响应)或动作(用于长时间运行的任务)进行传递。这些都是ROS的接口。ROS 2中的消息定义了传输数据的类型和结构。这些消息在.msg文件中定义,每个消息包含多个字段,这些字段代表特定的数据类型(如整数、字符串、数组等)。

ROS 2提供了一系列专门用于预定义消息类型的包,比如std_msgssensor_msgsgeometry_msgs

  • std_msgs消息类型包含字符串、整数等基本数据类型。
  • sensor_msgs消息类型包含用于图像和点云的数据类型。
  • geometry_msgs则包含用于存储姿态、速度等信息的消息。

ROS 2消息内部可能包含多个字段,不一定只是单个字符串,可能是多种数据类型的组合。我们可以使用命令ros2 interface来查看ROS 2消息中包含的字段列表。

例如,查看std_msgs/msg/String消息的字段:

ros2 interface show std_msgs/msg/String

输出为:

string data

这表示要填写std_msgs/msg/String消息,你需要填写它内部的data字段。

你可以在[37]中了解更多关于ROS 2消息的内容。

接下来,我们将通过一些示例探索ROS服务(Services)和动作(Actions)的概念。

什么是ROS服务,Turtlesim又是如何工作的?

在ROS 2中,服务是一种基于调用-响应模型的通信方式。它涉及双向通信:客户端发送请求给服务器,服务器处理请求后返回响应。这个概念与ROS话题不同,话题是单向的,采用发布-订阅模型。尽管服务的底层模式是同步的,但ROS 2中的服务调用可以在节点层面实现异步,这允许客户端在等待响应的同时继续处理其他任务。

我们来看一个服务的使用示例:如果想控制机器人上的某个设备开关,可以编写一个服务。当客户端调用该服务时,服务器会执行开启或关闭操作。ROS服务在机器人中有很多类似的应用场景。

下图展示了一个ROS 2服务服务器与客户端的示意图,其中两个服务客户端与一个ROS服务服务器进行交互。

image.png

通过示例学习有助于我们更好地理解ROS 2服务。为了动手实践ROS 2服务,我们可以使用Turtlesim,这是ROS 2软件包中预装的一个二维模拟器。Turtlesim是一个包含话题、服务、参数和动作的ROS 2节点,我们可以通过它来学习所有ROS 2的核心概念。

你可以使用以下命令启动turtlesim节点:

ros2 run turtlesim turtlesim_node

如果命令执行成功,你会看到如下窗口:

image.png

ROS 2中的Turtlesim是一个图形化的二维模拟器,随ROS 2桌面安装包一同提供。它主要用于学习和实验ROS 2的相关概念。这些工具有助于理解ROS 2通信机制的工作原理。

Turtlesim是一个带有GUI的ROS 2节点。在模拟器中,你会看到中心有一只乌龟。乌龟即机器人,我们可以发送速度指令让它移动。通过ROS 2的话题、服务、参数和动作,我们可以与这个模拟器进行交互。乌龟配备了一个笔,能够通过ROS服务来启用或禁用。当乌龟在二维平面移动时,笔会在表面留下轨迹。笔的颜色也是可以配置的。乌龟还有一个颜色传感器,会发布它从平面检测到的颜色值,这些值通过话题发布。我们可以通过ROS 2服务清除乌龟绘制的图形,甚至调用服务来重置整个turtlesim模拟器。

接下来,我们学习ROS 2服务的工作原理。和ROS 2的话题命令行工具类似,ROS 2也有分析服务的命令行工具。

我们可以先列出当前运行的节点,使用命令:

ros2 node list

你会看到如下节点:

/turtlesim

执行以下命令:

ros2 node info /turtlesim

将获得完整的 /turtlesim 节点信息,输出示例如下:

/turtlesim
  Subscribers:
    /turtle1/cmd_vel: geometry_msgs/msg/Twist
  Publishers:
    /turtle1/color_sensor: turtlesim/msg/Color
    /turtle1/pose: turtlesim/msg/Pose
  Service Servers:
    /clear: std_srvs/srv/Empty
    /kill: turtlesim/srv/Kill
    /reset: std_srvs/srv/Empty
    /spawn: turtlesim/srv/Spawn
    /turtle1/set_pen: turtlesim/srv/SetPen
    /turtle1/teleport_absolute: turtlesim/srv/TeleportAbsolute
    /turtle1/teleport_relative: turtlesim/srv/TeleportRelative
  Service Clients:
  Action Servers:
    /turtle1/rotate_absolute: turtlesim/action/RotateAbsolute
  Action Clients:

在“Service Servers”部分,你会发现turtlesim已经提供了一系列服务,如 /clear/kill/reset/spawn等。你可以从服务客户端调用这些服务。服务名旁边还会显示对应的服务数据类型。服务数据类型通常包含两个部分,一是请求(request),二是响应(response),请求和响应中都可能包含多个字段。如果你想查看某个服务类型的具体内容,可以使用如下命令。

例如,想了解/spawn服务中包含哪些字段,可以执行:

ros2 interface show turtlesim/srv/Spawn

输出示例:

float32 x
float32 y
float32 theta
string name  # Optional.  A unique name will be created and returned if this is empty
---
string name

/spawn服务用于在turtlesim中新建一只乌龟。如果你调用spawn服务并传入新乌龟的位置和名称,服务调用成功后会创建这只乌龟,并返回名称。服务定义中---符号将请求和响应分开。

接下来,我们从命令行调用这个服务。

调用服务的语法为:

ros2 service call <service_name> <service_type> <arguments>

这条命令相当于一个ROS 2服务客户端。

例如,下面的命令在turtlesim中新建一只乌龟:

ros2 service call /spawn turtlesim/srv/Spawn "{x: 2, y: 2, theta: 0.2, name: 'turtle5'}"

执行后,如果服务调用成功,你会看到类似如下的终端信息:

waiting for service to become available...
requester: making request: turtlesim.srv.Spawn_Request(x=2.0, y=2.0, theta=0.2, name='turtle5')
response:
turtlesim.srv.Spawn_Response(name='turtle5')

随后,在模拟器中心的乌龟下方会出现一只新乌龟,如图所示:

image.png

你可以使用以下命令列出节点提供的服务:

ros2 service list -t

其中,-t 表示同时显示服务类型和服务名称。

你很可能会得到如下输出:

/clear [std_srvs/srv/Empty]
/kill [turtlesim/srv/Kill]
/reset [std_srvs/srv/Empty]
/spawn [turtlesim/srv/Spawn]

这里还有几个你可以在turtlesim中尝试的服务调用示例:

  • 以下命令将重置整个turtlesim。执行重置时,所有生成的乌龟都会被清除,所有绘图都会重置,乌龟位置会回到中心:
ros2 service call /reset std_srvs/srv/Empty {}
  • 以下命令清除乌龟所绘制的图形:
ros2 service call /clear std_srvs/srv/Empty {}
  • 如果你想用键盘远程控制乌龟,有对应的节点。运行以下命令启动该节点,可以用键盘方向键控制机器人:
ros2 run turtlesim turtle_teleop_key

这个节点会发布线速度和角速度到话题 /turtle1/cmd_vel,消息类型是 geometry_msgs/msg/Twist。该话题被turtlesim订阅,用以根据速度指令移动机器人。在此场景中,/teleop_turtle 是发布节点,而 turtlesim 是订阅节点。

你可以参考这里了解更多关于ROS 2服务的内容:[38]。

本节我们通过Turtlesim和ROS 2命令行工具,探讨了ROS 2服务的使用,演示了节点间的同步通信及多种服务操作,如生成、重置和控制乌龟。

总结

本章提供了一个ROS 2的实用指南,重点介绍了如何在Ubuntu、Windows和macOS等平台上安装ROS 2 Jazzy,并利用Docker和VirtualBox等虚拟化工具进行部署。介绍了ROS 2的基础概念,包括节点、话题、包和消息,并通过Turtlesim展示了进程间通信。章节还讲解了如何使用ROS 2客户端库进行多语言应用开发,强调了开发环境的搭建、机器人软件的构建及ROS 2概念的实践理解。

下一章将继续介绍ROS 2的动作(actions)和ROS 2包的创建,开始ROS 2编程。