关于客户端库
客户端库(client library)是让用户编写 ROS 2 代码的 API。通过客户端库,用户可以访问 ROS 2 的各种概念,例如节点(node)、话题(topic)、服务(service)等。客户端库提供多种编程语言版本,方便用户选择最适合自己应用的语言。例如,你可能更倾向于用 Python 编写可视化工具,因为原型迭代更快;而对于系统中关注性能的部分,用 C++ 实现节点可能更合适。
使用不同客户端库编写的节点能够相互收发消息,因为所有客户端库都实现了代码生成器,使用户能够在各自的语言中使用 ROS 2 接口文件。
除了语言特定的通信工具之外,客户端库还向用户暴露了使 ROS 成为”ROS”的核心功能。以下是通过客户端库通常可以访问的功能:
- 名称和命名空间(namespace)
- 时间(实时或仿真)
- 参数(parameter)
- 控制台日志(logging)
- 线程模型
- 进程内通信
支持的客户端库
Section titled “支持的客户端库”C++ 客户端库(rclcpp)和 Python 客户端库(rclpy)都是利用 rcl 中通用功能的客户端库。
rclcpp 包
Section titled “rclcpp 包”ROS C++ 客户端库(rclcpp)是面向用户的 C++ 惯用接口,提供了所有 ROS 客户端功能,如创建节点、发布者(publisher)和订阅者(subscriber)。rclcpp 构建在 rcl 和 rosidl API 之上,设计用于与 rosidl_generator_cpp 生成的 C++ 消息配合使用。
rclcpp 充分利用了 C++ 的惯用写法和 C++20 特性,使接口尽可能易于使用;同时由于复用了 rcl 中的实现,它能够与使用 rcl API 的其他客户端库保持行为一致。
rclcpp 仓库位于 GitHub 上的 ros2/rclcpp,包含 rclcpp 包。生成的 API 文档位于 rclcpp。
rclpy 包
Section titled “rclpy 包”ROS Python 客户端库(rclpy)是 C++ 客户端库的 Python 对应版本。与 C++ 客户端库一样,rclpy 也构建在 rcl C API 之上。该接口提供了惯用的 Python 开发体验,使用了原生 Python 类型和模式,如列表和上下文对象。通过在实现中使用 rcl API,它在功能对等性和行为方面与其他客户端库保持一致。除了提供围绕 rcl API 的 Python 惯用绑定和每种消息的 Python 类之外,Python 客户端库还负责管理执行模型,使用 threading.Thread 或类似方式运行 rcl API 中的函数。
与 C++ 一样,它会为用户使用的每种 ROS 消息生成定制的 Python 代码。但与 C++ 不同的是,它最终会将原生 Python 消息对象转换为消息的 C 版本。所有操作都在消息的 Python 版本上进行,直到需要将消息传入 rcl 层时,消息才会被转换为纯 C 版本以传入 rcl C API。在同一进程中的发布者和订阅者之间通信时,会尽可能避免这种转换,以减少 Python 与 C 之间的转换开销。
rclpy 仓库位于 GitHub 上的 ros2/rclpy,包含 rclpy 包。生成的 API 文档位于 rclpy。
虽然 C++ 和 Python 客户端库由 ROS 2 核心团队维护,但 ROS 2 社区的成员还维护着其他客户端库:
- Ada —— 这是一组软件包(包含
rcl绑定、消息生成器、tf2绑定、示例和教程),让开发者能够用 Ada 编写 ROS 2 应用程序。 - C ——
rclc并非在rcl之上添加一层封装,而是对rcl的补充,使rcl+rclc成为功能完整的 C 客户端库。教程请参见 micro.ros.org。 - JVM 和 Android —— ROS 2 的 Java 和 Android 绑定。
- .NET Core、UWP 和 C# —— 这是一组项目(包含绑定、代码生成器、示例等),用于编写面向 .NET Core 和 .NET Standard 的 ROS 2 应用程序。
- Node.js —— rclnodejs 是 ROS 2 的 Node.js 客户端,提供了简单易用的 ROS 2 JavaScript API。
- Rust —— 这是一组项目(包含 rclrs 客户端库、代码生成器、示例等),让开发者能够用 Rust 编写 ROS 2 应用程序。
- Flutter 和 Dart —— ROS 2 的 Flutter 和 Dart 绑定。
较旧的、不再维护的客户端库有:
通用功能:rcl
Section titled “通用功能:rcl”客户端库中的大部分功能并不特定于某种编程语言。例如,参数的行为和命名空间的逻辑,理想情况下在所有编程语言中都应该是一致的。因此,客户端库并不从头实现这些通用功能,而是利用通用的核心 ROS 客户端库(RCL)接口。该接口实现了与语言无关的 ROS 概念的逻辑和行为。这样一来,客户端库只需用外部函数接口(foreign function interface)包装 RCL 中的通用功能即可,使得客户端库更轻量、更易于开发。出于这个原因,通用 RCL 功能通过 C 接口提供,因为 C 语言通常是客户端库最容易包装的语言。
除了使客户端库更轻量之外,拥有通用核心的另一个优势是不同语言之间的行为更加一致。如果对核心 RCL 中的功能逻辑或行为进行了任何修改——例如命名空间相关的逻辑——所有使用 RCL 的客户端库都会反映这些变更。此外,拥有通用核心还意味着修复 bug 时维护多个客户端库的工作量更少。
rcl 的 API 文档可以在 这里 找到。
语言特定功能
Section titled “语言特定功能”需要使用语言特定特性或属性的客户端库概念,不在 RCL 中实现,而是在每个客户端库中分别实现。例如,“spin” 函数使用的线程模型就具有特定于客户端库语言的实现。
要了解使用 rclpy 的发布者和使用 rclcpp 的订阅者之间的消息交换演示,建议观看 这个 ROSCon 演讲,从 17:25 开始(幻灯片在这里)。
与 ROS 1 的比较
Section titled “与 ROS 1 的比较”在 ROS 1 中,所有客户端库都是从零开始开发的。这使得 ROS 1 的 Python 客户端库可以完全用纯 Python 实现,从而带来了不需要编译代码等好处。然而,命名约定和行为在各个客户端库之间并不总是一致的,修复 bug 需要在多个地方分别进行,而且有些功能只在某一个客户端库中实现过(例如 UDPROS)。
通过利用通用的核心 ROS 客户端库,不同编程语言编写的客户端库更容易开发,同时行为也更加一致。