这里对ROS1到ROS2的变迁,每个更改都尽可能简短地描述,但为熟悉 ROS 1 的读者提供足够的背景和基本原理。如果有更多的外部信息可用(例如其他文章),则应将其链接到。
ROS 1 仅在 Ubuntu 上进行 CI 测试。它得到了社区对其他 Linux 风格以及 OS X 的积极支持。
ROS 2 目前正在 Ubuntu Xenial、OS X El Capitan 以及 Windows 10 上进行 CI 测试和支持(参见 ci.ros2.org)。
1)C++ 标准
ROS 1 的核心是针对 C++03,并且在其 API 中没有使用 C++11 的特性。 ROS 2 广泛使用 C++11,并使用了 C++14 的一些部分。未来 ROS 2 可能会开始使用 C++17,只要它在所有主要平台上都受支持。
2)Python
ROS 1 的目标是 Python 2。ROS 2 至少需要 Python 3.5 版本。
ROS 1 使用自定义序列化格式、自定义传输协议以及自定义中央发现机制。 ROS 2 有一个抽象的中间件接口,通过它提供序列化、传输和发现。目前该接口的所有实现都基于 DDS 标准。这使 ROS 2 能够提供各种服务质量策略,从而改善不同网络上的通信。
有关构建系统的更多信息,请参阅 Ament 文章。
每个 ROS 包都是一个 CMake 项目。在 ROS 2 中,可以轻松支持其他构建系统。目前,构建工具支持 CMake 之外的纯 Python 包。
在 ROS 1 中,带有 Python 代码的包只能使用 setup.py 文件中可用功能的一小部分,因为 setup.py 文件正在由 CMake 中的自定义逻辑处理。在 ROS 2 中,Python 包可以使用 setup.py 文件中的任何内容,例如入口点,因为它们是通过 python3 setup.py install 调用的。
在 ROS 1 中,构建工具会生成脚本,这些脚本必须获取源才能设置环境,然后才能使用构建的 ROS 包。此方法仅在使用 ROS 特定构建工具构建 ROS 包时有效。
在 ROS 2 中,环境设置分为特定于包的脚本和特定于工作空间的脚本。每个包都提供了必要的脚本,以使自己在构建后可用。构建工具仅调用特定于工作空间的脚本,然后调用特定于包的脚本。
在 ROS 1 中,可以在单个 CMake 上下文中构建多个包。虽然这加快了构建步骤,但每个包都需要确保正确定义跨包目标依赖项。此外,所有包共享相同的命名空间,这会导致目标名称冲突等。
在 ROS 2 中,仅支持独立构建,即每个包都是独立构建的。安装空间可以被隔离或合并。
在 ROS 1 中,无需安装即可构建包。从开发空间与源空间相结合,系统已经可用。但是每个包都必须积极支持开发空间,例如在环境挂钩和 CMake 代码中。
在 ROS 2 中,必须先安装一个包,然后才能使用它。
ROS 1 中开发空间的一个原因是使开发人员能够更改文件,例如Python 代码或启动文件,直接使用修改后的代码,无需重新编译包。通过可选地将安装步骤中的复制操作替换为符号链接,这一优势在 ROS 2 中得以保留。
在 ROS 1 中,包 catkin_simple 旨在使编写 ROS 包的 CMake 代码更容易。在许多情况下,它没有实现这一目标,这通常是由于开发空间等支持功能所必需的设计限制。
在 ROS 2 中,CMake API 进行了重组以支持此用例。
在 ROS 1 中,构建系统只考虑带有清单文件的包。在 ROS 2 中,可以在没有清单文件的文件夹中检测具有支持的构建系统的包。如果包遵循惯例,甚至可能检测到一些缺失的元信息(如依赖项)。
有关更多信息,请参阅 ROS 接口定义文章。
在 ROS 1 中,.msg 和 .srv 文件可以具有相同的名称,但生成的代码会发生冲突。服务的请求和响应部分也是如此。
在 ROS 2 中,生成的代码使用单独的命名空间来保证它是无冲突的。
为消息和服务生成的 Python 代码目前在 ROS 1 和 ROS 2 中使用相同的模块和类名。因此,它们不能在单个应用程序中同时导入。如果需要,可能会重新考虑该决定。
在 ROS 2 中,消息中的原始值现在可以具有默认值,在构造消息时设置。非原始字段的默认值,即字符串数组、嵌套消息,尚不可能 (⏳)。
这是计算内存中消息的最大大小所必需的,这允许预先分配具有动态大小的消息。这对于性能和实时等用例很有用。
在 ROS 1 中,持续时间和时间类型在客户端库中定义。数据结构的成员名称在 C++ (sec, nsec) 和 Python (secs, nsecs) 中是不同的。
在 ROS 2 中,这些类型被定义为消息,因此跨语言是一致的。
该字段已被弃用很长时间,并且在 ROS 1 中的设置不一致。
1)主题命名空间 (⏳)
目前 ROS 2 不支持主题名称中的命名空间。这主要是由于 DDS 主题名称中有效字符的限制。设计文档描述了将来应该如何添加它。
2)通知
在 ROS 1 中,所有关于 ROS 图的信息都必须从 master 轮询。在 ROS 2 中,将改为发布更改,例如如果参数已更改,则通知。
3)具有生命周期的组件
在 ROS 1 中,每个节点通常都有自己的主要功能。在 ROS 2 中,建议从具有生命周期的组件子类化。
roslaunch 之类的工具可以使用生命周期来确定性地启动由许多组件组成的系统(⏳)。
4)参数和动态重新配置
在 ROS 1 中,全局参数和节点特定的动态重新配置参数是两个独立的概念。在 ROS 2 中使用了统一的方法。它类似于动态重新配置,名为“全局参数服务器”(⏳)的节点将无条件接受设置值的请求。在 ROS 1 中,所有这些信息都需要轮询,以便 ROS 2 中的更改将被发布以通知其他实体。
5)行动
ROS 2 目前没有动作的概念。将来它将作为可抢占服务器和反馈发布者的组合添加。
6)线程模型
在 ROS 1 中,开发人员只能在单线程执行或多线程执行之间进行选择。
在 ROS 2 中,C++ 中提供了更精细的执行模型(例如跨多个节点),并且可以轻松实现自定义执行器。
7)ROS图
在 ROS 1 中,节点和主题只能在启动时重新映射。在 ROS 2 中,对重映射的支持尚未实现 (⏳)。目标是不仅在启动期间而且在运行时启用重新映射和别名。
在 ROS 1 中,节点名称是唯一的,这是通过在启动具有相同名称的新节点时关闭现有节点来强制执行的。在 ROS 2 中,节点名称的唯一性尚未强制执行。
ROS 1 不支持编写实时代码,而是依赖于像 Orocos 这样的外部框架。
在 ROS 2 中,当使用适当的 RTOS 并仔细编写用户代码时,可以编写实时节点。
1)节点与 Nodelet
在 ROS 1 中,节点和 nodelet 的 API 是不同的,需要开发人员在编程时决定节点到进程的映射。在 ROS 2 中,建议将每个组件编译成一个共享库,然后可以在单独的进程中加载该库或与其他组件(如 ROS 1 节点小程序)共享相同的进程。这允许在部署时选择流程布局。
2)每个进程允许多个节点
在 ROS 1 中,不能在一个进程中创建多个节点。这是由于 API 本身,也由于内部实现决策。在 ROS 2 中,可以在一个进程中创建多个节点。
在 ROS 1 中,roslaunch 文件以 XML 定义,功能非常有限。
在 ROS 2 中,启动文件是用 Python 编写的,可以使用更复杂的逻辑,如条件等。当前状态仅提供使用多个进程运行测试的最少功能。
在 ROS 1 中,通过基于 ROS_PACKAGE_PATH 爬取文件系统来查找各种资源(包、消息、插件等)。当 ROS_PACKAGE_PATH 中的树很大时,这可能会导致性能下降,并且缓存会产生不一致的状态。
在 ROS 2 中,资源可以在编译时注册到索引中,然后在运行时高效查询。有关更多信息,请参阅资源索引的文档。
ROS 1 重新构建所有下游包,因为它假定 ABI 不兼容。为了避免这种显着的开销,ROS 2 包应该能够声明其 ABI,以避免尽可能地重建下游包。
ROS 1 只能在 Windows 上从源代码构建(这也仅适用于少数 ROS 包,不受支持)。 ROS 2 将提供基于 Chocolatey 的二进制包。