【ROS2】概念:高级-内部 ROS 2 接口

 目录

  • 内部 API 架构概述

  • 类型特定接口

    •  静态类型支持

    • 静态类型支持与 DDS

    •  动态类型支持

  •   rcl 仓库

  •   rmw 仓库

  •   rosidl 仓库

  •   rcutils 仓库

内部 ROS 接口是公共 C API,旨在供开发人员使用,他们正在创建客户端库或添加新的底层中间件,但不适用于典型的 ROS 用户。ROS 客户端库提供用户界面 API,大多数 ROS 用户都熟悉这些 API,并且可能采用各种编程语言。

内部 API 架构概述 

有两个主要的内部接口:

  • ROS 中间件接口 ( rmw API)

  • ROS 客户端库接口 ( rcl API)

rmw API 是 ROS 2 软件栈与底层中间件实现之间的接口。ROS 2 使用的底层中间件是 DDS 或 RTPS 实现,负责发现、发布和订阅机制、服务的请求-应答机制以及消息类型的序列化。

rcl API 是一个稍微高级一点的 API,用于实现客户端库,并不直接接触中间件实现,而是通过 ROS 中间件接口( rmw API)抽象来实现。

6175d6d0f86e0c67f7234720724f1629.png

如图所示,这些 API 是堆叠的,因此典型的 ROS 用户将使用客户端库 API,例如 rclcpp ,来实现他们的代码(可执行文件或库)。客户端库的实现,例如 rclcpp ,使用 rcl 接口,该接口提供对 ROS 图和图事件的访问。 rcl 实现反过来使用 rmw API 来访问 ROS 图。 rcl 实现的目的是为更复杂的 ROS 概念和实用程序提供一个通用实现,这些概念和实用程序可能会被各种客户端库使用,同时与所使用的底层中间件无关。 rmw 接口的目的是捕获支持 ROS 客户端库所需的最低限度的中间件功能。最后, rmw API 的实现由特定于中间件实现的包提供,例如 rmw_fastrtps_cpp ,其库是针对供应商特定的 DDS 接口和类型编译的

在上图中还有一个标记为 ros_to_dds 的框,该框的目的是表示一类可能的包,这些包允许用户使用 ROS 等效项访问 DDS 供应商特定的对象和设置这个抽象接口的目标之一是完全将 ROS 用户空间代码与所使用的中间件隔离开来,以便更换 DDS 供应商甚至中间件技术对用户代码的影响最小。然而,我们认识到,有时尽管可能会产生后果,但深入实现并手动调整设置是有用的。通过要求使用这些包之一来访问底层 DDS 供应商的对象,我们可以避免在正常接口中暴露供应商特定的符号和头文件。通过检查包的依赖关系,看看是否使用了这些 ros_to_dds 包之一,也可以很容易地看到哪些代码可能违反了供应商的可移植性。

类型特定接口 

一路上,API 的某些部分必然是特定于交换的消息类型的,例如发布消息或订阅主题,因此需要为每种消息类型生成代码。下图展示了从用户定义的 rosidl 文件(例如 .msg 文件)到用户和系统用于执行特定类型功能的特定类型代码的路径:

340a46bcd525cf91aa3594f556bb164c.png

图:从 rosidl 文件到面向用户代码的“静态”类型支持生成流程图。

图表的右侧显示了 .msg 文件如何直接传递给特定语言的代码生成器,例如 rosidl_generator_cpp 或 rosidl_generator_py 。这些生成器负责创建用户将包含(或导入)并用作在 .msg 文件中定义的消息的内存表示的代码。例如,考虑消息 std_msgs/String ,用户可能会在 C++中使用语句 #include <std_msgs/msg/string.hpp> ,或者他们可能会在 Python 中使用语句 from std_msgs.msg import String 。这些语句之所以有效,是因为这些特定语言(但与中间件无关)的生成器包生成的文件

另外, .msg 文件用于为每种类型生成类型支持代码。在此上下文中,类型支持是指:特定于给定类型的元数据或函数,系统使用这些元数据或函数来执行特定类型的特定任务。给定消息的类型支持可能包括消息中每个字段的名称和类型列表。它还可能包含对可以执行该类型特定任务的代码的引用,例如发布消息。

静态类型支持 

当类型支持引用代码以执行特定消息类型的特定功能时,该代码有时需要执行特定于中间件的工作。例如,考虑类型特定的发布功能,当使用“供应商 A”时,该功能需要调用一些“供应商 A”的 API,但当使用“供应商 B”时,它需要调用“供应商 B”的 API。为了允许中间件供应商特定的代码,用户定义的 .msg 文件可能会导致生成供应商特定的代码。通过类型支持抽象,这些供应商特定的代码仍然对用户隐藏,这类似于“私有实现”(或 Pimpl)模式的工作方式。

使用 DDS 的静态类型支持 

对于基于 DDS 的中间件供应商,特别是那些基于 OMG IDL 文件( .idl 文件)生成代码的供应商,用户定义的 rosidl 文件( .msg 文件)被转换为等效的 OMG IDL 文件( .idl 文件)。从这些 OMG IDL 文件中,创建了特定于供应商的代码,然后在由类型支持引用的特定类型的函数中使用。上图左侧显示了这一点,其中 .msg 文件由 rosidl_dds 包消耗以生成 .idl 文件,然后这些 .idl 文件被提供给特定语言和 DDS 供应商特定的类型支持生成包。

例如,考虑 Fast DDS 实现,它有一个名为 rosidl_typesupport_fastrtps_cpp 的包。这个包负责生成代码来处理诸如将 C++ 消息对象转换为要通过网络写入的序列化八位字节缓冲区之类的事情。尽管此代码特定于 Fast DDS,但由于类型支持代码中的抽象,用户仍然无法访问它

动态类型支持 

另一种实现类型支持的方法是为发布到主题等操作提供通用函数,而不是为每种消息类型生成一个函数版本。为了实现这一点,这个通用函数需要一些关于正在发布的消息类型的元信息,比如字段名称和类型的列表,以及它们在消息类型中出现的顺序。然后,要发布消息,您可以调用一个通用的发布函数,并传递一个包含必要元数据的结构。这样称为“动态”类型支持,相对于“静态”类型支持,后者需要为每种类型生成函数版本。

13bc22f011bc1d29ec0cc0c7c6ecd2a8.png

图:从 rosidl 文件到用户界面代码的“动态”类型支持生成流程图。

上图显示了从用户定义的 rosidl 文件到生成的用户界面代码的流程。它与静态类型支持的图非常相似,只是在类型支持的生成方式上有所不同,这在图的左侧表示。在动态类型支持中, .msg 文件直接转换为用户界面代码。

此代码也是中间件无关的,因为它只包含有关消息的元信息。实际执行工作的函数,例如发布到主题,是通用的消息类型,并将对中间件特定的 API 进行任何必要的调用。请注意,与提供类型支持代码的 dds 供应商特定包(这是静态类型支持中的情况)不同,此方法为每种语言提供中间件无关的包,例如 rosidl_typesupport_introspection_c 和 rosidl_typesupport_introspection_cpp 。包名称的 introspection 部分指的是能够使用生成的消息类型元数据来内省任何消息实例的能力。这是允许像“发布到主题”这样的函数的通用实现的基本功能。

这种方法的优点是所有生成的代码都是中间件无关的,这意味着只要允许动态类型支持,它们就可以重用于不同的中间件实现。这也导致生成的代码更少,从而减少了编译时间和代码大小。

但是,动态类型支持要求底层中间件支持类似形式的动态类型支持。在 DDS 的情况下,DDS-XTypes 标准允许使用元信息而不是生成的代码发布消息。了支持动态类型支持,底层中间件中需要 DDS-XTypes 或类似的东西。此外,这种类型支持方法通常比静态类型支持替代方案慢。静态类型支持中的类型特定生成代码可以编写得更高效,因为它不需要遍历消息类型的元数据来执行诸如序列化之类的操作。

  rcl 存储库 

ROS 客户端库接口( rcl API)可以被客户端库(例如 rclc 、 rclcpp 、 rclpy 等)使用,以避免重复逻辑和功能。通过重用 rcl API,客户端库可以更小且彼此更一致。客户端库的某些部分故意不包含在 rcl API 中,因为应使用语言惯用方法来实现系统的这些部分。一个很好的例子是执行execution 模型, rcl 根本没有涉及。相反,客户端库应提供一种语言惯用的解决方案,例如 C 中的 pthreads 、C++11 中的 std::thread 和 Python 中的 threading.Thread 。通常, rcl 接口提供的功能不特定于语言模式,也不特定于特定的消息类型。

rcl API 位于 GitHub 上的 ros2/rcl 仓库中,并包含作为 C 头文件的接口。 rcl C 实现由同一仓库中的 rcl 包提供。此实现通过使用 rmw 和 rosidl API 避免与中间件的直接接触。

要了解 rcl API 的完整定义,请参阅 rcl 文档。https://docs.ros.org/en/jazzy/p/rcl/

  rmw 存储库 

ROS 中间件接口 ( rmw API) 是构建 ROS 所需的最小原始中间件功能集。不同中间件实现的提供者必须实现此接口,以支持整个 ROS 堆栈。目前所有中间件实现都是针对不同的 DDS 供应商。

rmw API 位于 ros2/rmw 仓库中。 rmw 包含定义接口的 C 头文件,其实现由不同 DDS 供应商的各种 rmw 实现包提供。

有关 rmw API 的定义,请参阅 rmw 文档。https://docs.ros.org/en/jazzy/p/rmw/

  rosidl 存储库 

rosidl API 由一些与消息相关的静态函数和类型组成,并定义了不同语言的消息应生成的代码。API 中指定的生成消息代码将是特定于语言的,但可能会或可能不会重用其他语言的生成代码。API 中指定的生成消息代码包含消息数据结构、构造函数、析构函数等内容。API 还将实现一种获取消息类型的类型支持结构的方法,该方法在发布或订阅该消息类型的主题时使用。

有几个存储库在 rosidl API 和实现中起作用。

rosidl 仓库位于 GitHub 上的 ros2/rosidl,定义了消息 IDL 语法,即 .msg 文件、 .srv 文件等的语法,包含用于解析文件的包,用于提供 CMake 基础设施以从消息生成代码,用于生成与实现无关的代码(头文件和源文件),以及用于建立默认生成器集。该仓库包含以下包:

  • rosidl_cmake :提供用于从 rosidl 文件生成代码的 CMake 函数和模块,例如 .msg 文件、 .srv 文件等。

  • rosidl_default_generators : 定义默认生成器列表,确保它们作为依赖项安装,但也可以使用其他注入的生成器。

  • rosidl_generator_c : 提供工具生成 C 头文件 ( .h ) 用于 rosidl 文件。

  • rosidl_generator_cpp : 提供工具生成 C++ 头文件 ( .hpp ) 用于 rosidl 文件。

  • rosidl_generator_py : 提供用于生成 rosidl 文件的 Python 模块的工具。

  • rosidl_parser : 提供用于解析 rosidl 文件的 Python API。

其他语言的生成器,例如 rosidl_generator_java ,托管在外部(在不同的存储库中),但将使用与上述生成器相同的机制来“注册”自己作为 rosidl 生成器。

除了上述用于解析和生成 rosidl 文件头的包之外, rosidl 库还包含与文件中定义的消息类型的“类型支持”有关的包。类型支持是指解释和操作由特定类型的 ROS 消息实例表示的信息的能力(例如发布消息)。类型支持可以由编译时生成的代码提供,也可以根据 rosidl 文件的内容(例如 .msg 或 .srv 文件)和接收到的数据通过编程方式完成,通过内省数据。在后一种情况下,通过运行时解释消息进行类型支持时,ROS 2 生成的消息代码可以与 rmw 实现无关。通过内省数据提供这种类型支持的包有:

  • rosidl_typesupport_introspection_c : 提供用于生成支持 rosidl 消息数据类型的 C 代码的工具。

  • rosidl_typesupport_introspection_cpp : 提供用于生成支持 rosidl 消息数据类型的 C++代码的工具。

在编译时生成类型支持而不是通过编程生成类型支持的情况下,需要使用特定于 rmw 实现的包。这是因为通常特定的 rmw 实现需要以特定于 DDS 供应商的方式存储和操作数据,以便 DDS 实现能够使用这些数据。有关更多详细信息,请参见上面的类型特定接口部分。

有关 rosidl API(静态和生成)的详细信息,请参见此页面:https://docs.ros.org/en/jazzy/Glossary.html#term-API

  rcutils 存储库 

ROS 2 C 工具 ( rcutils ) 是一个 C API,由宏、函数和数据结构组成,贯穿于 ROS 2 代码库中主要用于错误处理、命令行参数解析和日志记录,这些功能不特定于客户端或中间件层,可以由两者共享。

rcutils API 和实现位于 GitHub 上的 ros2/rcutils 仓库中,其中包含 C 头文件接口。https://github.com/ros2/rcutils

要了解 rcutils API 的完整定义,请参阅 rcutils 文档。https://docs.ros.org/en/jazzy/p/rcutils/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值