Internal ROS 2 interfaces
Internal API Architecture Overview
这里主要由两部分组成
- the ROS middleware interface( rmw API)
- the ROS client library interface (rcl API)
rwm API是ROS2软件层与底层的第三方中间件之间的接口,ROS2使用底层中间件是对于DDS或RTPS协议的实现,它负责
- 发现、发布和订阅机制
- 服务的请求-回复机制
- 消息类型的序列化
rcl API是一个层次级别稍高一些的API,它被使用在实现"client libraries",并且不直接接触第三方的中间件,而是通过ROS中间件接口(rwm api)来接触
如图所示,这些API堆叠在一起,以便ROS用户使用"client library API", eg: rclcpp,可以使用rcl api去实现它的代码。
对于"client libraries"的实现,eg:rclcpp,使用rcl API ,它提供了对于ROS graph 和graph events的访问,同样,对于rcl的实现,又使rwm api去访问ROS graph。
rcl层的目的是提供更多复杂ROS概念和功能的通用实现,以便为各种"client library"所使用,同时又对底层的中间件层形成封装。
rwm层的目的是捕获那些ROS client libraries所需的绝对最小中间件功能。最后,rwm api的实现是由中间件实现的特定包提供,eg:rwm_fastrtps_cpp
在上方的图表左侧,存在一个"ros_to_dds"的方框,改框的目的是表示存在一类可能的package,它允许用户使用ROS等效操作访问DDS供应商的特定的对象和设置。该抽象接口的目标之一是将ROS用户空间代码与正在使用的中间件完全隔离,因此,改变DDS供应商甚至中间件技术对用户代码的影响最小。通过使用这些package之一来访问DDS供应商的对象,我们可以避免在正常接口中暴露供应商特定的符号和标识。
通过检测包的依赖关系,以查看是否正在使用ros_to_dds包之一,还可以轻松查看那些代码可能违反供应商的可移植性。
Type Specific Interfaces
这里有一部分API是特定用于message types之间的转换的,e.g:publishing a message 、subscribing to a topic、为message type生成代码。下面的图表展示了从用户定义rosidl文件到msg的路径:msg文件通过user或system去执行类型转换特定函数成为"the type specific code"
图表的右边部分表明".msg"文件如何传递到对应的编程语言代码生成器
- rosidl_generator_cpp
- rosidl_generator_py
这些代码生成器负责创建代码,这些代码在具体的表现在编程中为C++的消息头文件<std_msgs/msg/string.hpp>,python的import包
the ".msg"file是用于支持每一种代码生成器生成"编程类型支持的代码"
type support:
Static Type Support
当需要对于一些特定的消息类型,做特定的消息处理时,代码有时需要执行中间件特定的工作。
Static Type Support with DDS
对于基于DDS中间件的厂商,特别是那些基于"the OMG IDL files( .idl files )",用户需要从自定义的" rosidl files(*.msg files)"转换成等价的"OMG IDL files(.idl files)",从这些"OMG IDL files(.idl files)",创建DDS中间件厂商的特定代码,然后在特定类型函数(包含这些类型引用代码)中使用,上图中左侧部分展示这一流程。
Dynamic Type Support
另一条实现type support的方法是使用通用函数来处理,诸如发布话题之类的事情,而不是为每一种消息类型生成该函数的版本。为了实现这一想法,这些通用函数需要有一些关于被发布的话题消息的元信息,例如:字段名称和类型的列表(列表顺序按照它们在消息类型中出现的顺序排列)。然后为了发布这一message,你可以调用通用发布函数和传递要发布的消息以及包含有关消息类型的必要元数据的结构。这被称为"dynamic type support",与"static type support"相反,"static type support"需要为每种type生成函数版本。
上图展示了从用户定义的rosidl文件到生成面向用户的代码的流程,它与"static type support"的图非常相似,不同之处仅在于"type support"的生成方式(由图的左侧表示).在"dynamic type support"中,".msg file"直接转换为面向用户的代码
该代码也是与中间件无关的,因为它仅仅包含"message"的元信息。实际工作的Function:eg:发布话题,对于消息类型是通用的,并将对中间件特定的API进行任何必要的调用。
note: 与提供"the type support code"的dds厂商的特定包不同,此方法为每种编程语言提供与中间件无关的包,例如:
rosidl_typesupport_introspection_c
androsidl_typesupport_introspection_cpp
.这个package中的"introspection"部分指的是使用"the message type"生成的" meta data"能够复现任何"message instance"的能力,这是对于允许实现通用函数,"publish to a topic ",所需要的基础功能
"the dynamic type support"有这样的优点:生成的代码与中间件无关,这意味着它可以重新使用不同的中间件实现,只要它们允许"dynamic type support",它还会减少生成的代码,从而减少编译时间和代码大小。
但是,"the dynamic type support"要求底层中间件支持类似形式的"the dynamic type support",如果是DDS、DDS-XTypes标准,允许使用"Meta data",而不是生成代码来发布消息,底层中间件需要DDS-XTypes或类似的东西才能支持"the dynamic type support"。此外,这种"dynamic type support"通常比"static type support"更慢一些。"the static type support"中的生成代码可以编写得更加高效,因为它不需要迭代"message"的"meta data"去执行序列化的操作。
The rcl repository
rcl API可以被"client libraries"所使用(eg:rclc,rclcpp,rclpy, etc),目的是为了避免重复的逻辑和功能。
通过复用rcl API,使用客户端库("client libraries")可以更小并且彼此的功能更加一致。
client libraries的某些部分被故意排除在rcl API之外,因为使用编程语言的惯用方法来实现系统的这些部分会更好一些。一个比较好的例子,就是"the execution model",这在rcl api中就没有解决或提供对应的接口,于是不同编程语言就有了不同的实现
- pthreads in C
- std::thread in C++11
- threading.Thread in Python
通常,rcl 层提供的Function不会被编程语言限制,也不会被消息类型限制,是一个通用概念。
对于关于rcl API更复杂的定义,请看the rcl docs.
The rwm repository
rwm api是在其上构建ROS所需的最小原始中间件接口的功能集,不同的中间件供应商必须实现此接口,以便支持整个ROS堆栈,目前所有的中间件实现都是针对不同的DDS供应商
rwm层包含了定义接口的c头文件,其实现由不同DDS供应商的编写。
对于关于rwm API更复杂的定义,请看 the rmw docs.
The rosidl repository
rosidl api由一些与消息相关的静态函数和类型以及不同编程语言的代码生成定义组成,API中指定的生成消息代码是特定于某一编程语言的。消息生成代码特定于API包含message的数据结构、构造函数和销毁函数。API将实现一种获取消息类型结构的方法,该结构在发布话题或订阅话题中使用。
下面有几个库在rosidl API中有着及其重要的作用:
定义消息的IDL语法:".msg" files 和 ".srv" files等,包括这些功能包:
- 文件解析
- 为消息代码生成提供Cmake的基础设施
- 生成与实现无关的代码(头文件和源文件)
- 建立默认的生成器集
具体的package如下
- rosidl_cmake: 为从"rosidl files"中生成代码提供Cmake Function&Modules。
- rosidl_default_generators: 定义默认生成器列表,确保它们作为依赖项安装,但是其他生成器也可以被使用
- rosidl_generator_c:提供工具,用于从rosidl files中生成c header files
- rosidl_generator_cpp:提供工具,用于从rosidl files中生成c++ header files
- rosidl_generator_py:提供工具,用于从rosidl files中生成python modules
- rosidl_parser:提供python的API用于解析rosidl files
对于其他编程语言的生成器,譬如:rosidl_generator_java,这是由其他外部团队运营托管,不归ros团队管理,但是使用的是同一套机制
此外,rosidl 中还包含与"type support"定义相关的包。
"type support"指的是对 ROS message实例进行解释(翻译转述)和操作这个实例所表达信息的能力。
"type support"可以由编译时生成的代码提供,也可以根据rosidl files的内容以编程方式完成,静态和动态支持
下面的包(提供introspection数据的type support):
- rosidl_typesupport_introspection_c:提供工具,用于支持从rosidl message data type生成C 代码
- rosidl_typesupport_introspection_cpp: 提供工具,用于从支持从rosidl message data type生成C++代码
在这种情况下,"type support"是通过编译时生成,而不是通过编程方式生成,它需要使用到rmw层的包。这是因为特定的rmw实现通常需要以DDS供应商特定的方式存储和操作数据,以便DDS实现能够使用它。
更多细节看上一节的Type Specific Interfaces
[========]
The rcutils repository
rcutils是一个C语言的API,它由macros、functions 和 data structures组成,且在整个ROS2代码库中使用。
主要功能:
- error handling
- commandline argument parsing
- logging
这些功能不限定于client libraries还是middleware layers使用,简而言之,它是ros框架中通用的库
细节请看the rcutils docs.