如果不了解ROS 2,推荐先体验一下吧,具体教程:机器人操作系统二(ROS2)- 启程 Departure
ROS 2项目的目标是继承ROS 1的优点并改进不合适的部分。The goal of the ROS 2 project is to leverage what is great about ROS 1 and improve what isn’t.
何为ROS 2?Why ROS 2?
原作者:Brian Gerkey Original Author: Brian Gerkey 我们于2007年11月开始研究ROS。从那时起,已经发生了很多事情,我们相信现在是构建下一代ROS平台的时候了。在本文中,我们将解释具体的原因。We started work on ROS in November 2007. A lot has happened since then and we believe that it is now time to build the next generation ROS platform. In this article we will explain why.
起源 How we got here
ROS开始作为Willow Garage PR2机器人的开发环境。我们的主要目标是提供用户使用PR2进行新颖研发项目所需的软件工具。与此同时,我们知道PR2并不是世界上唯一的,甚至是最重要的机器人,我们希望ROS对其他机器人有用。因此,我们花了很多精力来定义抽象级别(通常通过消息接口),这将允许大部分软件在其他地方重用。ROS began life as the development environment for the Willow Garage PR2 robot. Our primary goal was to provide the software tools that users would need to undertake novel research and development projects with the PR2. At the same time, we knew that the PR2 would not be the only, or even the most important, robot in the world, and we wanted ROS to be useful on other robots. So we put a lot of effort into defining levels of abstraction (usually through message interfaces) that would allow much of the software to be reused elsewhere. 尽管如此,我们还是受到了PR2用例的指导(局限性),其突出特点包括:Still, we were guided by the PR2 use case, the salient characteristics of which included:
- 一个机器人;
- 主机的工作站级计算资源;
- 没有实时要求(或者,以特殊目的方式满足任何实时要求);
- 出色的网络连接(有线或近距离高带宽无线);
- 在研究中的应用,主要是学术界; 和
- 最大的灵活性,没有任何规定或禁止(例如,“我们不包装main()”)。
- a single robot;
- workstation-class computational resources on board;
- no real-time requirements (or, any real-time requirements would be met in a special-purpose manner);
- excellent network connectivity (either wired or close-proximity high-bandwidth wireless);
- applications in research, mostly academia; and
- maximum flexibility, with nothing prescribed or proscribed (e.g., “we don’t wrap your main()”).
可以公平地说,ROS满足了PR2的使用案例,但也因为在各种各样的机器人上变得有用而过度使用。今天我们看到ROS不仅用于PR2和类似于PR2的机器人,还用于各种尺寸的轮式机器人、腿式人形机器人、工业武器、户外地面车辆(包括自动驾驶汽车)、飞行器、地面车辆和更多。It is fair to say that ROS satisfied the PR2 use case, but also overshot by becoming useful on a surprisingly wide variety of robots. Today we see ROS used not only on the PR2 and robots that are similar to the PR2, but also on wheeled robots of all sizes, legged humanoids, industrial arms, outdoor ground vehicles (including self-driving cars), aerial vehicles, surface vehicles, and more. 此外,我们看到ROS的采用超出了我们最初关注的主要学术研究社区之外的领域。基于ROS的产品即将上市,包括制造机器人、农业机器人、商业清洁机器人等。政府机构也在密切关注ROS,以便在其现场系统中使用; 例如,预计NASA将在部署到国际空间站的Robonaut 2上运行ROS。In addition, we are seeing ROS adoption in domains beyond the mostly academic research community that was our initial focus. ROS-based products are coming to market, including manufacturing robots, agricultural robots, commercial cleaning robots, and others. Government agencies are also looking more closely at ROS for use in their fielded systems; e.g., NASA is expected to be running ROS on the Robonaut 2 that is deployed to the International Space Station. 随着ROS的所有这些新用途,平台正以意想不到的方式被拉伸。虽然它保持良好状态,但我们相信通过积极处理新的用例,我们可以更好地满足现在更广泛的ROS社区的需求。With all these new uses of ROS, the platform is being stretched in unexpected ways. While it is holding up well, we believe that we can better meet the needs of a now-broader ROS community by tackling their new use cases head-on.
新应用案例 New use cases
我们对ROS社区的持续性和未来发展特别感兴趣的是如下一些案例,我们在项目开始时没有考虑到这些情况:Of specific interest to us for the ongoing and future growth of the ROS community are the following use cases, which we did not have in mind at the beginning of the project:
- 多个机器人组队:虽然现在可以使用ROS构建多机器人系统,但是没有标准方法,并且它们在ROS的单主机结构之上都是有点黑客的感觉。
- 小型嵌入式平台:我们希望小型计算机(包括“裸机”微控制器)成为ROS环境中的一级参与者,而不是通过设备驱动程序与ROS隔离。
- 实时系统:我们希望直接支持ROS中的实时控制,包括进程间和机器间通信(假设适当的操作系统和/或硬件支持)。
- 非理想网络:我们希望ROS能够在网络连接因丢失和/或延迟而从劣质WiFi到地对空通信链路降级时表现得更好。
- 生产环境:尽管ROS仍然是研究实验室的首选平台,但我们希望确保基于ROS的实验室原型能够发展成适用于实际应用的基于ROS的产品。
- 建立和构建系统的规定模式:虽然我们将保持作为ROS标志的潜在灵活性,但我们希望为生命周期管理和部署静态配置等功能提供清晰的模式和支持工具。
- Teams of multiple robots: while it is possible to build multi-robot systems using ROS today, there is no standard approach, and they are all somewhat of a hack on top of the single-master structure of ROS.
- Small embedded platforms: we want small computers, including “bare-metal” micro controllers, to be first-class participants in the ROS environment, instead of being segregated from ROS by a device driver.
- Real-time systems: we want to support real-time control directly in ROS, including inter-process and inter-machine communication (assuming appropriate operating system and/or hardware support).
- Non-ideal networks: we want ROS to behave as well as is possible when network connectivity degrades due to loss and/or delay, from poor-quality WiFi to ground-to-space communication links.
- Production environments: while it is vital that ROS continue to be the platform of choice in the research lab, we want to ensure that ROS-based lab prototypes can evolve into ROS-based products suitable for use in real-world applications.
- Prescribed patterns for building and structuring systems: while we will maintain the underlying flexibility that is the hallmark of ROS, we want to provide clear patterns and supporting tools for features such as life cycle management and static configurations for deployment.
新技术 New technologies
ROS的核心是一个匿名的发布 – 订阅中间件系统,几乎完全是从头开始构建的。从2007年开始,我们构建了自己的系统,用于发现,消息定义,序列化和传输。在这七年间,已经看到在所有这些领域开发,改进和/或广泛采用与ROS相关的几项新技术,例如:At the core of ROS is an anonymous publish-subscribe middleware system that is built almost entirely from scratch. Starting in 2007, we built our own systems for discovery, message definition, serialization, and transport. The intervening seven years have seen the development, improvement, and/or widespread adoption of several new technologies that are relevant to ROS in all of those areas, such as:
- 零配置;
- 协议缓冲区;
- ZeroMQ(和其他MQ);
- Redis;
- WebSockets; 和
- DDS(数据分发服务)。
- Zeroconf;
- Protocol Buffers;
- ZeroMQ (and the other MQs);
- Redis;
- WebSockets; and
- DDS (Data Distribution Service).
现在可以使用现成的开源库构建类似ROS的中间件系统。我们可以通过多种方式从这种方法中获益匪浅,包括:It is now possible to build a ROS-like middleware system using off-the-shelf open source libraries. We can benefit tremendously from this approach in many ways, including:
- 我们维护的代码更少,特别是非机器人专用代码;
- 我们可以利用那些超出我们自己构建范围的库中的功能;
- 我们可以从其他人对这些库的持续改进中受益; 和
- 当人们问我们ROS是否“准备好迎接黄金时段”时,我们可以指出已经依赖这些库的现有生产系统。
- we maintain less code, especially non-robotics-specific code;
- we can take advantage of features in those libraries that are beyond the scope of what we would build ourselves;
- we can benefit from ongoing improvements that are made by others to those libraries; and
- we can point to existing production systems that already rely on those libraries when people ask us whether ROS is “ready for prime time”.
API更改 API changes
构建ROS 2的另一个原因是利用这个机会来改进我们面向用户的API。今天存在的大量ROS代码与客户端库兼容,早在2009年2月发布的0.4“Mango Tango”。从稳定性的角度来看,这很好,但它也意味着我们仍然存在与几年前制定的API决策一起生活,其中一些我们现在知道并不是最好的。A further reason to build ROS 2 is to take advantage of the opportunity to improve our user-facing APIs. A great deal of the ROS code that exists today is compatible with the client libraries as far back as the 0.4 “Mango Tango” release from February 2009. That’s great from the point of view of stability, but it also implies that we’re still living with API decisions that were made several years ago, some of which we know now to be not the best. 因此,通过ROS 2,我们将设计新的API,尽可能地利用第一代API集成社区的集体体验。因此,虽然关键概念(分布式处理,匿名发布/订阅消息传递,带反馈的RPC(即动作),语言中立性,系统内省性等)将保持不变,但您不应期望ROS 2是API – 与现有的ROS代码兼容。So, with ROS 2, we will design new APIs, incorporating to the best of our ability the collective experience of the community with the first-generation APIs. As a result, while the key concepts (distributed processing, anonymous publish/subscribe messaging, RPC with feedback (i.e., actions), language neutrality, system introspectability, etc.) will remain the same, you should not expect ROS 2 to be API-compatible with existing ROS code. 但不要害怕:将存在允许ROS 2代码与现有ROS代码共存的机制。至少,将有转换中继,它将支持两个系统之间的运行时交互。并且可能会有库填充程序允许现有的ROS代码编译/运行ROS 2库,其行为与今天看到的定性相似。But fear not: there will be mechanisms in place to allow ROS 2 code to coexist with existing ROS code. At the very least, there will be translation relays that will support run-time interactions between the two systems. And it is possible that there will be library shims that will allow existing ROS code to compile/run against ROS 2 libraries, with behavior that is qualitatively similar to what is seen today.
为什么不加强ROS 1 Why not just enhance ROS 1
原则上,上述变化可以集成到现有的核心ROS代码中。例如,新的通讯传输技术可以添加到roscpp
和rospy
。我们考虑了这个选项,并得出结论认为,鉴于实现我们所寻求的利益所需的变化的侵入性(非兼容性改变,对原有系统有一定破坏性),与改变当前ROS系统相关的风险太大,这是许多人所依赖的。我们希望今天存在的ROS 1能够继续工作并且不受ROS 2的开发影响。因此,ROS 2将被构建为一组并行的软件包,可以与ROS 1一起安装并与之交互协作(例如,通过消息桥)。In principle, the changes described above could be integrated into the existing core ROS code. E.g., new transport technologies could be added to roscpp
and rospy
. We considered this option and concluded that, given the intrusive nature of the changes that would be required to achieve the benefits that we are seeking, there is too much risk associated with changing the current ROS system that is relied upon by so many people. We want ROS 1 as it exists today to keep working and be unaffected by the development of ROS 2. So ROS 2 will be built as a parallel set of packages that can be installed alongside and interoperate with ROS 1 (e.g., through message bridges).
推动发展的故事 Stories driving the development
本文搜集了一些驱动ROS特征方向的故事。This article captures some stories which drive the direction of features in ROS. 原作者:Dirk Thomas Original Author: Dirk Thomas 文章列举了一些故事,描述了未来ROS的可能性。该清单并非详尽无遗。
将ROS植入硬件节点 Push ROS into the hardware nodes
ROS 1中的一些设计决策使得难以将硬件设备(例如传感器或致动器)原生地集成到ROS图中。主服务器是ROS图中的中心实体,需要在任何节点之前启动。此外,节点和主节点之间的通信是使用XML-RPC完成的,由于其递归的无界特性,XML-RPC在小资源约束系统/微控制器上实现时具有显着的依赖性。相反,通常使用的驱动程序使用自定义协议在设备和计算机之间进行通信,并在计算机上公开ROS接口。A couple of design decisions in ROS 1 make it difficult to integrate hardware devices (e.g. a sensor or actuator) natively into the ROS graph. The master is a central entity in the ROS graph and needs to be started before any of the nodes. Also the communication between the nodes and the master is done using XML-RPC which poses a significant dependency when being implemented on small resource constraint systems / micro controllers due to its recursive unbounded nature. Instead commonly a driver is being used which uses a custom protocol to communicate between the device and the computer and exposes a ROS interface on the computer. 将来应该可以直接在设备嵌入式系统上实现ROS协议。然后,启用ROS的设备将能够自动发现其他节点并公开ROS接口(由发布者,服务和参数组成)。采用像DDS这样的行业标准以及中间件的分散性质是实现这一目标的重要部分。In the future it should be possible to implement the ROS protocol directly on the devices embedded system. A ROS-enabled device would then be able to discover other nodes automatically and expose a ROS interface (composed of publisher, services and parameters). The adoption of an industry standard like DDS as well as the decentralized nature of the middleware are important pieces to enable this. 这种方法的优点是双重的:The advantage of this approach is twofold:
- 由于不再需要自定义驱动程序,因此正在减少构建新系统的集成工作。
- 通过实现特定的基于ROS的接口,可以容易地替换特定类的设备,而无需花费时间来集成来自例如不同供应商的软件/硬件。
- The integration effort to build a new system is being reduced since no custom drivers are required anymore.
- By implementing a specific ROS-based interface the devices of specific class can easily be substituted without the need to spend time on integrating the software / hardware from e.g. a different vendor.
延迟决定流程布局到部署时间 Delay decision on process layout to deploy time
在ROS 1节点中,通常使用Node
API并实现它们自己的main
功能。只有少数软件包利用Nodelet
API并将其组件编译为共享库。开发人员必须在这两个不同的API之间进行选择,并且从使用一个API转换到另一个需要一些非常重要的工作。In ROS 1 nodes commonly use the Node
API and implement their own main
function. Only a few packages leverage the Nodelet
API and compile their components into shared libraries instead. The developer has to choose between these two different APIs and converting from using one to the other requires some non trivial effort. 在ROS 2中,建议的节点编写方式与nodelets类似。这将使用户能够在部署时决定应该如何启动一组节点。一方面,每个节点都可以在一个单独的进程中启动,以便于单独调试它们。另一方面,可以在单个进程中聚合多个节点,以便从进程内消息传递可能带来的性能优势中受益。In ROS 2 the recommended way of writing nodes will be similar to nodelets. This will enable the user to decide at deploy time how a set of nodes should be started. On the one hand each node could be started in a separate process to ease debugging of them individually. On the other multiple nodes can be aggregate in a single process to benefit from the performance advantages possible by in-process message passing.
确定性启动文件 Deterministic launch
在ROS 1中,启动系统只能启动一组进程。如果流程已完成,它不会提供超出信息的任何反馈。对于复杂系统,这通常是不够的。开发人员以一种等待固定时间或等待自定义标志的方式编写流程是很常见的,这些标志在开始处理数据之前发出“一切”就绪的信号。此外,当启动过程中出现“某事”错误时,专心的开发人员会注意到并手动重启启动过程。显然,在产品上使用软件的用例中,这也是不可行的。In ROS 1 the launch system is only able to start a set of processes. It doesn’t provide any more feedback beyond the information if a process has finished. For complex systems this is often not sufficient. It is quite common that developers write their processes in a way which either waits a fixed amount of time or waits for a custom flag which signals that “everything” is ready before starting to process data. Also when “something” goes wrong during startup the attentive developer will notice and manually restart the launch process. Obviously in use cases where the software is being used on a product this is not feasible either. 目标是使启动系统能够确保确定性结果。为了实现这一点,启动系统需要能够内省(内部核查)启动的进程(通常是ROS节点),并确保它们已成功启动,完成初始化,并已与其他实体建立了所有需要的通信通道。这将需要一个简约的生命周期以及从发射系统内省状态的能力。甚至可以将启动的ROS图与“已知良好”状态进行比较,以确保系统已根据预期启动。The goal is to enable the launch system to ensure a deterministic result. In order to achieve this the launch system needs to be able to introspect the started processes (which are usually ROS nodes) and ensure that they have been started successfully, finished initializing, and have established all require communication channels with other entities. This will require a minimalistic life cycle as well as the ability to introspect the state from the launch system. It would even be possible to compare the started ROS graph with a “known good” state to ensure the system has been started according to the expectations.
内省,编排等 Introspection, Orchestration, and beyond
在复杂系统中,系统及其动态配置的观察变得更加重要。在ROS 1中,节点没有任何特定的状态,只有少数组件(如nodelet管理器)提供了获取信息甚至操作正在运行的系统的接口。In complex systems the observation of the system and its dynamic configuration becomes more important. In ROS 1 nodes do not have any specific state and only a few components (like the nodelet manager) provide an interface to get information or even manipulate the running system. 一旦ROS系统使用上述功能(Nodelet样式节点和可访问的生命周期),内省和编排的能力可以用来构建更复杂的系统。以下是通过全面的内省和调试功能启用的几个示例场景:Once a ROS system is using the above features (Nodelet-style nodes and an accessible life cycle) the abilities for introspection as well as orchestration can be leverages to build more complicated systems. The following a only a few example scenario enabled by the comprehensive introspection and debugging capabilities:
- 可以监视每个节点的状态,并且基于可以触发特定动作的信息。例如,可以向用户发信号通知某些错误条件,或者在回退行为可用的情况下,可以选择它们以提供系统的降级延续。
- 可以在运行时监视系统的资源使用情况。基于可用信息,可以动态地重新配置系统,例如通过启用/禁用特定节点,改变任何类型的参数(例如,帧速率或任何其他阈值)。
- 如果单个进程包含多个节点崩溃,系统应决定不仅重新启动这些节点,还要将它们分成单独的进程,以便在将来的情况下隔离问题。
- 如果单台计算机上的系统负载超过某个阈值,则编排实体可以触发以下步骤:通过生命周期界面暂停所有节点,关闭特定节点,在单独的计算机上生成节点并传递相同的配置/参数,一旦建立了所有通信信道,就恢复所有节点。
- The state of each node can be monitored and based on the information specific actions can be triggered. E.g. certain error conditions can be signaled to the user, or in case fall back behaviors are available they can be selected to provide a degraded continuation of the system.
- The resource usage of the system could be monitored at runtime. Based on the available information the system can be dynamically reconfigured e.g. by enabling / disabling specific nodes, altering any kind of parameter (e.g. frame rate, or any other threshold).
- In case of a single process containing multiple nodes crashing the system should decide to not only restart these nodes but also separate them into individual processes to isolate the problem in future cases.
- If the system load on a single computer exceeds a certain threshold an orchestration entity can trigger the following steps: pause all nodes through the life cycle interface, shutdown a specific node, spawning the node on a separate machine and passing the same configuration / parameters, and once all communication channels have been established resume all nodes.
ROS 1和ROS 2之间的变化 Changes between ROS 1 and ROS 2
本文概述了ROS 2与ROS 1相比所做的更改。This article provides an overview about the changes being made in ROS 2 compared to ROS 1. 原作者:Dirk Thomas Original Author: Dirk Thomas
前言 Preface
每个更改都尽可能简短地描述,但为熟悉ROS 1的读者提供了足够的背景和基本原理。如果有其他外部信息(例如其他文章),则应将其链接到。Each change is described as briefly as possible but giving enough context and rationale for a reader familiar with ROS 1. If further external information is available (e.g. other articles) it should be linked to. 部分描述的功能尚不可用(开发未完成),并标有*
平台和依赖项 Platforms and dependencies
平台 Platforms
ROS 1仅在Ubuntu上进行CI测试。社区积极支持其他Linux版本以及OS X. ROS 1 is only being CI tested on Ubuntu. It is actively supported by the community on other Linux flavors as well as OS X. ROS 2目前正在进行CI测试,并在Ubuntu Xenial,OS X El Capitan以及Windows 10(请参阅ci.ros2.org)上得到支持。ROS 2 is currently being CI tested and supported on Ubuntu Xenial, OS X El Capitan as well as Windows 10 (see ci.ros2.org). 目前,最新版本Ubuntu 18.04。
语言 Languages
C ++标准 C++ standard ROS 1的核心是针对C ++ 03,并且在其API中没有使用C ++ 11特性。ROS 2广泛使用C ++ 11并使用C ++ 14中的一些部分。在未来,ROS 2可能会开始使用C ++ 17,只要它在所有主要平台上都受支持。The core of ROS 1 is targeting C++03 and doesn’t make use of C++11 features in its API. ROS 2 uses C++11 extensively and uses some parts from C++14. In the future ROS 2 might start using C++17 as long as it is supported on all major platforms. Python ROS 1的目标是Python 2.ROS 2至少需要Python 3.5版。ROS 1 is targeting Python 2. ROS 2 requires at least Python version 3.5.
重用现有的中间件 Reusing existing middleware
ROS 1使用自定义序列化格式,自定义传输协议以及自定义中央发现机制。ROS 2具有抽象的中间件接口,通过该接口提供序列化,传输和发现。目前,该接口的所有实现都基于DDS标准。这使得ROS 2能够提供各种服务质量策略,从而改善不同网络上的通信。ROS 1 uses a custom serialization format, a custom transport protocol as well as a custom central discovery mechanism. ROS 2 has an abstract middleware interface, through which serialization, transport, and discovery is being provided. Currently all implementations of this interface are based on the DDS standard. This enables ROS 2 to provide various Quality of Service policies which improve communication over different networks.
编译系统 Build system
有关构建系统的更多信息,请参见ament文章。现在已经更新为colcon。For more information about the build system please see the ament article.
支持CMake旁边的其他构建系统 Support other build systems beside CMake
每个ROS包都是一个CMake项目。在ROS 2中,可以轻松支持其他构建系统。目前,构建工具支持CMake旁边的普通Python包。Every ROS package is a CMake project. In ROS 2 other build systems can be easily supported. For now the build tool supports plain Python packages beside CMake.
Python包 Python packages
在ROS 1中,使用Python代码的包只能使用setup.py文件中可用功能的一小部分,因为setup.py文件正由CMake中的自定义逻辑处理。在ROS 2中,Python包可以使用setup.py文件中的任何内容,例如入口点,因为它们被调用python3 setup.py install
。In ROS 1 a package with Python code can only use a small subset of the features available in setup.py files since the setup.py file is being processed by custom logic from within CMake. In ROS 2 a Python package can use anything in setup.py files, e.g. entry points since they are being invoked with python3 setup.py install
.
环境设置 Environment setup
在ROS 1中,构建工具生成脚本,必须获取这些脚本才能在使用构建的ROS包之前设置环境。只有在使用ROS特定构建工具构建ROS包时,此方法才有效。In ROS 1 the build tool generates scripts which must be sourced in order to setup the environment before being able to use the built ROS packages. This approach only works when the ROS packages are being built with ROS specific build tool. 在ROS 2中,环境设置分为特定于包的脚本和特定于工作空间的脚本。每个包提供必要的脚本,使其在构建后可用。构建工具仅调用特定于工作空间的脚本,然后调用特定于包的脚本。In ROS 2 the environment setup is separated into package-specific scripts and workspace-specific scripts. Each package provides the necessary scripts to make itself usable after being built. The build tool only invokes the workspace-specific scripts which then call the package-specific scripts.
没有非隔离的构建 No non-isolated build
在ROS 1中,可以在单个CMake上下文中构建多个包。虽然这加快了构建步骤,但每个包都需要确保正确定义跨包目标依赖关系。此外,所有包共享相同的命名空间,这会导致目标名称冲突等。In ROS 1 multiple packages can be built in a single CMake context. While this speeds up the build step, every package needs to ensure that cross package target dependencies are defined correctly. Additionally all packages share the same namespace which leads to colliding target names, etc. 在ROS 2中,仅支持隔离的构建,即每个包都是单独构建的。安装空间可以是隔离的,也可以是合并的。In ROS 2 only isolated builds are supported, i.e. every package is built separately on its own. The install spaces can be either isolated or merged.
没有开发空间 No devel space
在ROS 1中,可以在不安装包的情况下构建包。从开发空间结合源空间,系统已经可用。但是每个包都必须主动支持开发空间,例如环境挂钩和CMake代码。In ROS 1 packages can be built without installing them. From the devel space in combination with the source space the system is already usable. But every package has to actively support the devel space, e.g. in environment hooks and CMake code. 在ROS 2中,必须在构建之后安装包,然后才能使用它。In ROS 2 a package must be installed after building it before it can be used. ROS 1中的开发空间的一个原因是使开发人员能够更改文件,例如Python代码或启动文件,并直接使用修改后的代码,而无需重建包。通过使用符号链接替换安装步骤中的复制操作,可以在ROS 2中保留此优势。One reason for the devel space in ROS 1 is to enable the developer to change files, e.g. Python code or launch files, and use the modified code directly without the need to rebuild the package. This benefit is preserved in ROS 2 by optionally replacing copy operations in the install steps with symlinks.
支持catkin_simple用例 Support catkin_simple use case
在ROS 1中,包catkin_simple旨在使编写ROS包的CMake代码更容易。在许多情况下,它没有实现这一目标,这通常是由于对诸如开发空间等支持特征所必需的设计的限制。In ROS 1 the package catkin_simple is aiming to make writing the CMake code of ROS packages easier. In many cases it is not achieving this goal which is often due to restrictions of the design necessary for support features like the devel space. 在ROS 2中,重构了CMake API以支持此用例。In ROS 2 the CMake API was restructured to support this use case.
对没有清单的包的最小支持 Minimal support for packages without manifest
在ROS 1中,构建系统仅考虑具有清单文件的包。在ROS 2中,可以在没有清单文件的文件夹中检测具有受支持的构建系统的软件包。如果程序包遵循常规做法,甚至可能检测到一些丢失的元信息(如依赖项)。In ROS 1 only packages with a manifest file are considered by the build system. In ROS 2 it is possible to detect packages with supported build system in folders without a manifest file. If the package follows common practice it might even be possible to detect some of the missing meta information (like dependencies).
消息,服务 Messages, Services
有关更多信息,请参阅ROS接口定义文章。 For more information please see the ROS interface definition article.
C ++中分隔的命名空间 Separated namespaces in C++
在ROS中,.msg和.srv文件可以具有相同的名称,但生成的代码会发生冲突。服务的请求和响应部分也是如此。 在ROS 2中,生成的代码使用单独的命名空间来保证它是无冲突的。 In ROS 1 .msg and .srv files can have the same name but the generated code collides. The same is the case for the request and response parts of services. In ROS 2 the generated code uses separate namespaces to guarantee it is collision-free.
Python中的名称相同 Same names in Python
生成的消息和服务的Python代码当前在ROS 1和ROS 2中使用相同的模块和类名。因此,它们不能在单个应用程序中导入。如果需要,可以重新考虑该决定。The generated Python code for messages and service is currently using the same module and class names in ROS 1 and ROS 2. Therefore they can not be imported both in a single application. This decision might be revisited if required.
消息定义中的可选默认值 Optional default values in message definitions
在ROS 2中,消息中的原始值现在可以具有默认值,在构造消息时设置。非原始字段的默认值,即字符串数组,嵌套消息,尚不可能*。In ROS 2 primitive values in messages can now have default values, set when the message is constructed. Default values for non-primitive fields, i.e. array of strings, nested messages, are not yet possible.
数组和字符串的可选上限 Optional upper bounds for arrays and strings
这对于计算内存中消息的最大大小是必要的,这允许预先分配具有动态大小的消息。这对于性能和实时用例非常有用。This is necessary in order to calculate the maximum size of a message in memory, which allows for preallocation of messages with a dynamic sizes. This is useful for performance and for use cases like real-time.
统一持续时间和时间类型 Unify duration and time types
在ROS 1中,持续时间和时间类型在客户端库中定义。数据结构的成员名称在C ++(sec,nsec)和Python(secs,nsecs)中是不同的。In ROS 1 the duration and time types are defined in the client libraries. The member names of the data structures are different in C++ (sec, nsec) and Python (secs, nsecs). 在ROS 2中,这些类型被定义为消息,因此在不同语言中是一致的。In ROS 2 these types are defined as messages and therefore are consistent across languages.
从标题消息中删除序列字段 Remove sequence field from Header message
该字段已被弃用了很长时间,并且在ROS 1中未设置一致。 The field has been deprecated for a long time and was not set consistently in ROS 1.
客户端库 Client libraries
跨语言 Across languages
主题命名空间* Topic namespaces 目前,ROS 2不支持主题名称中的名称空间。这主要是由于DDS主题名称中有效字符的限制。一个设计文档描述了如何在未来应该加入。Currently ROS 2 does not support namespaces in topic names. This is mostly due to restrictions of valid characters in DDS topic names. A design document describes how this should be added in the future. 通知 Notifications 在ROS 1中,必须从主站轮询所有关于ROS图的信息。在ROS 2中,将发布更改,例如,如果参数已更改,则通知。In ROS 1 all information about the ROS graph must be polled from the master. In ROS 2 changes will be published instead, e.g. notifications if a parameter has been changed. 具有生命周期的组件 Components with life cycle 在ROS 1中,每个节点通常都有自己的主要功能。在ROS 2中,建议从具有生命周期的组件中继承子类。In ROS 1 every node usually has its own main function. In ROS 2 it is recommended to subclass from a component which has a life cycle. 生命周期可以被像roslaunch这样的工具用来以确定的方式启动由许多组件组成的系统(*)。The life cycle can be used by tools like roslaunch to start a system composed of many components in a deterministic way. 有关更多信息,请参阅节点生命周期文章。For more information please see the node life cycle article. 参数和动态重新配置 Parameters and Dynamic Reconfigure 在ROS 1中,全局参数和节点特定的动态重新配置参数是两个单独的概念。在ROS 2中,正在使用统一的方法。它类似于动态重新配置,名为“全局参数服务器”(*)的节点将接受无条件设置值的请求。在ROS 1中,需要对所有这些信息进行轮询,以便发布ROS 2变更,以通知其他实体。In ROS 1 global parameters and node-specific dynamic reconfigure parameters are two separate concepts. In ROS 2 a unified approach is being used. It is similar to dynamic reconfigure and a node named “global parameter server” (*) will accept requests to set values unconditionally. In ROS 1 all this information needs to be polled for changes in ROS 2 changes will be published to notify other entities. 有关详细信息,请参阅参数设计文章。For more information please see the parameter design article. 行动* Actions ROS 2目前没有行动的概念。它将在未来作为可抢占服务器和反馈发布者的组合添加。ROS 2 currently doesn’t have the concept of actions. It will be added in the future as a combination of a preemptable server and a feedback publisher. 线程模型 Threading model 在ROS 1中,开发人员只能在单线程执行或多线程执行之间进行选择。在ROS 2中,C ++中可以使用更多粒度执行模型(例如跨多个节点),并且可以轻松实现自定义执行程序。对于Python,执行模型尚未实现。In ROS 1 the developer can only choose between single-threaded execution or multi-threaded execution. In ROS 2 more granular execution models are available in C++ (e.g. across multiple nodes) and custom executors can be implemented easily. For Python the execution models haven’t been implemented yet. ROS图 ROS Graph 在ROS 1中,节点和主题只能在启动时重新映射。在ROS 2中,对重映射的支持尚未实现(*)。目标是不仅在启动时间而且在运行时期间启用重映射和别名。In ROS 1 nodes and topics can be remapped at startup time only. In ROS 2 support for remapping hasn’t been implemented yet (*). The goal is to enable remapping as well as aliasing not only during startup time but also during runtime. 在ROS 1中,节点名称是唯一的,并且当启动具有相同名称的新节点时,通过关闭现有节点来强制执行此操作。在ROS 2中,节点名称的唯一性尚未得到强制执行。In ROS 1 node names are unique and this is being enforced by shutting down existing nodes when a new node with the same name is started. In ROS 2 the uniqueness of node names is not yet enforced.
C和C++ C and C++
支持实时 Support for real-time ROS 1不支持编写实时代码,但依赖于Orocos等外部框架。在ROS 2中,当使用适当的RTOS和精心编写的用户代码时,可以编写实时节点。ROS 1 does not support writing real-time code but relies on external frameworks like Orocos. In ROS 2 it will be possible to write real-time nodes when using a proper RTOS and with carefully written user code.
C++ C++
节点与Nodelet Node vs. Nodelet 在ROS 1中,节点和节点的API是不同的,需要开发人员在编程时决定节点到进程的映射。在ROS 2中,建议将每个组件编译到一个共享库中,然后可以在单独的进程中加载或与其他组件(如ROS 1 nodelets)共享相同的进程。这样可以在部署时选择流程布局。In ROS 1 the API’s for nodes and nodelets are different and requires the developer to decide the mapping of nodes to processes at programming time. In ROS 2 it is recommended to compile each component into a shared library which can then be loaded in a separate process or share the same process with other components (like ROS 1 nodelets). This enables to choose the process layout at deploy-time. 每个进程允许多个节点 Allow multiple nodes per process 在ROS 1中,不可能在进程中创建多个节点。这是由于API本身,也是由于内部实现决策。在ROS 2中,可以在进程中创建多个节点。In ROS 1 it is not possible to create more than one node in a process. This is due to the API itself but also because of internal implementation decisions. In ROS 2 it it possible to create multiple nodes in a process.
工具 Tools
roslaunch* roslaunch
在ROS 1中,roslaunch文件以XML定义,功能非常有限。在ROS 2中,启动文件是用Python编写的,它允许使用更复杂的逻辑,如条件等。当前状态仅提供使用多个进程运行测试的最小功能。In ROS 1 roslaunch files are defined in XML with very limited capabilities. In ROS 2 launch file are written in Python which enables to use more complex logic like conditionals etc. The current state only provides minimal functionality to run tests using multiple processes.
资源查找 Resource lookup
在ROS 1中,通过基于ROS_PACKAGE_PATH爬行文件系统来查找各种资源(包,消息,插件等)。当ROS_PACKAGE_PATH中的树很大并且缓存产生不一致状态时,这可能导致性能不佳。 在ROS 2中,资源可以在编译时在索引处注册,然后在运行时有效地查询。有关更多信息,请参阅资源索引的文档。 In ROS 1 various resources (packages, messages, plugins, etc.) are looked up by crawling the file system based on the ROS_PACKAGE_PATH. This can cause poor performance when the trees in the ROS_PACKAGE_PATH are large, and caching produces inconsistent state. In ROS 2 resources can be registered at an index at compile time and then be queried efficiently at runtime. For more information please see the documentation of the resource index.
打包 Packaging
ABI版本* ABI versioning
ROS 1重建所有下游包,因为它假定ABI不兼容。为了避免这种显着的开销,ROS 2软件包应该能够声明其ABI,以避免在可能的情况下重建下游软件包。ROS 1 rebuilds all downstream packages since it assumes ABI incompatibility. To avoid this significant overhead a ROS 2 package should be able to declare its ABI to avoid rebuilding downstream packages whenever possible.
适用于Windows的二进制包* Binary packages for Windows
ROS 1只能在Windows上从源代码构建(它也只适用于少数ROS软件包并且不受支持)。ROS 2将提供基于Chocolatey的二进制包。ROS 1 can only be built from source on Windows (which also only works for a few ROS packages and is not supported). ROS 2 will provide binary package based on Chocolatey.
DDS上的ROS ROS on DDS
本文是在决定使用DDS和RTPS作为ROS 2的基础通信标准之前编写的。有关如何实现ROS 2的详细信息,请参阅核心文档。This article was written at a time before decisions were made to use DDS and RTPS as the underlying communication standards for ROS 2. For details on how ROS 2 has been implemented, see the Core Documentation 本文介绍了如何使用DDS作为ROS的中间件,概述了这种方法的优缺点,并考虑了使用DDS对用户体验和代码API的影响。“ros_dds”原型的结果也被总结并用于探索该问题。This article makes the case for using DDS as the middleware for ROS, outlining the pros and cons of this approach, as well as considering the impact to the user experience and code API that using DDS would have. The results of the “ros_dds” prototype are also summarized and used in the exploration of the issue. 原作者:William Woodall Original Author: William Woodall
为什么要考虑DDS Why Consider DDS
在探索下一代ROS通信系统的选项时,最初的选择是改进ROS 1传输或使用组件库(如ZeroMQ,Protocol Buffers和zeroconf(Bonjour / Avahi))构建新的中间件。但是,除了这些选项之外,两者都涉及我们从部件或临时构建中间件,还考虑了其他端到端中间件。在我们的研究中,一个脱颖而出的中间件是DDS。When exploring options for the next generation communication system of ROS, the initial options were to either improve the ROS 1 transport or build a new middleware using component libraries such as ZeroMQ, Protocol Buffers, and zeroconf (Bonjour/Avahi). However, in addition to those options, both of which involved us building a middleware from parts or scratch, other end-to-end middlewares were considered. During our research, one middleware that stood out was DDS.
端到端中间件 An End-to-End Middleware
使用端到端中间件(如DDS)的好处是,维护的代码要少得多,中间件的行为和确切规范已经被提炼到文档中。除了系统级文档,DDS还推荐了用例和软件API。通过这个具体的规范,第三方可以审查,审计和实现具有不同程度的互操作性的中间件。这是ROS从未有过的东西,除了wiki和参考实现中的一些基本描述。此外,如果要从现有库构建新的中间件,则无论如何都需要创建此类规范。The benefit of using an end-to-end middleware, like DDS, is that there is much less code to maintain and the behavior and exact specifications of the middleware have already been distilled into documentation. In addition to system-level documentation, DDS also has recommended use cases and a software API. With this concrete specification, third parties can review, audit, and implement the middleware with varying degrees of interoperability. This is something that ROS has never had, besides a few basic descriptions in a wiki and a reference implementation. Additionally, this type of specification would need to be created anyway if a new middleware were to be built from existing libraries. 使用端到端中间件的缺点是ROS必须在现有设计中工作。如果设计没有针对相关用例或不灵活,则可能需要解决设计问题。在某种程度上,采用端到端中间件包括采用该中间件的理念和文化,这不应该掉以轻心。The drawback of using an end-to-end middleware is that ROS must work within that existing design. If the design did not target a relevant use case or is not flexible, it might be necessary to work around the design. On some level, adopting an end-to-end middleware includes adopting the philosophy and culture of that middleware, which should not be taken lightly.
什么是DDS What is DDS
DDS提供的发布 – 订阅传输与ROS的发布 – 订阅传输非常相似。DDS使用对象管理组(OMG)定义的“接口描述语言(IDL)” 进行消息定义和序列化。DDS具有请求 – 响应式传输,就像ROS的服务系统一样,在2016年6月的beta 2中称为DDS-RPC。DDS provides a publish-subscribe transport which is very similar to ROS’s publish-subscribe transport. DDS uses the “Interface Description Language (IDL)” as defined by the Object Management Group (OMG) for message definition and serialization. DDS has a request-response style transport, which would be like ROS’s service system, in beta 2 as of June 2016 (called DDS-RPC). DDS提供的默认发现系统是使用DDS的发布 – 订阅传输所必需的,是一种分布式发现系统。这允许任何两个DDS程序进行通信,而无需像ROS主机那样的工具。这使得系统具有更强的容错性和灵活性。但是,不需要使用动态发现机制,因为多个DDS供应商提供静态发现选项。The default discovery system provided by DDS, which is required to use DDS’s publish-subscribe transport, is a distributed discovery system. This allows any two DDS programs to communicate without the need for a tool like the ROS master. This makes the system more fault tolerant and flexible. It is not required to use the dynamic discovery mechanism, however, as multiple DDS vendors provide options for static discovery.
DDS来自哪里 Where did DDS come from
DDS起初是一群具有类似中间件框架的公司,并且当普通客户想要在供应商之间获得更好的互操作性时成为标准。DDS标准由对象管理组创建,它们为我们提供了UML,CORBA,SysML和其他通用软件相关标准。现在,根据您的观点,这可能是积极的认可或负面认可。一方面,你有一个常年的标准委员会,显然对软件工程界有很大的影响,但另一方面你有一个缓慢移动的身体,适应变化很慢,因此可以说并不总是保持了解软件工程的最新趋势。DDS got its start as a group of companies which had similar middleware frameworks and became a standard when common customers wanted to get better interoperability between the vendors. The DDS standard was created by the Object Management Group, which are the same people that brought us UML, CORBA, SysML, and other generic software related standards. Now, depending on your perspective, this may be a positive endorsement or a negative endorsement. On the one hand you have a standards committee which is perennial and clearly has a huge influence on the software engineering community, but on the other hand you have a slow moving body which is slow to adapt to changes and therefore arguably doesn’t always keep up with the latest trends in software engineering. DDS最初是几个类似的中间件,最终变得彼此如此接近,以至于编写标准来统一它们是有意义的。因此,通过这种方式,即使DDS规范由委员会编写,它也通过响应用户的需求而发展到现在的形式。在批准之前,规范的这种类型的有机演变有助于减轻系统在真空中设计的担忧,并且在真实环境中表现不佳。有一些委员会提出了善意和描述良好的规范,没有人想要使用或不满足他们所服务的社区的需求,但DDS似乎并非如此。DDS was originally several similar middlewares which eventually became so close to one another that writing a standard to unify them made sense. So in this way, even though the DDS specification has been written by a committee, it has evolved to its current form by reacting to the needs of its users. This type of organic evolution of the specification before it was ratified helps to alleviate the concern that the system was designed in a vacuum and that it does not perform well in real environments. There are some examples of committees coming up with well intentioned and well described specifications that nobody wants to use or that don’t meet the needs of the community they serve, but this does not appear to be the case for DDS. 还有一个问题是DDS是一个静态规范,它被定义并用于“遗留”系统,但没有保持最新。这种刻板印象来自UML和CORBA之类的恐怖故事,它们也是OMG的产品。相反,DDS似乎有一个主动和有机的规范,在最近的过去已经添加或增加了更多的规范,如websockets,SSL上的安全性,可扩展类型,请求和响应传输,以及一个新的,更多现代C ++ 11样式API规范,用于替换现有C ++接口的核心API。DDS标准体中的这种演变是令人鼓舞的事情,即使身体相对较慢,与软件工程技术趋势相比,它也在不断发展以满足其用户的需求。There is also a concern that DDS is a static specification which was defined and is used in “legacy” systems, but has not kept current. This kind of stereotype comes from horror stories about things like UML and CORBA, which are also products of OMG. On the contrary, DDS seems to have an active and organic specification, which in the recent past has added, or is adding, more specifications for things like websockets, security over SSL, extensible types, request and response transport, and a new, more modern C++11 style API specification for the core API to replace the existing C++ interface. This type of evolution in the standard body for DDS is an encouraging thing to observe, and even though the body is relatively slow, as compared to software engineering technology trends, it is evolving to meet demands of its users.
技术可信度 Technical Credibility
DDS拥有广泛的各种安装清单,这些安装通常都是关键任务。DDS已用于:DDS has an extensive list of varied installations which are typically mission critical. DDS has been used in:
- 战舰
- 像水坝这样的大型公用设施
- 金融系统
- 太空系统
- 飞行系统
- 火车总机系统
- battleships
- large utility installations like dams
- financial systems
- space systems
- flight systems
- train switchboard systems
以及许多其他同样重要和多变的场景。这些成功的使用案例为DDS的设计提供了可靠和灵活的可信度。and many other equally important and varied scenarios. These successful use cases lend credibility to DDS’s design being both reliable and flexible. DDS不仅满足了这些用例的需求,而且在与DDS用户(在这种情况下也是ROS的用户的政府和NASA员工)交谈之后,他们都赞扬了它的可靠性和灵活性。这些用户会注意到DDS的灵活性是以复杂性为代价的。API的复杂性和DDS的配置是ROS需要解决的问题。Not only has DDS met the needs of these use cases, but after talking with users of DDS (in this case government and NASA employees who are also users of ROS), they have all praised its reliability and flexibility. Those same users will note that the flexibility of DDS comes at the cost of complexity. The complexity of the API and configuration of DDS is something that ROS would need to address. DDS电线规范(DDSI-RTPS)非常灵活,允许它用于可靠的高级系统集成以及嵌入式设备上的实时应用。一些DDS供应商为嵌入式系统提供了特殊的DDS实现,其中包含与数据库大小和内存占用相关的规格,数量级为数十或数百千字节。由于DDS默认在UDP上实现,因此它不依赖于可靠的传输或硬件进行通信。这意味着DDS必须重新发明可靠性轮(基本上是TCP加或减一些功能),但作为交换,DDS获得了可移植性和对行为的控制。控制几个可靠性参数,DDS称之为服务质量(QoS),在控制通信行为方面提供了最大的灵活性。例如,如果您担心延迟,对于软实时,您基本上可以将DDS调整为UDP增强器。在另一种情况下,您可能需要一些行为类似于TCP的东西,但需要更长时间容忍长丢失,而使用DDS,可以通过更改QoS参数来控制所有这些事情。The DDS wire specification (DDSI-RTPS) is extremely flexible, allowing it to be used for reliable, high level systems integration as well as real-time applications on embedded devices. Several of the DDS vendors have special implementations of DDS for embedded systems which boast specs related to library size and memory footprint on the scale of tens or hundreds of kilobytes. Since DDS is implemented, by default, on UDP, it does not depend on a reliable transport or hardware for communication. This means that DDS has to reinvent the reliability wheel (basically TCP plus or minus some features), but in exchange DDS gains portability and control over the behavior. Control over several parameters of reliability, what DDS calls Quality of Service (QoS), gives maximum flexibility in controlling the behavior of communication. For example, if you are concerned about latency, like for soft real-time, you can basically tune DDS to be just a UDP blaster. In another scenario you might need something that behaves like TCP, but needs to be more tolerant to long dropouts, and with DDS all of these things can be controlled by changing the QoS parameters. 虽然DDS的默认实现是通过UDP,并且只需要传输的功能级别,但OMG还在其规范的1.2版本中添加了对DDS over TCP的支持。只是简单地看,两家供应商(RTI和ADLINK Technologies)都支持基于TCP的DDS。Though the default implementation of DDS is over UDP, and only requires that level of functionality from the transport, OMG also added support for DDS over TCP in version 1.2 of their specification. Only looking briefly, two of the vendors (RTI and ADLINK Technologies) both support DDS over TCP. 来自RTI的网站(http://community.rti.com/kb/xml-qos-example-using-rti-connext-dds-tcp-transport):From RTI’s website (http://community.rti.com/kb/xml-qos-example-using-rti-connext-dds-tcp-transport):
默认情况下,RTI Connext DDS使用UDPv4和共享内存传输与其他DDS应用程序进行通信。在某些情况下,发现和数据交换可能需要TCP协议。有关RTI TCP传输的更多信息,请参阅“RTI核心库和实用程序用户手册”中标题为“RTI TCP传输”的部分。By default, RTI Connext DDS uses the UDPv4 and Shared Memory transport to communicate with other DDS applications. In some circumstances, the TCP protocol might be needed for discovery and data exchange. For more information on the RTI TCP Transport, please refer to the section in the RTI Core Libraries and Utilities User Manual titled “RTI TCP Transport”.
从ADLINK的网站,他们支持OpenSplice v6.4的TCP:From ADLINK’s website, they support TCP as of OpenSplice v6.4: https://www.adlinktech.com/en/data-distribution-service.aspx
供应商和许可 Vendors and Licensing
OMG与几家公司定义了DDS规范,这些公司现在是主要的DDS供应商。受欢迎的DDS供应商包括:The OMG defined the DDS specification with several companies which are now the main DDS vendors. Popular DDS vendors include:
- RTI
- 凌华科技
- Twin Oaks软件
- RTI
- ADLINK Technologies
- Twin Oaks Software
这些供应商中包含一系列具有不同策略和许可证的参考实现。OMG维护着一个活跃的DDS供应商列表。Amongst these vendors is an array of reference implementations with different strategies and licenses. The OMG maintains an active list of DDS vendors. 除了提供DDS规范API实现的供应商之外,还有软件供应商提供了对DDS有线协议RTPS的更直接访问的实现。例如:In addition to vendors providing implementations of the DDS specification’s API, there are software vendors which provide an implementation with more direct access to the DDS wire protocol, RTPS. For example:
- eProsima
这些以RTPS为中心的实现也很受关注,因为它们的范围可以更小,并且仍然提供了在顶层实现必要的ROS功能所需的功能。These RTPS-centric implementations are also of interest because they can be smaller in scope and still provide the needed functionality for implementing the necessary ROS capabilities on top. RTI的Connext DDS在定制的“社区基础设施”许可下提供,该许可与ROS社区的需求兼容,但需要与社区进一步讨论,以确定其作为ROS的默认DDS供应商的可行性。通过“与ROS社区的需求兼容”,我们的意思是,虽然它不是OSI批准的许可证研究表明,允许ROS保留BSD风格的许可证以及ROS社区中的任何人以源代码或二进制形式重新分发它是足够宽松的。RTI似乎也愿意就许可证进行谈判以满足ROS社区的需求,但ROS社区和RTI之间需要进行一些迭代才能确保这样做。与其他供应商一样,此许可证可用于核心功能集,基本上是基本的DDS API,而其产品的其他部分(如开发和内省工具)是专有的。RTI似乎拥有最大的在线存在和安装基础。RTI’s Connext DDS is available under a custom “Community Infrastructure” License, which is compatible with the ROS community’s needs but requires further discussion with the community in order to determine its viability as the default DDS vendor for ROS. By “compatible with the ROS community’s needs,” we mean that, though it is not an OSI-approved license, research has shown it to be adequately permissive to allow ROS to keep a BSD style license and for anyone in the ROS community to redistribute it in source or binary form. RTI also appears to be willing to negotiate on the license to meet the ROS community’s needs, but it will take some iteration between the ROS community and RTI to make sure this would work. Like the other vendors this license is available for the core set of functionality, basically the basic DDS API, whereas other parts of their product like development and introspection tools are proprietary. RTI seems to have the largest on-line presence and installation base. ADLINK的DDS实现OpenSplice是根据LGPL许可的,LGPL与许多流行的开源库(如glibc,ZeroMQ和Qt)使用的许可证相同。它可以在Github上找到:ADLINK’s DDS implementation, OpenSplice, is licensed under the LGPL, which is the same license used by many popular open source libraries, like glibc, ZeroMQ, and Qt. It is available on Github: https://github.com/ADLINK-IST/opensplice ADLINK的实现带有一个基本的,功能强大的构建系统,并且相当容易打包。OpenSplice似乎是正在使用的第二个DDS实现,但这很难确定。ADLINK’s implementation comes with a basic, functioning build system and was fairly easy to package. OpenSplice appears to be the number two DDS implementation in use, but that is hard to tell for sure. TwinOaks的CoreDX DDS实现仅是专有的,但显然它们专注于能够在嵌入式设备甚至裸机上运行的最小实现。TwinOaks’s CoreDX DDS implementation is proprietary only, but apparently they specialize in minimal implementations which are able to run on embedded devices and even bare metal. eProsima的FastRTPS实现可在GitHub上获得并获得LGPL许可:eProsima’s FastRTPS implementation is available on GitHub and is LGPL licensed: https://github.com/eProsima/Fast-RTPS eProsima Fast RTPS是一种相对较新的轻量级开源RTPS实现。它允许直接访问RTPS协议设置和功能,而其他DDS实现并非总能如此。eProsima的实施还包括最低DDS API,IDL支持和自动代码生成,他们愿意与ROS社区合作以满足他们的需求。eProsima Fast RTPS is a relatively new, lightweight, and open source implementation of RTPS. It allows direct access to the RTPS protocol settings and features, which is not always possible with other DDS implementations. eProsima’s implementation also includes a minimum DDS API, IDL support, and automatic code generation and they are open to working with the ROS community to meet their needs. 鉴于相对较强的LGPL选项以及来自RTI的令人鼓舞但自定义的许可证,似乎依赖甚至分发DDS作为依赖应该是直截了当的。该提案的目标之一是使ROS 2 DDS供应商不可知。所以,举个例子,如果默认实现是Connext,但有人想使用其中一个LGPL选项,比如OpenSplice或FastRTPS,他们只需要重新编译ROS源代码,并翻转一些选项,他们可以使用他们的实现选择。Given the relatively strong LGPL option and the encouraging but custom license from RTI, it seems that depending on and even distributing DDS as a dependency should be straightforward. One of the goals of this proposal would be to make ROS 2 DDS vendor agnostic. So, just as an example, if the default implementation is Connext, but someone wants to use one of the LGPL options like OpenSplice or FastRTPS, they simply need to recompile the ROS source code with some options flipped and they can use the implementation of their choice. 这是因为DDS在其规范中定义了API。研究表明,制作与供应商无关的代码即使不是一点点痛苦也是可能的,因为不同供应商的API几乎相同,但是存在一些细微差别,如返回类型(指针与shared_ptr之类的东西)和头文件组织。This is made possible because of the fact that DDS defines an API in its specification. Research has shown that making code which is vendor agnostic is possible if not a little painful since the APIs of the different vendors is almost identical, but there are minor differences like return types (pointer versus shared_ptr like thing) and header file organization.
思潮与社区 Ethos and Community
DDS来自几十年前的公司,由OMG(一个老派软件工程组织)制定,主要由政府和军方用户使用。因此,DDS社区与ROS社区以及ZeroMQ等类似的现代软件项目看起来大不相同也就不足为奇了。虽然RTI有一个值得尊敬的在线存在,社区成员提出的问题几乎总是由RTI的员工回答,虽然技术上是开源的,但RTI和OpenSplice都没有花时间为Ubuntu或Homebrew或任何其他现代软件包提供软件包。经理。他们没有广泛的用户贡献的wiki或活跃的Github存储库。DDS comes out of a set of companies which are decades old, was laid out by the OMG which is an old-school software engineering organization, and is used largely by government and military users. So it comes as no surprise that the community for DDS looks very different from the ROS community and that of similar modern software projects like ZeroMQ. Though RTI has a respectable on-line presence, the questions asked by community members are almost always answered by an employee of RTI and though technically open source, neither RTI nor OpenSplice has spent time to provide packages for Ubuntu or Homebrew or any other modern package manager. They do not have extensive user-contributed wikis or an active Github repository. 社区之间的这种精神上的这种坚定差异是取决于DDS最受关注的问题之一。与保留TCPROS或使用ZeroMQ等选项不同,没有感觉有大型社区可以依赖DDS。但是,DDS供应商在我们的研究过程中对我们的查询非常敏感,很难说当ROS社区提出问题时是否会继续这样做。This staunch difference in ethos between the communities is one of the most concerning issues with depending on DDS. Unlike options like keeping TCPROS or using ZeroMQ, there isn’t the feeling that there is a large community to fall back on with DDS. However, the DDS vendors have been very responsive to our inquiries during our research and it is hard to say if that will continue when it is the ROS community which brings the questions. 尽管在做出使用DDS的决定时应该考虑到这一点,但它不应该超过DDS提案的技术优势和缺点。Even though this is something which should be taken into consideration when making a decision about using DDS, it should not disproportionately outweigh the technical pros and cons of the DDS proposal.
ROS建立在DDS上 ROS Built on DDS
目标是使DDS成为ROS 2的实现细节。这意味着需要隐藏所有DDS特定的API和消息定义。DDS提供发现,消息定义,消息序列化和发布 – 订阅传输。因此,DDS将提供发现,发布 – 订阅传输,以及至少ROS的基础消息序列化。ROS 2将在DDS之上提供类似于ROS 1的接口,它为大多数ROS用户隐藏了DDS的大部分复杂性,但随后为具有极端用例或需要集成的用户单独提供对底层DDS实现的访问。其他现有的DDS系统。The goal is to make DDS an implementation detail of ROS 2. This means that all DDS specific APIs and message definitions would need to be hidden. DDS provides discovery, message definition, message serialization, and publish-subscribe transport. Therefore, DDS would provide discovery, publish-subscribe transport, and at least the underlying message serialization for ROS. ROS 2 would provide a ROS 1 like interface on top of DDS which hides much of the complexity of DDS for the majority of ROS users, but then separately provides access to the underlying DDS implementation for users that have extreme use cases or need to integrate with other, existing DDS systems.
访问DDS实现需要依赖于通常不使用的附加包。通过这种方式,您可以通过查看包依赖关系来判断包是否已将自身绑定到特定DDS供应商。ROS API基于DDS的目标应该是满足ROS社区的所有常见需求,因为一旦用户使用底层DDS系统,它们将失去DDS供应商之间的可移植性。DDS供应商之间的可移植性并不是为了鼓励人们经常选择不同的供应商,而是让高级用户选择满足其特定要求的DDS实施,以及针对DDS供应商选项变化的面向未来的ROS。将为ROS提供一个推荐的和最佳支持的默认DDS实现。Accessing the DDS implementation would require depending on an additional package which is not normally used. In this way you can tell if a package has tied itself to a particular DDS vendor by just looking at the package dependencies. The goal of the ROS API, which is on top of DDS, should be to meet all the common needs for the ROS community, because once a user taps into the underlying DDS system, they will lose portability between DDS vendors. Portability among DDS vendors is not intended to encourage people to frequently choose different vendors, but rather to enable power users to select the DDS implementation that meets their specific requirements, as well as to future-proof ROS against changes in the DDS vendor options. There will be one recommended and best-supported default DDS implementation for ROS.
发现 Discovery
DDS将完全取代基于ROS主设备的发现系统。ROS需要利用DDS API来获取所有节点列表,所有主题列表以及它们如何连接等信息。访问此信息将隐藏在ROS定义的API后面,从而阻止用户直接调用DDS。DDS would completely replace the ROS master based discovery system. ROS would need to tap into the DDS API to get information like a list of all nodes, a list of all topics, and how they are connected. Accessing this information would be hidden behind a ROS defined API, preventing the users from having to call into DDS directly. DDS发现系统的优点是,默认情况下,它是完全分布式的,因此系统的各个部分之间没有相互通信所需的中心故障点。DDS还允许在其发现系统中使用用户定义的元数据,这将使ROS能够将更高级别的概念搭载到发布 – 订阅上。The advantage of the DDS discovery system is that, by default, it is completely distributed, so there is no central point of failure which is required for parts of the system to communicate with each other. DDS also allows for user defined meta data in their discovery system, which will enable ROS to piggyback higher level concepts onto publish-subscribe.
发布 – 订阅传输 Publish-Subscribe Transport
DDSI-RTPS(DDS-互操作性实时发布订阅)协议将取代ROS的TCPROS和UDPROS有线协议用于发布/订阅。DDS API为ROS 1的典型发布 – 订阅模式提供了更多的参与者。在ROS中,节点的概念最明显地与DDS中的图形参与者并行。图表参与者可以拥有零到多个主题,这些主题与ROS中的主题概念非常相似,但在DDS中表示为单独的代码对象,既不是订阅者也不是发布者。然后,从DDS主题,可以创建DDS订阅者和发布者,但这些用户和发布者再次用于表示DDS中的订阅者和发布者概念,而不是直接从主题读取数据或向主题写入数据。除了主题,订阅者和发布者之外,DDS还有 DataReaders和DataWriters的概念,它们是由订阅者或发布者创建的,然后在用于读取和写入主题数据之前专门用于特定的消息类型。这些额外的抽象层允许DDS具有高级别的配置,因为您可以在发布 – 订阅堆栈的每个级别设置QoS设置,从而提供最高的配置粒度。大多数这些抽象级别不是满足当前ROS需求所必需的。因此,在更简单的类似ROS的接口(节点,发布者和订阅者)下打包通用工作流将是ROS 2隐藏DDS复杂性的一种方式,同时暴露其一些功能。The DDSI-RTPS (DDS-Interoperability Real Time Publish Subscribe) protocol would replace ROS’s TCPROS and UDPROS wire protocols for publish/subscribe. The DDS API provides a few more actors to the typical publish-subscribe pattern of ROS 1. In ROS the concept of a node is most clearly paralleled to a graph participant in DDS. A graph participant can have zero to many topics, which are very similar to the concept of topics in ROS, but are represented as separate code objects in DDS, and is neither a subscriber nor a publisher. Then, from a DDS topic, DDS subscribers and publishers can be created, but again these are used to represent the subscriber and publisher concepts in DDS, and not to directly read data from or write data to the topic. DDS has, in addition to the topics, subscribers, and publishers, the concept of DataReaders and DataWriters which are created with a subscriber or publisher and then specialized to a particular message type before being used to read and write data for a topic. These additional layers of abstraction allow DDS to have a high level of configuration, because you can set QoS settings at each level of the publish-subscribe stack, providing the highest granularity of configuration possible. Most of these levels of abstractions are not necessary to meet the current needs of ROS. Therefore, packaging common workflows under the simpler ROS-like interface (Node, Publisher, and Subscriber) will be one way ROS 2 can hide the complexity of DDS, while exposing some of its features.
高效的运输替代品 Efficient Transport Alternatives
在ROS 1中,从来没有标准的共享内存传输,因为它比localhost TCP环回连接快得多。通过在进程之间仔细地执行零拷贝样式共享内存,可以获得非平凡的性能改进,但是只要使用比ROS 1中的localhost TCP更快的任务,就会使用nodelet。Nodelet允许发布者和订阅者通过将boost::shared_ptr
s 传递给消息来共享数据。此进程内通信几乎肯定比任何进程间通信选项更快,并且与网络发布 – 订阅实现的讨论正交。In ROS 1 there was never a standard shared-memory transport because it is negligibly faster than localhost TCP loop-back connections. It is possible to get non-trivial performance improvements from carefully doing zero-copy style shared-memory between processes, but anytime a task required faster than localhost TCP in ROS 1, nodelets were used. Nodelets allow publishers and subscribers to share data by passing around boost::shared_ptr
s to messages. This intraprocess communication is almost certainly faster than any interprocess communication options and is orthogonal to the discussion of the network publish-subscribe implementation. 在DDS环境中,大多数供应商将以透明的方式使用共享内存优化消息流量(甚至在进程之间),仅在离开本地主机时使用有线协议和UDP套接字。这为DDS提供了相当大的性能提升,而对于ROS 1却没有,因为localhost网络优化在调用时发生send
。对于ROS 1,过程是:将消息序列化为一个大缓冲区,send
在缓冲区上调用一次TCP 。对于DDS,过程更像是:序列化消息,将消息分解为可能的许多UDP数据包,send
多次调用UDP 。通过这种方式,发送许多UDP数据报不会像一个大TCP那样受益于同样的速度send
。因此,许多DDS供应商会将此过程与localhost消息短路,并使用黑板式共享内存机制在进程之间进行有效通信。In the context of DDS, most vendors will optimize message traffic (even between processes) using shared-memory in a transparent way, only using the wire protocol and UDP sockets when leaving the localhost. This provides a considerable performance increase for DDS, whereas it did not for ROS 1, because the localhost networking optimization happens at the call to send
. For ROS 1 the process was: serialize the message into one large buffer, call TCP’s send
on the buffer once. For DDS the process would be more like: serialize the message, break the message into potentially many UDP packets, call UDP’s send
many times. In this way sending many UDP datagrams does not benefit from the same speed up as one large TCP send
. Therefore, many DDS vendors will short circuit this process for localhost messages and use a blackboard style shared-memory mechanism to communicate efficiently between processes. 但是,并非所有DDS供应商在这方面都是相同的,因此ROS不会依赖这种“智能”行为来实现有效的进程内通信。此外,如果保留ROS消息格式(将在下一节中讨论),则无法阻止转换为进程内主题的DDS消息类型。因此,需要为ROS开发自定义的进程内通信系统,该系统永远不会序列化或转换消息,而是使用DDS主题在发布者和订阅者之间传递指针(到共享的进程内存储器)。例如,在ZeroMQ上构建的自定义中间件需要相同的进程内通信机制。However, not all DDS vendors are the same in this respect, so ROS would not rely on this “intelligent” behavior for efficient intraprocess communication. Additionally, if the ROS message format is kept, which is discussed in the next section, it would not be possible to prevent a conversion to the DDS message type for intraprocess topics. Therefore a custom intraprocess communication system would need to be developed for ROS which would never serialize nor convert messages, but instead would pass pointers (to shared in-process memory) between publishers and subscribers using DDS topics. This same intraprocess communication mechanism would be needed for a custom middleware built on ZeroMQ, for example. 这里要点的是,无论中间件的网络/进程间实现如何,都将解决有效的进程内通信。The point to take away here is that efficient intraprocess communication will be addressed regardless of the network/interprocess implementation of the middleware.
消息 Messages
当前的ROS消息定义有很多价值。格式很简单,消息本身已经由机器人社区多年使用而发展。当前ROS代码的大部分语义内容是由这些消息的结构和内容驱动的,因此保留消息的格式和内存表示具有很大的价值。为了实现这一目标,并且为了使DDS成为实现细节,ROS 2应该保留ROS 1,如消息定义和内存中表示。 There is a great deal of value in the current ROS message definitions. The format is simple, and the messages themselves have evolved over years of use by the robotics community. Much of the semantic contents of current ROS code is driven by the structure and contents of these messages, so preserving the format and in-memory representation of the messages has a great deal of value. In order to meet this goal, and in order to make DDS an implementation detail, ROS 2 should preserve the ROS 1 like message definitions and in-memory representation. 因此,.msg
将继续使用ROS 1 文件,并将.msg
文件转换为.idl
文件,以便它们可以与DDS传输一起使用。将为.msg
文件和.idl
文件生成特定于语言的文件,以及用于在ROS和DDS内存中实例之间进行转换的转换函数。ROS 2 API将专门.msg
用于内存中的样式消息对象,并.idl
在发布之前将它们转换为对象。Therefore, the ROS 1 .msg
files would continue to be used and the .msg
files would be converted into .idl
files so that they could be used with the DDS transport. Language specific files would be generated for both the .msg
files and the .idl
files as well as conversion functions for converting between ROS and DDS in-memory instances. The ROS 2 API would work exclusively with the .msg
style message objects in memory and would convert them to .idl
objects before publishing.
首先,为每次发布调用逐字段地将消息转换为另一种对象类型的想法似乎是一个巨大的性能问题,但实验表明,与序列化的成本相比,此副本的成本是微不足道的。转换类型的成本和序列化的成本之间的比率,发现至少一个数量级,对于我们尝试过的每个序列化库都是如此,除了Cap’n Proto没有序列化步骤。因此,如果逐个字段的副本不适用于您的用例,则不会通过网络进行序列化和传输,此时您将不得不利用进程内或零拷贝进程间通信。ROS中的进程内通信不会使用DDS内存中表示,因此除非数据进入线路,否则不会使用此逐字段复制。由于此转换仅与更昂贵的序列化步骤一起调用,因此逐字段复制似乎是通过保留ROS .msg
文件和内存表示提供的可移植性和抽象的合理权衡。At first, the idea of converting a message field-by-field into another object type for each call to publish seems like a huge performance problem, but experimentation has shown that the cost of this copy is insignificant when compared to the cost of serialization. This ratio between the cost of converting types and the cost of serialization, which was found to be at least one order of magnitude, holds true with every serialization library that we tried, except Cap’n Proto which doesn’t have a serialization step. Therefore, if a field-by-field copy will not work for your use case, neither will serializing and transporting over the network, at which point you will have to utilize an intraprocess or zero-copy interprocess communication. The intraprocess communication in ROS would not use the DDS in-memory representation so this field-by-field copy would not be used unless the data is going to the wire. Because this conversion is only invoked in conjunction with a more expensive serialization step, the field-by-field copy seems to be a reasonable trade-off for the portability and abstraction provided by preserving the ROS .msg
files and in-memory representation. 这并不排除.msg
使用默认值和可选字段等内容改进文件格式的选项。但这是一个不同的权衡,可以在以后决定。This does not preclude the option to improve the .msg
file format with things like default values and optional fields. But this is a different trade-off which can be decided later.
服务和行动 Services and Actions
DDS目前没有已批准或实施的请求 – 响应式RPC标准,可用于实现ROS中的服务概念。目前正在考虑在OMG DDS工作组中批准RPC规范,并且一些DDS供应商具有RPC API的草案实现。然而,目前尚不清楚该标准是否适用于行动,但它至少可以支持不可抢占的ROS服务版本。ROS 2可以在发布 – 订阅之上实现服务和操作(由于其可靠的发布 – 订阅QoS设置,这在DDS中更可行),或者它可以在完成服务后使用DDS RPC规范,然后在顶部,再次像在ROS 1中一样。DDS currently does not have a ratified or implemented standard for request-response style RPC which could be used to implement the concept of services in ROS. There is currently an RPC specification being considered for ratification in the OMG DDS working group, and several of the DDS vendors have a draft implementation of the RPC API. It is not clear, however, whether this standard will work for actions, but it could at least support non-preemptable version of ROS services. ROS 2 could either implement services and actions on top of publish-subscribe (this is more feasible in DDS because of their reliable publish-subscribe QoS setting) or it could use the DDS RPC specification once it is finished for services and then build actions on top, again like it is in ROS 1. Either way actions will be a first class citizen in the ROS 2 API and it may be the case that services just become a degenerate case of actions.
语言支持 Language Support
DDS供应商通常至少提供C,C ++和Java实现,因为这些语言的API由DDS规范明确定义。研究发现,没有任何完善的Python DDS版本。因此,ROS 2系统的一个目标是提供一流的,功能完备的C API。这将允许更容易地为其他语言进行绑定,并在客户端库之间实现更一致的行为,因为它们将使用相同的实现。像Python,Ruby和Lisp这样的语言可以将C API包装在一个瘦的,语言惯用的实现中。 DDS vendors typically provide at least C, C++, and Java implementations since APIs for those languages are explicitly defined by the DDS specification. There are not any well established versions of DDS for Python that research has uncovered. Therefore, one goal of the ROS 2 system will be to provide a first-class, feature complete C API. This will allow bindings for other languages to be made more easily and to enable more consistent behavior between client libraries, since they will use the same implementation. Languages like Python, Ruby, and Lisp can wrap the C API in a thin, language idiomatic implementation. ROS的实际实现可以是C语言,使用C DDS API,也可以是C ++,使用DDS C ++ API,然后将C ++实现包装在C API中,用于其他语言。用C ++实现并用C包装是一种常见的模式,例如ZeroMQ就是这样做的。然而,ZeroMQ的作者并没有在他的新图书馆nanomsg中这样做,引用复杂性增加和C ++ stdlib作为依赖的膨胀。由于DDS的C实现通常是纯C,因此可以在ROS C API的整个DDS实现中实现纯C实现。但是,用C语言编写整个系统可能不是第一个目标,并且为了使最小可行产品正常工作,实现可能是用C ++编写的,并且用C语言包装开始,以后可以用C代替C ++。这似乎是必要的。The actual implementation of ROS can either be in C, using the C DDS API, or in C++ using the DDS C++ API and then wrapping the C++ implementation in a C API for other languages. Implementing in C++ and wrapping in C is a common pattern, for example ZeroMQ does exactly this. The author of ZeroMQ, however, did not do this in his new library, nanomsg, citing increased complexity and the bloat of the C++ stdlib as a dependency. Since the C implementation of DDS is typically pure C, it would be possible to have a pure C implementation for the ROS C API all the way down through the DDS implementation. However, writing the entire system in C might not be the first goal, and in the interest of getting a minimal viable product working, the implementation might be in C++ and wrapped in C to begin with and later the C++ can be replaced with C if it seems necessary.
DDS作为依赖 DDS as a Dependency
ROS 2的目标之一是尽可能多地重用代码(“不要重新发明轮子”),同时最大限度地减少依赖项的数量,以提高可移植性并保持构建依赖性列表的精简。这两个目标有时是不一致的,因为它通常是在内部实现某些内容或依赖外部源(依赖项)实现之间的选择。One of the goals of ROS 2 is to reuse as much code as possible (“do not reinvent the wheel”) but also minimize the number of dependencies to improve portability and to keep the build dependency list lean. These two goals are sometimes at odds, since it is often the choice between implementing something internally or relying on an outside source (dependency) for the implementation. 这是DDS实施的亮点,因为正在评估的三个DDS供应商中有两个构建在Linux,OS X,Windows和其他更具异国情调的系统上,没有外部依赖性。C实现仅依赖于系统库,C ++实现仅依赖于C ++ 03编译器,而Java实现仅需要JVM和Java标准库。在Ubuntu和OS X上捆绑为二进制(在原型设计期间),OpenSplice(LGPL)的C,C ++,Java和C#实现的大小不到3兆字节,没有其他依赖关系。就依赖性而言,这使得DDS非常有吸引力,因为它显着简化了ROS的构建和运行依赖性。此外,由于目标是使DDS成为实现细节,因此可能会将其作为传递运行依赖项删除。This is a point where the DDS implementations shine, because two of the three DDS vendors under evaluation build on Linux, OS X, Windows, and other more exotic systems with no external dependencies. The C implementation relies only on the system libraries, the C++ implementations only rely on a C++03 compiler, and the Java implementation only needs a JVM and the Java standard library. Bundled as a binary (during prototyping) on both Ubuntu and OS X, the C, C++, Java, and C# implementations of OpenSplice (LGPL) is less than three megabytes in size and has no other dependencies. As far as dependencies go, this makes DDS very attractive because it significantly simplifies the build and run dependencies for ROS. Additionally, since the goal is to make DDS an implementation detail, it can probably be removed as a transitive run dependency, meaning that it will not even need to be installed on a deployed system.
DDS原型上的ROS The ROS on DDS Prototype
在研究ROS在DDS上的可行性之后,留下了几个问题,包括但不限于:Following the research into the feasibility of ROS on DDS, several questions were left, including but not limited to:
- ROS 1 API和行为能否在DDS之上实现?
- 从ROS MSG消息生成IDL消息并将其与DDS一起使用是否切实可行?
- 打包(作为依赖)DDS实现有多难?
- DDS API规范是否真正实现了DDS供应商的可移植性?
- 配置DDS有多难?
- Can the ROS 1 API and behavior be implemented on top of DDS?
- Is it practical to generate IDL messages from ROS MSG messages and use them with DDS?
- How hard is it to package (as a dependency) DDS implementations?
- Does the DDS API specification actually make DDS vendor portability a reality?
- How difficult is it to configure DDS?
为了回答其中一些问题,在此存储库中创建了一个原型和几个实验:In order to answer some of these questions a prototype and several experiments were created in this repository: https://github.com/osrf/ros_dds 更多问题和一些结果被收集为问题:More questions and some of the results were captured as issues: https://github.com/osrf/ros_dds/issues?labels=task&page=1&state=closed 此存储库中的主要工作是在prototype
文件夹中,并且是使用DDS实现Node,Publisher和Subscriber API的ROS 1:The major piece of work in this repository is in the prototype
folder and is a ROS 1 like implementation of the Node, Publisher, and Subscriber API using DDS: https://github.com/osrf/ros_dds/tree/master/prototype 特别是这个原型包括这些包:Specifically this prototype includes these packages:
- 从
.msg
文件生成DDS IDL :https://github.com/osrf/ros_dds/tree/master/prototype/src/genidl - 为每个生成的IDL文件生成DDS特定的C ++代码:https://github.com/osrf/ros_dds/tree/master/prototype/src/genidlcpp
- 用于C ++的最小ROS客户端库(rclcpp):https://github.com/osrf/ros_dds/tree/master/prototype/src/rclcpp
- pub-sub和服务调用的发言者和监听器:https://github.com/osrf/ros_dds/tree/master/prototype/src/rclcpp_examples
- 的一个分支,
ros_tutorials
在其turtlesim
已被修改,建立了反对rclcpp
库:https://github.com/ros/ros_tutorials/tree/ros_dds/turtlesim。这个分支turtlesim
不是特征完整的(例如,不支持服务和参数),但基础工作,并且它表明从ROS 1转换roscpp
到ROS 2原型所需的变化rclcpp
并不显着。
- Generation of DDS IDLs from
.msg
files: https://github.com/osrf/ros_dds/tree/master/prototype/src/genidl - Generation of DDS specific C++ code for each generated IDL file: https://github.com/osrf/ros_dds/tree/master/prototype/src/genidlcpp
- Minimal ROS Client Library for C++ (rclcpp): https://github.com/osrf/ros_dds/tree/master/prototype/src/rclcpp
- Talker and listener for pub-sub and service calls: https://github.com/osrf/ros_dds/tree/master/prototype/src/rclcpp_examples
- A branch of
ros_tutorials
in whichturtlesim
has been modified to build against therclcpp
library: https://github.com/ros/ros_tutorials/tree/ros_dds/turtlesim. This branch ofturtlesim
is not feature-complete (e.g., services and parameters are not supported), but the basics work, and it demonstrates that the changes required to transition from ROS 1roscpp
to the prototype of ROS 2rclcpp
are not dramatic.
这是一个用于回答问题的快速原型,因此它不代表最终产品或完全抛光。一旦回答了关键问题,某些功能的工作就会停止。This is a rapid prototype which was used to answer questions, so it is not representative of the final product or polished at all. Work on certain features was stopped cold once key questions had been answered. rclcpp_example
包中的示例表明,可以在DDS之上实现基本的ROS API,并获得熟悉的行为。这绝不是一个完整的实现,并不涵盖所有功能,而是用于教育目的,并解决了使用DDS时遇到的大多数疑虑。The examples in the rclcpp_example
package showed that it was possible to implement the basic ROS like API on top of DDS and get familiar behavior. This is by no means a complete implementation and doesn’t cover all of the features, but instead it was for educational purposes and addressed most of the doubts which were held with respect to using DDS. IDL文件的生成被证明有一些关键点,但最终可以解决,并且实现服务等基本内容被证明是易处理的问题。Generation of IDL files proved to have some sticking points, but could ultimately be addressed, and implementing basic things like services proved to be tractable problems. 除了上述基本部分之外,还起草了拉取请求,该请求设法完全隐藏任何公开安装的标头中的DDS符号,rclcpp
并且std_msgs
:In addition to the above basic pieces, a pull request was drafted which managed to completely hide the DDS symbols from any publicly installed headers for rclcpp
and std_msgs
: https://github.com/osrf/ros_dds/pull/17 这个拉取请求最终没有合并,因为它是代码结构的重大重构,同时也取得了其他进展。然而,它的目的在于它表明可以隐藏DDS实施,尽管有关于如何实际实现该目标的讨论空间。This pull request was ultimately not merged because it was a major refactoring of the structure of the code and other progress had been made in the meantime. However, it served its purpose in that it showed that the DDS implementation could be hidden, though there is room for discussion on how to actually achieve that goal.
结论 Conclusion
在与DDS合作并对道德,社区和许可持怀疑态度之后,很难提出任何真正的技术批评。虽然围绕DDS的社区确实与ROS社区或ZeroMQ社区非常不同,但似乎DDS只是ROS可以安全依赖的可靠技术。关于ROS究竟如何利用DDS仍然存在很多问题,但它们在这一点上似乎都是工程练习而不是ROS的潜在交易破坏者。After working with DDS and having a healthy amount of skepticism about the ethos, community, and licensing, it is hard to come up with any real technical criticisms. While it is true that the community surrounding DDS is very different from the ROS community or the ZeroMQ community, it appears that DDS is just solid technology on which ROS could safely depend. There are still many questions about exactly how ROS would utilize DDS, but they all seem like engineering exercises at this point and not potential deal breakers for ROS.
ROS 1的迁移指南 Migration guide from ROS 1
有两种不同类型的包迁移:There are two different kinds of package migrations:
- 将现有软件包的源代码从ROS 1迁移到ROS 2,并希望源代码的重要部分保持相同或至少相似。这方面的一个例子可能是pluginlib,其中源代码在同一个存储库中的不同分支中维护,并且通常可以在必要时在这些分支之间移植补丁。Migrating the source code of an existing package from ROS 1 to ROS 2 with the intend that a significant part of the source code will stay the same or at least similar. An example for this could be pluginlib where the source code is maintained in different branches within the same repository and commonly patches can be ported between those branches when necessary.
- 为ROS 2实现ROS 1包的相同或类似功能,但假设源代码将显着不同。这方面的一个例子可能是ROS 1中的roscpp和ROS 2中的rclcpp,它们是独立的存储库,不共享任何代码。Implementing the same or similar functionality of a ROS 1 package for ROS 2 but with the assumption that the source code will be significantly different. An example for this could be roscpp in ROS 1 and rclcpp in ROS 2 which are separate repositories and don’t share any code.
本文重点介绍前一种情况,并描述了将ROS 1包迁移到ROS 2的高级步骤。它并非旨在成为逐步迁移指令,并且不被视为最终的 “解决方案”。未来的版本旨在使迁移更顺畅,更省力,直到从ROS 1和ROS 2的同一分支维护单个包。This article focuses on the former case and describes the high-level steps to migrate a ROS 1 package to ROS 2. It does not aim to be a step-by-step migration instruction and is not considered the final “solution”. Future versions will aim to make migration smoother and less effort up to the point that maintaining a single package from the same branch for ROS 1 as well as ROS 2.
先决条件 Prerequisites
在能够将ROS 1包迁移到ROS 2之前,必须在ROS 2中提供其所有依赖关系。Before being able to migrate a ROS 1 package to ROS 2 all of its dependencies must be available in ROS 2.
迁移步骤 Migration steps
包清单 Package manifests
ROS 2不支持包规范的格式1,只支持更新的格式版本(2和更高版本)。因此,如果package.xml
文件使用格式1,则必须将文件更新为至少格式2.由于ROS 1支持所有格式,因此可以安全地在ROS 1包中执行该转换。 某些软件包在ROS 2中可能具有不同的名称,因此可能需要相应地更新依赖项。 ROS 2 doesn’t support format 1 of the package specification but only newer format versions (2 and higher). Therefore the package.xml
file must be updated to at least format 2 if it uses format 1. Since ROS 1 supports all formats it is safe to perform that conversion in the ROS 1 package. Some packages might have different names in ROS 2 so the dependencies might need to be updated accordingly.
消息,服务和操作定义 Message, service, and action definitions
消息文件必须.msg
以及必须位于子文件夹中msg
。服务文件必须.srv
以子文件夹结尾并且必须位于子文件夹中srv
。操作文件必须.action
以及必须位于子文件夹中action
。 可能需要更新这些文件以符合ROS接口定义。一些原始类型已被删除,ROS 1中的类型duration
和time
内置类型已被普通消息定义替换,必须在builtin_interfaces包中使用。在ROS 1中,一些命名约定也更为严格。 Message files must end in .msg
and must be located in the subfolder msg
. Service files must end in .srv
and must be located in the subfolder srv
. Actions files must end in .action
and must be located in the subfolder action
. These files might need to be updated to comply with the ROS Interface definition. Some primitive types have been removed and the types duration
and time
which were builtin types in ROS 1 have been replaced with normal message definitions and must be used from the builtin_interfaces package. Also some naming conventions are stricter then in ROS 1. 在你的package.xml
:In your package.xml
:
- 添加
<buildtool_depend>rosidl_default_generators</buildtool_depend>
。 - 添加
<exec_depend>rosidl_default_runtime</exec_depend>
。 - 对于每个相关的消息包,请添加
<depend>message_package</depend>
。
- Add
<buildtool_depend>rosidl_default_generators</buildtool_depend>
. - Add
<exec_depend>rosidl_default_runtime</exec_depend>
. - For each dependent message package, add
<depend>message_package</depend>
.
在你的CMakeLists.txt
:In your CMakeLists.txt
:
- 首先启用C ++ 14
- Start by enabling C++14
set(CMAKE_CXX_STANDARD 14)
- 加
find_package(rosidl_default_generators REQUIRED)
- 对于每一个消息相关包,添加和替换cmake的函数调用与。
find_package(message_package REQUIRED)
generate_messages
rosidl_generate_interfaces
- Add
find_package(rosidl_default_generators REQUIRED)
- For each dependent message package, add
find_package(message_package REQUIRED)
and replace the cmake function call togenerate_messages
withrosidl_generate_interfaces
.
这将替换add_message_files
并add_service_files
列出可以删除的所有消息和服务文件。This will replace add_message_files
and add_service_files
listing of all the message and service files, which can be removed.
构建系统 Build system
在ROS 2构建系统被称为ament和构建工具是colcon。Ament建立在CMake上:ament_cmake
提供CMake功能,使写CMakeLists.txt
文件更容易。The build system in ROS 2 is called ament and the build tool is colcon. Ament is built on CMake: ament_cmake
provides CMake functions to make writing CMakeLists.txt
files easier. 构建工具 Build tool 不是使用catkin_make
,catkin_make_isolated
或者catkin build,
ROS 2使用命令行工具colcon来构建和安装一组包。Instead of using catkin_make
, catkin_make_isolated
or catkin build
ROS 2 uses the command line tool colcon to build and install a set of packages. 纯Python包 Pure Python package 如果ROS 1包仅使用CMake来调用setup.py
文件并且不包含Python代码旁边的任何内容(例如,也没有消息,服务等),则应将其转换为ROS 2中的纯Python包:If the ROS 1 package uses CMake only to invoke the setup.py
file and does not contain anything beside Python code (e.g. also no messages, services, etc.) it should be converted into a pure Python package in ROS 2:
- 在
package.xml
文件中更新或添加构建类型:Update or add the build type in thepackage.xml
file:<export> <build_type>ament_python</build_type> </export>
- 删除该
CMakeLists.txt
文件 Remove theCMakeLists.txt
file - 将
setup.py
文件更新为标准Python安装脚本 Update thesetup.py
file to be a standard Python setup script
ROS 2仅支持Python 3。虽然每个包都可以选择也支持Python 2,但如果它使用其他ROS 2包提供的任何API,它必须使用Python 3调用可执行文件。ROS 2 supports Python 3 only. While each package can choose to also support Python 2 it must invoke executables with Python 3 if it uses any API provided by other ROS 2 packages. 更新的CMakeLists.txt使用ament_cmake Update the CMakeLists.txt to use ament_cmake 应用以下更改来ament_cmake
代替catkin
:Apply the following changes to use ament_cmake
instead of catkin
:
- 在
package.xml
文件导出部分中设置构建类型:Set the build type in thepackage.xml
file export section:<export> <build_type>ament_cmake</build_type> </export>
- 更换
find_package
与调用catkin
和COMPONENTS
使用:Replace thefind_package
invocation withcatkin
and theCOMPONENTS
with:find_package(ament_cmake REQUIRED) find_package(component1 REQUIRED) # ... find_package(componentN REQUIRED)
- 使用以下命令移动和更新
catkin_package
调用:Move and update thecatkin_package
invocation with:ament_package
相反,但在所有目标都已注册后调用。Invokeament_package
instead but after all targets have been registered.- ament_package唯一有效的参数是
CONFIG_EXTRAS
。所有其他参数都由单独的函数覆盖,所有函数都需要在之前 调用ament_package
:The only valid argument for ament_package isCONFIG_EXTRAS
. All other arguments are covered by separate functions which all need to be invoked beforeament_package
:- 而不是之前通过调用。
CATKIN_DEPENDS ...
ament_export_dependencies(...)
Instead of passingCATKIN_DEPENDS ...
callament_export_dependencies(...)
before. - 而不是之前通过调用。
INCLUDE_DIRS ...
ament_export_include_directories(...)
Instead of passingINCLUDE_DIRS ...
callament_export_include_directories(...)
before. - 而不是之前通过调用。
LIBRARIES ...
ament_export_libraries(...)
- Instead of passing
LIBRARIES ...
callament_export_libraries(...)
before.
- Instead of passing
- 而不是之前通过调用。
- TODO文件ament_export_interfaces?TODO document ament_export_interfaces?
- 更换的调用
add_message_files
,add_service_files
并generate_messages
与rosidl_generate_interfaces。Replace the invocation ofadd_message_files
,add_service_files
andgenerate_messages
with rosidl_generate_interfaces.- 第一个参数是
target_name
。如果您只构建一个库,那就是它${PROJECT_NAME}
The first argument is thetarget_name
. If you’re building just one library it’s${PROJECT_NAME}
- 后面是相对于包根的消息文件名列表。Followed by the list of message filenames, relative to the package root.
- 如果要多次使用文件名列表,建议编写一个消息文件列表,并将列表传递给函数以便清楚。If you will be using the list of filenames multiple times, it is recommended to compose a list of message files and pass the list to the function for clarity.
- 最终的多值关键字参数FPR
generate_messages
是DEPENDENCIES
这需要依赖消息包的列表。The final multi-value-keyword argument fprgenerate_messages
isDEPENDENCIES
which requires the list of dependent message packages.rosidl_generate_interfaces(${PROJECT_NAME} ${msg_files} DEPENDENCIES std_msgs )
- 第一个参数是
- 删除任何出现的开发空间。相关的CMake变量
CATKIN_DEVEL_PREFIX
不再存在了。Remove any occurrences of the devel space. Related CMake variables likeCATKIN_DEVEL_PREFIX
do not exist anymore.- 在
CATKIN_DEPENDS
和DEPENDS
参数传递给新的功能ament_export_dependencies。TheCATKIN_DEPENDS
andDEPENDS
arguments are passed to the new function ament_export_dependencies.
- 在
- 更换的调用
add_message_files
,add_service_files
并generate_messages
与rosidl_generate_interfaces。Replace the invocation ofadd_message_files
,add_service_files
andgenerate_messages
with rosidl_generate_interfaces. - 删除任何出现的开发空间。相关的CMake变量
CATKIN_DEVEL_PREFIX
不再存在了。Remove any occurrences of the devel space. Related CMake variables likeCATKIN_DEVEL_PREFIX
do not exist anymore.CATKIN_GLOBAL_BIN_DESTINATION
:bin
CATKIN_GLOBAL_INCLUDE_DESTINATION
:include
CATKIN_GLOBAL_LIB_DESTINATION
:lib
CATKIN_GLOBAL_LIBEXEC_DESTINATION
:lib
CATKIN_GLOBAL_SHARE_DESTINATION
:share
CATKIN_PACKAGE_BIN_DESTINATION
:lib/${PROJECT_NAME}
CATKIN_PACKAGE_INCLUDE_DESTINATION
:include/${PROJECT_NAME}
CATKIN_PACKAGE_LIB_DESTINATION
:lib
CATKIN_PACKAGE_SHARE_DESTINATION
:share/${PROJECT_NAME}
单元测试 Unit tests 如果你正在使用gtest:If you are using gtest:
- 替换
CATKIN_ENABLE_TESTING
为BUILD_TESTING
(直到alpha 5为止AMENT_ENABLE_TESTING
) - 替换
catkin_add_gtest
为ament_add_gtest
- 加入
<test_depend>ament_cmake_gtest</test_depend>
你的package.xml
。
- Replace
CATKIN_ENABLE_TESTING
withBUILD_TESTING
(until alpha 5 this wasAMENT_ENABLE_TESTING
) - Replace
catkin_add_gtest
withament_add_gtest
- Add
<test_depend>ament_cmake_gtest</test_depend>
to yourpackage.xml
.
Linters 在ROS 2中,我们正在努力使用短绒来维护干净的代码。我们的开发人员指南中定义了不同语言的样式。 如果您从头开始创建项目,建议您按照样式指南并通过在下面添加这些行来打开自动linter单元测试if(BUILD_TESTING)
(直到alpha 5为止AMENT_ENABLE_TESTING
)。 In ROS 2 we are working to maintain clean code using linters. The styles for different languages are defined in our Developer Guide. If you are starting a project from scratch it is recommended to follow the style guide and turn on the automatic linter unittests by adding these lines just below if(BUILD_TESTING)
(until alpha 5 this was AMENT_ENABLE_TESTING
).
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
您还需要将以下依赖项添加到您的package.xml
:You will also need to add the following dependencies to your package.xml
:
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
继续catkin
在CMake中使用 Continue to use catkin
in CMake ROS 2使用ament作为构建系统,但为了向后兼容,ROS 2有一个名为package的包catkin
,它提供与ROS 1中的catkin几乎相同的API。为了使用这种向后兼容性API,CMakeLists.txt
必须只更新以catkin_ament_package()
在所有目标之后调用该函数。ROS 2 uses ament as the build system but for backward compatibility ROS 2 has a package called catkin
which provides almost the same API as catkin in ROS 1. In order to use this backward compatibility API the CMakeLists.txt
must only be updated to call the function catkin_ament_package()
after all targets. 注意:这还没有实现,目前只是一个想法。由于与依赖关系相关的更改量,尚未确定此兼容性API是否足以证明该工作的合理性。NOTE: This has not been implemented yet and is only an idea at the moment. Due to the amount of changes related to dependencies it has not yet been decided if this compatibility API is useful enough to justify the effort.
更新源代码 Update source code
消息,服务和动作 Messages, services, and actions ROS 2的消息,服务和行动的命名空间使用一个子名字空间(msg
,srv
,或action
包的名称后,分别)。因此,包括如下:。然后将C ++类型命名为:。#include <my_interfaces/msg/my_message.hpp>
my_interfaces::msg::MyMessage
共享指针类型在消息结构中作为typedef提供:my_interfaces::msg::MyMessage::SharedPtr
以及my_interfaces::msg::MyMessage::ConstSharedPtr
。 有关更多详细信息,请参阅有关生成的C ++接口的文章。 The namespace of ROS 2 messages, services, and actions use a subnamespace (msg
, srv
, or action
, respectively) after the package name. Therefore an include looks like: #include <my_interfaces/msg/my_message.hpp>
. The C++ type is then named: my_interfaces::msg::MyMessage
. Shared pointer types are provided as typedefs within the message structs: my_interfaces::msg::MyMessage::SharedPtr
as well as my_interfaces::msg::MyMessage::ConstSharedPtr
. For more details please see the article about the generated C++ interfaces. 迁移需要包括更改:The migration requires includes to change by:
msg
在包名称和消息数据类型之间插入子文件夹- 将包含的文件名从CamelCase更改为下划线分隔
- 改变
*.h
为*.hpp
- inserting the subfolder
msg
between the package name and message datatype - changing the included filename from CamelCase to underscore separation
- changing from
*.h
to*.hpp
// ROS 1 style is in comments, ROS 2 follows, uncommented.
// # include <geometry_msgs/PointStamped.h>
#include <geometry_msgs/msg/point_stamped.hpp>
// geometry_msgs::PointStamped point_stamped;
geometry_msgs::msg::PointStamped point_stamped;
迁移需要代码将msg
命名空间插入到所有实例中。The migration requires code to insert the msg
namespace into all instances. 使用服务对象 Use of service objects ROS 2中的服务回调没有布尔返回值。建议不要在失败时返回false,而是抛出异常。Service callbacks in ROS 2 do not have boolean return values. Instead of returning false on failures, throwing exceptions is recommended.
// ROS 1 style is in comments, ROS 2 follows, uncommented.
// #include "nav_msgs/GetMap.h"
#include "nav_msgs/srv/get_map.hpp"
// bool service_callback(
// nav_msgs::GetMap::Request & request,
// nav_msgs::GetMap::Response & response)
void service_callback(
const std::shared_ptr<nav_msgs::srv::GetMap::Request> request,
std::shared_ptr<nav_msgs::srv::GetMap::Response> response)
{
// ...
// return true; // or false for failure
}
ros ::Time的用法 Usages of ros::Time TODO没有直接替代ros :: Time但我们希望将来有一个。 我们希望利用跨平台std::chrono
库。 目前用于ros::Time
: TODO There is no direct replacement for ros::Time yet we expect to have one in the future. Under the hood we expect to leverage the cross platform std::chrono
library. Currently for usages of ros::Time
:
- 替换的所有实例
ros::Time
与builtin_interfaces::msg::Time
- 转换的所有实例
nsec
来nanosec
- 将所有单个参数双构造函数转换为裸构造函数和赋值
- Replace all instances of
ros::Time
withbuiltin_interfaces::msg::Time
- Convert all instances of
nsec
tonanosec
- Convert all single argument double constructors to bare constructor plus assignment
构造时,字段值不会初始化为零。您必须确保将所有值设置为零而不是依赖它们。 或者,您可以在等待rclcpp :: Time时临时切换到内部代理数据类型 Field values do not get initialized to zero when constructed. You must make sure to set all values instead of relying on them to be zero. Alternatively you can switch to an internal proxy datatype temporarily while waiting for an rclcpp::Time ros :: Rate的用法 Usages of ros::Rate 有一个等价的类型rclcpp::Rate
对象,基本上是一个替代品ros::Rate
。There is an equivalent type rclcpp::Rate
object which is basically a drop in replacement for ros::Rate
. ROS客户端库 ROS client library
注意:其他要写的 NOTE: Others to be written Boost Boost以前提供的大部分功能已集成到C ++标准库中。因此,我们希望利用新的核心功能,并尽可能避免依赖于增强。Much of the functionality previously provided by Boost has been integrated into the C++ standard library. As such we would like to take advantage of the new core features and avoid the dependency on boost where possible. 共享指针 Shared Pointers 要将共享指针从boost切换到标准C++,请替换以下实例:To switch shared pointers from boost to standard C++ replace instances of:
#include <boost/shared_ptr.hpp>
同#include <memory>
boost::shared_ptr
同std::shared_ptr
#include <boost/shared_ptr.hpp>
with#include <memory>
boost::shared_ptr
withstd::shared_ptr
也可能存在weak_ptr
您想要转换的变体。here may also be variants such as weak_ptr
which you want to convert as well. 此外,建议练习使用using
而不是typedef
。 using
有能力在模板化逻辑中更好地工作。详情请见此处 Also it is recommended practice to use using
instead of typedef
. using
has the ability to work better in templated logic. For details see here 线程/互斥量 Thread/Mutexes ROS代码库中使用的另一个常见的提升部分是互斥体boost::thread
。Another common part of boost used in ROS codebases are mutexes in boost::thread
.
- 替换
boost::mutex::scoped_lock
为std::unique_lock<std::mutex>
- 替换
boost::mutex
为std::mutex
- 替换为
#include <boost/thread/mutex.hpp>
#include <mutex>
- Replace
boost::mutex::scoped_lock
withstd::unique_lock<std::mutex>
- Replace
boost::mutex
withstd::mutex
- Replace
#include <boost/thread/mutex.hpp>
with#include <mutex>
无序地图 Unordered Map 更换:
#include <boost/unordered_map.hpp>
同#include <unordered_map>
boost::unordered_map
同std::unordered_map
Replace:
#include <boost/unordered_map.hpp>
with#include <unordered_map>
boost::unordered_map
withstd::unordered_map
函数 function 更换:
#include <boost/function.hpp>
同#include <functional>
boost::function
同std::function
Replace:
#include <boost/function.hpp>
with#include <functional>
boost::function
withstd::function
启动文件 Launch files
虽然ROS 1中的启动文件是使用.xml文件指定的,但ROS 2使用Python脚本来实现更大的灵活性(请参阅启动包)。While launch files in ROS 1 are specified using .xml files ROS 2 uses Python scripts to enable more flexibility (see launch package).
示例:转换现有的ROS 1包以使用ROS2 Example: Converting an existing ROS 1 package to use ROS 2
假设我们有一个简单的ROS 1包被称为 在一个节点talker
中使用roscpp
,称为talker
。这个包位于catkin工作区,位于~/ros1_talker
。Let’s say that we have simple ROS 1 package called talker
that uses roscpp
in one node, called talker
. This package is in a catkin workspace, located at ~/ros1_talker
.
ROS 1代码 The ROS 1 code
这是我们的catkin工作区的目录布局:Here’s the directory layout of our catkin workspace:
$ cd ~/ros1_talker
$ find .
.
./src
./src/talker
./src/talker/package.xml
./src/talker/CMakeLists.txt
./src/talker/talker.cpp
以下是这三个文件的内容:Here is the content of those three files: src/talker/package.xml
:
<package>
<name>talker</name>
<version>0.0.0</version>
<description>talker</description>
<maintainer email="gerkey@osrfoundation.org">Brian Gerkey</maintainer>
<license>Apache 2.0</license>
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>std_msgs</build_depend>
<run_depend>roscpp</run_depend>
<run_depend>std_msgs</run_depend>
</package>
src/talker/CMakeLists.txt
:
cmake_minimum_required(VERSION 2.8.3)
project(talker)
find_package(catkin REQUIRED COMPONENTS roscpp std_msgs)
catkin_package()
include_directories(${catkin_INCLUDE_DIRS})
add_executable(talker talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
install(TARGETS talker
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
src/talker/talker.cpp
:
#include <sstream>
#include "ros/ros.h"
#include "std_msgs/String.h"
int main(int argc, char **argv)
{
ros::init(argc, argv, "talker");
ros::NodeHandle n;
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
int count = 0;
std_msgs::String msg;
while (ros::ok())
{
std::stringstream ss;
ss << "hello world " << count++;
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
}
return 0;
}
构建ROS 1代码 Building the ROS 1 code 我们使用bash来源环境设置文件(在本例中为Jade),然后我们使用以下命令构建我们的包:catkin_make install
We source an environment setup file (in this case for Jade using bash), then we build our package using catkin_make install
:
. /opt/ros/jade/setup.bash
cd ~/ros1_talker
catkin_make install
运行ROS 1节点 Running the ROS 1 node 如果还没有一个正在运行,我们先启动一个roscore
,首先从我们的catkin
安装树中获取安装文件(系统安装文件 /opt/ros/jade/setup.bash
也在这里工作):If there’s not already one running, we start a roscore
, first sourcing the setup file from our catkin
install tree (the system setup file at /opt/ros/jade/setup.bash
would also work here):
. ~/ros1_talker/install/setup.bash roscore
在另一个shell中,我们catkin
使用安装空间 运行节点rosrun
,再次首先获取安装文件(在这种情况下,它必须是我们工作区中的那个):In another shell, we run the node from the catkin
install space using rosrun
, again sourcing the setup file first (in this case it must be the one from our workspace):
. ~/ros1_talker/install/setup.bash
rosrun talker talker
迁移到ROS2 Migrating to ROS 2
让我们首先创建一个可以工作的新工作区:Let’s start by creating a new workspace in which to work:
mkdir ~/ros2_talker
cd ~/ros2_talker
我们将源树从ROS 1包复制到该工作区,我们可以在其中修改它:We’ll copy the source tree from our ROS 1 package into that workspace, where we can modify it:
mkdir src
cp -a ~/ros1_talker/src/talker src
现在我们将修改节点中的C ++代码。称为ROS 2 C ++库rclcpp
提供的API与提供的API不同roscpp
。两个库之间的概念非常相似,这使得更改变得相当简单。Now we’ll modify the the C++ code in the node. The ROS 2 C++ library, called rclcpp
, provides a different API from that provided by roscpp
. The concepts are very similar between the two libraries, which makes the changes reasonably straightforward to make. 包含的头文件 Included headers 代替ros/ros.h
,这使我们可以访问roscpp
库API,我们需要包含rclcpp/rclcpp.hpp
,这使我们可以访问rclcpp
库API:In place of ros/ros.h
, which gave us access to the roscpp
library API, we need to include rclcpp/rclcpp.hpp
, which gives us access to the rclcpp
library API:
//#include "ros/ros.h"
#include "rclcpp/rclcpp.hpp"
要获取std_msgs/String
消息定义 std_msgs/String.h
,我们需要包括std_msgs/msg/string.hpp
:To get the std_msgs/String
message definition, in place of std_msgs/String.h
, we need to include std_msgs/msg/string.hpp
:
//#include "std_msgs/String.h"
#include "std_msgs/msg/string.hpp"
更改C ++库调用 Changing C++ library calls 我们不是将节点的名称传递给库初始化调用,而是进行初始化,然后将节点名称传递给节点对象的创建(我们可以使用auto
关键字,因为现在我们需要一个C++ 14编译器):Instead of passing the node’s name to the library initialization call, we do the initialization, then pass the node name to the creation of the node object (we can use the auto
keyword because now we’re requiring a C++14 compiler):
// ros::init(argc, argv, "talker");
// ros::NodeHandle n;
rclcpp::init(argc, argv);
auto node = rclcpp::Node::make_shared("talker");
发布者和速率对象的创建看起来非常相似,对命名空间和方法的名称进行了一些更改。对于发布者而言,我们传递服务质量(qos)配置文件而不是整数队列长度参数,这是控制消息传递处理方式的一种更灵活的方式。在这个例子中,我们只传递默认配置文件rmw_qos_profile_default
(它是全局的,因为它是声明的rmw
,用C语言编写,因此没有命名空间)。The creation of the publisher and rate objects looks pretty similar, with some changes to the names of namespace and methods. For the publisher, instead of an integer queue length argument, we pass a quality of service (qos) profile, which is a far more flexible way to controlling how message delivery is handled. In this example, we just pass the default profile rmw_qos_profile_default
(it’s global because it’s declared in rmw
, which is written in C and so doesn’t have namespaces).
// ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
// ros::Rate loop_rate(10);
auto chatter_pub = node->create_publisher<std_msgs::msg::String>("chatter",
rmw_qos_profile_default);
rclcpp::Rate loop_rate(10);
传出消息的创建在命名空间和我们继续创建共享指针的事实上是不同的(将来可能会更改接受const引用的更多发布API):The creation of the outgoing message is different in both the namespace and the fact that we go ahead and create a shared pointer (this may change in the future with more publish API that accepts const references):
// std_msgs::String msg;
auto msg = std::make_shared<std_msgs::msg::String>();
在ros::ok()
,我们称之为rclcpp::ok()
:In place of ros::ok()
, we call rclcpp::ok()
:
// while (ros::ok())
while (rclcpp::ok())
在发布循环中,我们使用->
运算符来访问该data
字段(因为现在msg
是一个共享指针):Inside the publishing loop, we use the ->
operator to access the data
field (because now msg
is a shared pointer):
// msg.data = ss.str();
msg->data = ss.str();
要打印控制台消息,而不是使用ROS_INFO()
,我们使用RCLCPP_INFO()
它和它的各种表兄弟。关键区别在于RCLCPP_INFO()
将Logger对象作为第一个参数。To print a console message, instead of using ROS_INFO()
, we use RCLCPP_INFO()
and its various cousins. The key difference is that RCLCPP_INFO()
takes a Logger object as the first argument.
// ROS_INFO("%s", msg.data.c_str());
RCLCPP_INFO(node->get_logger(), "%s\n", msg->data.c_str());
发布消息非常相似,唯一明显的区别是发布者现在是一个共享指针:Publishing the message is very similar, the only noticeable difference being that the publisher is now a shared pointer:
// chatter_pub.publish(msg);
chatter_pub->publish(msg);
旋转(即,让通信系统处理任何未决的传入/传出消息)是不同的,因为调用现在将节点作为参数:Spinning (i.e., letting the communications system process any pending incoming/outgoing messages) is different in that the call now takes the node as an argument:
// ros::spinOnce();
rclcpp::spin_some(node);
使用速率对象休眠不变。Sleeping using the rate object is unchanged. 把它们放在一起,新的talker.cpp
看起来像这样:Putting it all together, the new talker.cpp
looks like this:
#include <sstream>
// #include "ros/ros.h"
#include "rclcpp/rclcpp.hpp"
// #include "std_msgs/String.h"
#include "std_msgs/msg/string.hpp"
int main(int argc, char **argv)
{
// ros::init(argc, argv, "talker");
// ros::NodeHandle n;
rclcpp::init(argc, argv);
auto node = rclcpp::Node::make_shared("talker");
// ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
// ros::Rate loop_rate(10);
auto chatter_pub = node->create_publisher<std_msgs::msg::String>("chatter", rmw_qos_profile_default);
rclcpp::Rate loop_rate(10);
int count = 0;
// std_msgs::String msg;
auto msg = std::make_shared<std_msgs::msg::String>();
// while (ros::ok())
while (rclcpp::ok())
{
std::stringstream ss;
ss << "hello world " << count++;
// msg.data = ss.str();
msg->data = ss.str();
// ROS_INFO("%s", msg.data.c_str());
RCLCPP_INFO(node->get_logger(), "%s\n", msg->data.c_str());
// chatter_pub.publish(msg);
chatter_pub->publish(msg);
// ros::spinOnce();
rclcpp::spin_some(node);
loop_rate.sleep();
}
return 0;
}
改变package.xml
Changing the package.xml
ROS 2不支持包规范的格式1,只支持更新的格式版本(2和更高版本)。我们首先在package
标记中指定格式版本:ROS 2 doesn’t support format 1 of the package specification but only newer format versions (2 and higher). We start by specifying the format version in the package
tag:
<!-- <package> -->
<package format="2">
ROS 2使用的新版本catkin
,称为ament_cmake
,这是我们在指定 buildtool_depend
标签:ROS 2 uses a newer version of catkin
, called ament_cmake
, which we specify in the buildtool_depend
tag:
<!-- <buildtool_depend>catkin</buildtool_depend> -->
<buildtool_depend>ament_cmake</buildtool_depend>
在我们的构建依赖项中,而不是roscpp
我们使用rclcpp
,它提供了我们使用的C ++ API。我们还依赖于rmw_implementation
,它rmw
引入了允许我们支持多个DDS实现的抽象层的默认实现(我们应该考虑重构/重命名事物,以便可以依赖于一件事,类似于roscpp
):In our build dependencies, instead of roscpp
we use rclcpp
, which provides the C++ API that we use. We additionally depend on rmw_implementation
, which pulls in the default implementation of the rmw
abstraction layer that allows us to support multiple DDS implementations (we should consider restructuring / renaming things so that it’s possible to depend on one thing, analogous to roscpp
):
<!-- <build_depend>roscpp</build_depend> -->
<build_depend>rclcpp</build_depend>
<build_depend>rmw_implementation</build_depend>
我们在运行依赖项中添加相同的内容,并从run_depend
标记更新 到exec_depend
标记(升级到包格式的第2版的一部分):We make the same addition in the run dependencies and also update from the run_depend
tag to the exec_depend
tag (part of the upgrade to version 2 of the package format):
<!-- <run_depend>roscpp</run_depend> -->
<exec_depend>rclcpp</exec_depend>
<exec_depend>rmw_implementation</exec_depend>
<!-- <run_depend>std_msgs</run_depend> -->
<exec_depend>std_msgs</exec_depend>
我们还需要告诉构建工具我们是什么类型的包,以便它知道如何构建我们。因为我们正在使用ament
和CMake,所以我们添加以下行来声明我们的构建类型ament_cmake
:We also need to tell the build tool what kind of package we are, so that it knows how to build us. Because we’re using ament
and CMake, we add the following lines to declare our build type to be ament_cmake
:
<export>
<build_type>ament_cmake</build_type>
</export>
总而言之,我们package.xml
现在看起来像这样:Putting it all together, our package.xml
now looks like this:
<!-- <package> -->
<package format="2">
<name>talker</name>
<version>0.0.0</version>
<description>talker</description>
<maintainer email="gerkey@osrfoundation.org">Brian Gerkey</maintainer>
<license>Apache License 2.0</license>
<!-- <buildtool_depend>catkin</buildtool_depend> -->
<buildtool_depend>ament_cmake</buildtool_depend>
<!-- <build_depend>roscpp</build_depend> -->
<build_depend>rclcpp</build_depend>
<build_depend>rmw_implementation</build_depend>
<build_depend>std_msgs</build_depend>
<!-- <run_depend>roscpp</run_depend> -->
<exec_depend>rclcpp</exec_depend>
<exec_depend>rmw_implementation</exec_depend>
<!-- <run_depend>std_msgs</run_depend> -->
<exec_depend>std_msgs</exec_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
TODO:使用“<depend>“标签显示该文件的更简单版本,该标签由包格式的第2版启用(在“catkin“中也支持,严格来说,与ROS 2正交)。TODO: show simpler version of this file just using the “<depend>“ tag, which is enabled by version 2 of the package format (also supported in “catkin“ so, strictly speaking, orthogonal to ROS 2). 更改CMake代码 Changing the CMake code ROS 2依赖于更高版本的CMake:ROS 2 relies on a higher version of CMake:
#cmake_minimum_required(VERSION 2.8.3)
cmake_minimum_required(VERSION 3.5)
ROS 2依赖于C ++ 14标准。根据您使用的编译器,默认情况下可能不启用对C ++ 14的支持。使用gcc
5.3(这是在Ubuntu Xenial上使用的),我们需要显式启用它,我们通过在文件顶部附近添加这一行来实现:ROS 2 relies on the C++14 standard. Depending on what compiler you’re using, support for C++14 might not be enabled by default. Using gcc
5.3 (which is what is used on Ubuntu Xenial), we need to enable it explicitly, which we do by adding this line near the top of the file:
set(CMAKE_CXX_STANDARD 14)
使用时catkin
,我们通过COMPONENTS
在最初查找catkin
自身时将它们作为参数传递来指定我们要构建的包。有了ament_cmake
,我们单独找到每个包,从ament_cmake
(并添加我们的新依赖项rmw_implementation
)开始:Using catkin
, we specify the packages we want to build against by passing them as COMPONENTS
arguments when initially finding catkin
itself. With ament_cmake
, we find each package individually, starting with ament_cmake
(and adding our new dependency, rmw_implementation
):
#find_package(catkin REQUIRED COMPONENTS roscpp std_msgs)
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rmw_implementation REQUIRED)
find_package(std_msgs REQUIRED)
我们呼吁catkin_package()
为使用我们软件包的其他软件包自动生成CMake配置文件等内容。虽然在指定要构建的目标之前发生了调用,但我们现在在目标ament_package()
之后调用类似的:We call catkin_package()
to auto-generate things like CMake configuration files for other packages that use our package. Whereas that call happens before specifying targets to build, we now call the analogous ament_package()
after the targets:
# catkin_package()
# At the bottom of the file:
ament_package()
类似于我们如何分别找到每个依赖包,而不是将它们作为catkin的一部分,我们还需要单独添加它们的include目录(另请参见ament_target_dependencies()
下文,这是处理依赖包的构建标志的更简洁和更彻底的方法) :Similarly to how we found each dependent package separately, instead of finding them as parts of catkin, we also need to add their include directories separately (see also ament_target_dependencies()
below, which is a more concise and more thorough way of handling dependent packages’ build flags):
#include_directories(${catkin_INCLUDE_DIRS})
include_directories(${rclcpp_INCLUDE_DIRS}
${rmw_implementation_INCLUDE_DIRS}
${std_msgs_INCLUDE_DIRS})
我们这样做是为了链接我们的依赖包的库:We do the same to link against our dependent packages’ libraries:
#target_link_libraries(talker ${catkin_LIBRARIES})
target_link_libraries(talker
${rclcpp_LIBRARIES}
${rmw_implementation_LIBRARIES}
${std_msgs_LIBRARIES})
TODO:解释“ament_target_dependencies()“如何简化上述步骤并且也更好(也处理“* _DEFINITIONS“,做特定于目标的包含目录等)。TODO: explain how “ament_target_dependencies()“ simplifies the above steps and is also better (also handling “*_DEFINITIONS“, doing target-specific include directories, etc.). 对于安装,catkin
定义变量如CATKIN_PACKAGE_BIN_DESTINATION
。有了ament_cmake
,我们只提供一个相对于安装根的路径,就像bin
可执行文件一样(这部分是因为我们还没有相应的rosrun
):For installation, catkin
defines variables like CATKIN_PACKAGE_BIN_DESTINATION
. With ament_cmake
, we just give a path relative to the installation root, like bin
for executables (this is in part because we don’t yet have an equivalent of rosrun
):
#install(TARGETS talker
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
install(TARGETS talker RUNTIME DESTINATION bin)
把它们放在一起,新的CMakeLists.txt
看起来像这样:Putting it all together, the new CMakeLists.txt
looks like this:
#cmake_minimum_required(VERSION 2.8.3)
cmake_minimum_required(VERSION 3.5)
project(talker)
set(CMAKE_CXX_STANDARD 14)
#find_package(catkin REQUIRED COMPONENTS roscpp std_msgs)
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rmw_implementation REQUIRED)
find_package(std_msgs REQUIRED)
#catkin_package()
#include_directories(${catkin_INCLUDE_DIRS})
include_directories(${rclcpp_INCLUDE_DIRS}
${rmw_implementation_INCLUDE_DIRS}
${std_msgs_INCLUDE_DIRS})
add_executable(talker talker.cpp)
#target_link_libraries(talker ${catkin_LIBRARIES})
target_link_libraries(talker
${rclcpp_LIBRARIES}
${rmw_implementation_LIBRARIES}
${std_msgs_LIBRARIES})
#install(TARGETS talker
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
install(TARGETS talker RUNTIME DESTINATION bin)
ament_package()
TODO:用“ament_auto“显示它的样子。TODO: Show what this would look like with “ament_auto“. 构建ROS 2代码 Building the ROS 2 code 我们提供了一个环境设置文件(在这种情况下是通过遵循ROS 2安装教程生成的文件,该教程是内置的~/ros2_ws
,然后我们使用以下命令构建我们的包:colcon build
We source an environment setup file (in this case the one generated by following the ROS 2 installation tutorial, which builds in ~/ros2_ws
, then we build our package using colcon build
:
. ~/ros2_ws/install/setup.bash
cd ~/ros2_talker
colcon build
运行ROS 2节点 Running the ROS 2 node 因为我们在从安装树获取安装文件之后安装了talker
可执行bin
文件,所以我们可以直接通过名称调用它(也就是说,还没有相应的ROS 2 rosrun
):Because we installed the talker
executable into bin
, after sourcing the setup file, from our install tree, we can invoke it by name directly (also, there is not yet a ROS 2 equivalent for rosrun
):
. ~/ros2_ws/install/setup.bash
talker
许可 Licensing
在ROS 2中,我们推荐的许可证是Apache 2.0许可证 在ROS 1中,我们推荐的许可证是3条款BSD许可证 对于任何新项目,我们建议使用Apache 2.0许可证,无论是ROS 1还是ROS 2。 但是,当将代码从ROS 1迁移到ROS 2时,我们不能简单地更改许可证,必须为任何预先存在的贡献保留现有许可证。 为此目的,如果正在迁移软件包,我们建议保留现有许可证,并继续根据现有OSI许可证为该软件包做出贡献,我们希望这些许可证是核心元素的BSD许可证。 这将使事情清晰易懂。 In ROS 2 our recommended license is the Apache 2.0 License In ROS 1 our recommended license was the 3-Clause BSD License For any new project we recommend using the Apache 2.0 License, whether ROS 1 or ROS 2. However when migrating code from ROS 1 to ROS 2 we cannot simply change the license, the existing license must be preserved for any preexisting contributions. To that end if a package is being migrated we recommend keeping the existing license and continuing to contributing to that package under the existing OSI license, which we expect to be the BSD license for core elements. This will keep things clear and easy to understand.
更改许可证 Changing the License
可以更改许可证,但是您需要联系所有贡献者并获得许可。对于大多数包装来说,这可能是一项重大的努力而不值得考虑。如果包作为一小组贡献者那么这可能是可行的。 It is possible to change the license, however you will need to contact all the contributors and get permission. For most packages this is likely to be a significant effort and not worth considering. If the package as a small set of contributors then this may be feasible.