【ROS2】Concept(Basic) - 详解

基本概念

ROS 2 是一种基于强类型、匿名发布/订阅机制的中间件,能够实现不同进程间的消息传递。

任何 ROS 2 系统的核心都在于 ROS graph。ROS graph 指的是 ROS 系统中的节点网络,以及节点之间用于通信的连接。

以下这些概念将助力你初步理解 ROS 2 的基础知识。

一、Nodes(节点)

节点是 ROS 2 graph 中的参与者,它通过 client library 与其他节点通信。节点可以与同一进程、不同进程或不同机器上的其他节点进行通信。节点通常是 ROS graph 中的计算单元;每个节点应负责一项逻辑功能。

节点可以向命名的 topics 发布消息,以向其他节点传递数据,或者订阅命名的 topics,以从其他节点获取数据。它们还可以作为 service client,让其他节点代为执行计算,或者作为 service server,为其他节点提供功能。对于长时间运行的计算,节点可以作为 action client,让其他节点代为执行,或者作为 action server,为其他节点提供功能。节点可以提供可配置的 parameters,以在运行时改变行为。

节点通常是发布者、订阅者、service server、service client、action server 和 action client 的复杂组合。

节点之间的连接通过分布式发现过程建立。

二、Discovery(发现)

节点的发现通过 ROS 2 的底层中间件自动完成。其过程可总结如下:

  • 当节点启动时,会向同一 ROS 域(由 ROS_DOMAIN_ID 环境变量设置)网络中的其他节点宣告自身存在。其他节点会对这一宣告做出回应,提供自身信息,以便建立适当的连接,实现节点间的通信。

  • 节点会定期宣告自身存在,这样即使在初始发现阶段之后,也能与新发现的实体建立连接。

  • 节点离线时会向其他节点宣告。

只有当节点具有兼容的 Quality of Service 设置时,它们才会建立连接。

以 talker-listener 演示为例:在一个终端中运行 C++ talker 节点,它会在某个 topic 上发布消息,而在另一个终端中运行的 Python listener 节点会订阅同一 topic 上的消息。你会看到这些节点会自动发现彼此,并开始交换消息。

三、Interfaces(接口)

1. 背景

ROS 应用通常通过三种类型的接口进行通信:topics(话题)、services(服务)或 actions(动作)。ROS 2 使用一种简化的描述语言,即接口定义语言(IDL)来描述这些接口。这种描述便于 ROS 工具自动为多种目标语言生成接口类型的源代码。

本文档将介绍以下支持的类型:

  • msg:.msg 文件是用于描述 ROS 消息字段的简单文本文件。它们用于为不同语言生成消息的源代码。
  • srv:.srv 文件用于描述服务。它们由两部分组成:请求(request)和响应(response)。请求和响应都是消息声明。
  • action:.action 文件用于描述动作。它们由三部分组成:目标(goal)、结果(result)和反馈(feedback)。每一部分本身都是一个消息声明。

2. Messages

消息是 ROS 2 节点在网络上向其他 ROS 节点发送数据的一种方式,不期望收到响应。例如,如果一个 ROS 2 节点从传感器读取温度数据,它可以使用 Temperature 消息在 ROS 2 网络上发布该数据。ROS 2 网络上的其他节点可以订阅该数据并接收 Temperature 消息。

消息在 ROS 包的 msg/ 目录下的 .msg 文件中进行描述和定义。.msg 文件由字段(fields)和常量(constants)两部分组成。

Fileds

int32 my_int
string my_string

(1)Field types

字段类型可以是:

  • 内置类型
  • 自行定义的消息描述名称,如 “geometry_msgs/PoseStamped”
    目前支持的内置类型:
    在这里插入图片描述

每个内置类型都可以用来定义数组:

在这里插入图片描述

所有比其 ROS 定义更宽松的类型都会通过软件来强制实施 ROS 在范围和长度方面的约束。

使用数组和有界类型的消息定义示例:

在这里插入图片描述

(2)Field names

字段名称必须是小写字母数字字符,单词之间用下划线分隔。名称必须以字母开头,且不能以下划线结尾或包含两个连续的下划线。

(3)Field default value

默认值可以设置为消息类型中的任何字段。目前不支持字符串数组和复杂类型(即上文内置类型表中未列出的类型;这适用于所有嵌套消息)。

定义默认值是通过向字段定义行添加第三个元素来完成的,即:

在这里插入图片描述
在这里插入图片描述

Constants

每个常量定义就像一个带有默认值的字段描述,只不过这个值永远无法通过编程更改。这种赋值方式用等号“=”表示,例如

在这里插入图片描述
常量名称必须大写

3. Services

服务是一种请求/响应式的通信方式,客户端(请求方)会等待服务器(响应方)完成简短计算并返回结果。

服务的描述和定义位于ROS功能包的srv/目录下的.srv文件中。

一个服务描述文件包含请求和响应两种消息类型,两者用“—”分隔。任何两个.msg文件通过“—”连接起来,都是合法的服务描述。

以下是一个非常简单的服务示例,它接收一个字符串并返回一个字符串:

string str
---
string str

当然,我们也可以定义更复杂的服务(如果要引用同一个功能包中的消息,不必提及功能包名称):

# 请求部分的常量
int8 FOO=1
int8 BAR=2
# 请求部分的字段
int8 foobar
another_pkg/AnotherMessage msg
---
# 响应部分的常量
uint32 SECRET=123456
# 响应部分的字段
another_pkg/YetAnotherMessage val
CustomMessageDefinedInThisPackage value
uint32 an_integer

注意,不能在一个服务内部嵌套另一个服务。

4. Actions

动作(Actions)是一种长期运行的请求/响应式通信方式,动作客户端(请求方)会等待动作服务器(响应方)执行某些操作并返回结果。与服务不同,动作可以长时间运行(数秒甚至数分钟),在运行过程中能提供反馈,并且可以被中断。

动作的定义格式如下:

---
 
---

和服务类似,第一个三连字符(—)之前是请求字段,之后是响应字段。第二个三连字符之后是第三组字段,用于发送反馈信息。

请求字段、响应字段和反馈字段的数量均可任意设置(包括零个)。

<请求类型>、<响应类型>和<反馈类型>遵循与消息中<类型>相同的所有规则;<请求字段名>、<响应字段名>和<反馈字段名>遵循与消息中<字段名>相同的所有规则。

例如,斐波那契(Fibonacci)动作的定义如下:

int32 order
---
int32[] sequence
---
int32[] sequence

在这个动作定义中,动作客户端发送一个int32类型的字段,代表要计算的斐波那契数列的阶数,期望动作服务器返回一个int32类型的数组,包含完整的数列结果。同时,在计算过程中,动作服务器可能会提供一个int32类型的中间数组,包含截至某一时刻已完成的计算步骤。

四、Topics(话题)

目录

  • 发布/订阅
  • 匿名性
  • 强类型

话题是ROS 2提供的三种主要接口类型之一。话题应用于连续的数据流,如传感器数据、机器人状态等。

如前所述,ROS 2是一个强类型、匿名的发布/订阅系统。下面我们来详细解析这句话并进行更多说明。

1、发布/订阅

发布/订阅系统包含数据的生产者(发布者)和消费者(订阅者)。发布者和订阅者通过“话题”这一概念实现相互通信,话题是一个通用名称,使各实体能够找到彼此。例如,当创建一个发布者时,必须给它指定一个字符串作为话题名称;订阅者也是如此。任何使用相同话题名称的发布者和订阅者都能直接进行通信。任何特定的话题上都可以有零个或多个发布者,以及零个或多个订阅者。当任何发布者向话题发布数据时,系统中的所有订阅者都会接收到该数据。这种系统也被称为“总线”,因为它在某种程度上类似于电气工程中的设备总线。这种总线概念是ROS 2成为强大且灵活的系统的部分原因。发布者和订阅者可以根据需要随时加入或退出,这意味着调试和内省是该系统的自然扩展。例如,如果想要记录数据,可以使用ros2 bag record命令。在底层,ros2 bag record会为你指定的任何话题创建一个新的订阅者,而不会中断数据流向系统其他部分的过程。

2、匿名性

引言中提到的另一点是ROS 2具有“匿名性”。这意味着当订阅者收到一条数据时,通常不知道也不关心该数据最初是由哪个发布者发送的(尽管如果想知道的话也能查到)。这种架构的好处是,发布者和订阅者可以随意替换,而不会影响系统的其他部分。

3、强类型

最后,引言中还提到发布/订阅系统是“强类型”的。在这种情况下,这有两层含义:

  • ROS消息中每个字段的类型都是指定的,并且该类型在多个层面都受到强制约束。例如,如果ROS消息包含:
    uint32 field1 string field2
    那么代码会确保field1始终是一个无符号整数,field2始终是一个字符串。
  • 每个字段的语义都有明确的定义。虽然没有自动机制来确保这一点,但所有核心的ROS类型都具有明确的语义。例如,IMU消息包含一个用于测量角速度的三维向量,每个维度都被指定为以弧度/秒为单位。不应将其他解释赋予该消息。

五、Services(服务)

目录

  • 服务端
  • 客户端

在ROS 2中,服务指的是一种远程过程调用。也就是说,一个节点可以对另一个节点进行远程过程调用,后者会执行计算并返回结果。

这种结构体现在服务消息的定义方式上:

uint32 request
---
uint32 response

在ROS 2中,服务应快速返回结果,因为客户端通常会等待结果。服务绝不应用于长时间运行的进程,特别是那些在特殊情况下可能需要被抢占的进程。如果有一个服务需要执行长时间的计算,考虑使用动作(action)代替。

服务通过服务名称来标识,服务名称与话题名称类似(但属于不同的命名空间)。

服务由两部分组成:服务端和客户端。

1、服务端

服务端是接受远程过程请求并对其执行某些计算的实体。例如,假设ROS 2消息包含以下内容:

uint32 a
uint32 b
---
uint32 sum

服务端会接收这条消息,将ab相加,然后返回sum

注意:
每个服务名称应只对应一个服务端。如果同一服务名称存在多个服务端,客户端的请求会被哪个服务端接收是不确定的。

2、客户端

客户端是请求远程服务端代表其执行计算的实体。承接上面的例子,客户端是创建包含ab的初始消息,并等待服务端计算出总和并返回结果的实体。

与服务端不同,使用同一服务名称的客户端数量可以是任意的。

六、Actions(动作)

目录

  • 动作服务器
  • 动作客户端

在ROS 2中,动作指的是一种长期运行的远程过程调用,它能提供反馈,并且支持取消或抢占目标。例如,运行机器人的高级状态机可能会调用一个动作,告知导航子系统前往某个航点,这一过程可能需要数秒(甚至数分钟)。在此过程中,导航子系统可以提供关于进度的反馈,而高级状态机则可以选择取消或抢占前往该航点的任务。

这种结构体现在动作消息的定义方式上:

int32 request
---
int32 response
---
int32 feedback

在ROS 2中,动作适用于长期运行的过程,因为建立和监控连接存在一定开销。如果需要进行短时间运行的远程过程调用,考虑使用服务代替。

动作通过动作名称来标识,动作名称与话题名称类似(但属于不同的命名空间)。

动作由两部分组成:动作服务器和动作客户端。

1、动作服务器

动作服务器是接受远程过程请求并对其执行某些过程的实体。它还负责在动作执行过程中发送反馈,并且应该对取消/抢占请求做出响应。例如,考虑一个用于计算斐波那契数列的动作,其接口如下:

int32 order
---
int32[] sequence
---
int32[] sequence

动作服务器会接收这条消息,开始计算到order为止的数列(过程中提供反馈),最终在sequence中返回完整结果。

注意:
每个动作名称应只对应一个动作服务器。如果同一动作名称存在多个动作服务器,客户端的请求会被哪个动作服务器接收是不确定的。

2、动作客户端

动作客户端是请求远程动作服务器代表其执行某个过程的实体。承接上面的例子,动作客户端是创建包含order的初始消息,并等待动作服务器计算出数列并返回(过程中接收反馈)的实体。

与动作服务器不同,使用同一动作名称的动作客户端数量可以是任意的。

七、Parameters(参数)

目录

  • 概述
  • 参数背景
  • 声明参数
  • 参数类型
  • 参数回调
  • 与参数交互
  • 运行节点时设置初始参数值
  • 启动节点时设置初始参数值
  • 运行时修改参数值
  • 从ROS 1迁移

1、概述

ROS 2中的参数与各个节点相关联。参数用于在启动时(以及运行期间)配置节点,而无需修改代码。参数的生命周期与节点的生命周期绑定(不过节点可以实现某种持久化机制,以便在重启后重新加载值)。

参数通过节点名称、节点命名空间、参数名称和参数命名空间来寻址。参数命名空间是可选的。

每个参数都包含一个键、一个值和一个描述符。键是一个字符串,值可以是以下类型之一:boolint64float64stringbyte[]bool[]int64[]float64[]string[]。默认情况下,所有描述符都是空的,但可以包含参数描述、值范围、类型信息和其他约束条件。

有关ROS参数的实践教程,请参见《理解参数》。

2、参数背景

声明参数

默认情况下,节点需要声明其在生命周期内将接受的所有参数。这样做是为了在节点启动时明确定义参数的类型和名称,从而减少后续配置错误的可能性。有关从节点声明和使用参数的教程,请参见《在类中使用参数(C++)》或《在类中使用参数(Python)》。

对于某些类型的节点,并非所有参数都能提前知晓。在这种情况下,可以将节点实例化时的allow_undeclared_parameters设置为true,这将允许获取和设置节点上未声明的参数。

参数类型

ROS 2节点上的每个参数都具有概述中提到的预定义参数类型之一。默认情况下,尝试在运行时更改已声明参数的类型将会失败。这可以防止常见错误,例如将布尔值放入整数参数中。

如果某个参数需要是多种不同类型,并且使用该参数的代码能够处理,那么可以更改此默认行为。声明参数时,应使用ParameterDescriptor,并将其dynamic_typing成员变量设置为true

参数回调

ROS 2节点可以注册三种不同类型的回调,以便在参数发生更改时得到通知。这三种回调都是可选的。

第一种称为“预设置参数”回调,可以通过节点API的add_pre_set_parameters_callback进行设置。该回调会接收一个正在被更改的Parameter对象列表,并且不返回任何内容。调用它时,可以修改Parameter列表,以更改、添加或删除条目。例如,如果parameter2需要在parameter1更改时随之更改,就可以通过此回调实现。

第二种称为“设置参数”回调,可以通过节点API的add_on_set_parameters_callback进行设置。该回调接收一个不可变的Parameter对象列表,并返回一个rcl_interfaces/msg/SetParametersResult。此回调的主要目的是让用户能够检查参数即将发生的更改,并明确拒绝该更改。

注意:
“设置参数”回调不得有副作用,这一点很重要。由于多个“设置参数”回调可以串联,单个回调无法知道后续回调是否会拒绝更新。例如,如果单个回调对其所在的类进行了更改,它可能会与实际参数不同步。要在参数成功更改后获得回调,请参见下面的下一种回调类型。

第三种类型的回调称为“后设置参数”回调,可以通过节点API的add_post_set_parameters_callback进行设置。该回调接收一个不可变的Parameter对象列表,并且不返回任何内容。此回调的主要目的是让用户能够对已成功接受的参数更改做出反应。

ROS 2演示中包含了所有这些回调的使用示例。

3、与参数交互

ROS 2节点可以通过节点API执行参数操作,如《在类中使用参数(C++)》或《在类中使用参数(Python)》中所述。外部进程可以通过节点实例化时默认创建的参数服务执行参数操作。默认创建的服务如下:

  • /node_name/describe_parameters:使用rcl_interfaces/srv/DescribeParameters服务类型。给定参数名称列表,返回与这些参数相关联的描述符列表。
  • /node_name/get_parameter_types:使用rcl_interfaces/srv/GetParameterTypes服务类型。给定参数名称列表,返回与这些参数相关联的参数类型列表。
  • /node_name/get_parameters:使用rcl_interfaces/srv/GetParameters服务类型。给定参数名称列表,返回与这些参数相关联的参数值列表。
  • /node_name/list_parameters:使用rcl_interfaces/srv/ListParameters服务类型。给定可选的参数前缀列表,返回具有该前缀的可用参数列表。如果前缀为空,则返回所有参数。
  • /node_name/set_parameters:使用rcl_interfaces/srv/SetParameters服务类型。给定参数名称和值的列表,尝试在节点上设置这些参数。返回尝试设置每个参数的结果列表,其中一些可能成功,一些可能失败。
  • /node_name/set_parameters_atomically:使用rcl_interfaces/srv/SetParametersAtomically服务类型。给定参数名称和值的列表,尝试在节点上设置这些参数。返回尝试设置所有参数的单个结果,因此如果一个失败,所有都失败。

4、运行节点时设置初始参数值

可以在运行节点时通过单个命令行参数或YAML文件设置初始参数值。有关如何设置初始参数值的示例,请参见《直接从命令行设置参数》。

5、启动节点时设置初始参数值

也可以通过ROS 2启动工具在运行节点时设置初始参数值。有关如何通过启动指定参数的信息,请参见本文档。
https://docs.ros.org/en/kilted/Tutorials/Intermediate/Launch/Using-ROS2-Launch-For-Large-Projects.html

6、运行时修改参数值

ros2 param命令是与已运行节点的参数进行交互的常用方式。ros2 param使用上述参数服务API来执行各种操作。有关如何使用ros2 param的详细信息,请参见本操作指南。
https://docs.ros.org/en/kilted/How-To-Guides/Using-ros2-param.html

7、从ROS 1迁移

《启动文件迁移指南》解释了如何将ROS 1中的paramrosparam启动标签迁移到ROS 2。

《迁移指南》解释了如何将参数从ROS 1迁移到ROS 2。

八、使用命令行工具进行 Introspection

目录

  • 用法
  • 示例
  • ROS 2守护进程:后台发现服务
  • 实现方式

ROS 2包含一套命令行工具,可用于对ROS 2系统进行内省。

1、用法

这些工具的主要入口是ros2命令,该命令本身包含多个子命令,可用于内省节点、话题、服务等组件,也可与这些组件进行交互。

若要查看所有可用的子命令,请运行以下指令:

$ ros2 --help

可用的子命令示例如下:

  • action:内省ROS动作 / 与ROS动作进行交互
  • bag:记录/播放rosbag(数据包)
  • component:管理组件容器
  • daemon:内省ROS 2守护进程 / 配置ROS 2守护进程
  • doctor:检查ROS设置是否存在潜在问题
  • interface:显示ROS接口相关信息
  • launch:运行启动文件 / 内省启动文件
  • lifecycle:内省具有受控生命周期的节点 / 管理具有受控生命周期的节点
  • multicast:多播调试命令
  • node:内省ROS节点
  • param:内省节点上的参数 / 配置节点上的参数
  • pkg:内省ROS功能包
  • run:运行ROS节点
  • security:配置安全设置
  • service:内省ROS服务 / 调用ROS服务
  • test:运行ROS启动测试
  • topic:内省ROS话题 / 发布ROS话题
  • trace:用于获取ROS节点执行信息的跟踪工具(仅在Linux系统可用)
  • wtfdoctor命令的别名

2、示例

若要使用命令行工具实现典型的“发布者-订阅者”(talker-listener)示例,可通过topic子命令在话题上发布消息和回显消息。

在一个终端中发布消息,运行以下指令:

$ ros2 topic pub /chatter std_msgs/msg/String "data: Hello world"

终端将输出以下内容(表示开始循环发布消息):

publisher: beginning loop
publishing #1: std_msgs.msg.String(data='Hello world')
publishing #2: std_msgs.msg.String(data='Hello world')

在另一个终端中回显接收到的消息,运行以下指令:

$ ros2 topic echo /chatter

终端将输出以下内容:

data: Hello world
data: Hello world

3、ROS 2守护进程:后台发现服务

ROS 2采用分布式发现流程,使节点能够相互连接。由于该流程特意不采用集中式发现机制,ROS节点可能需要一定时间才能发现ROS计算图中的所有其他参与者。为解决这一问题,ROS 2会运行一个后台守护进程,该进程会维护ROS计算图的相关信息,以便更快地响应查询(例如节点名称列表查询)。

当你首次使用ros2 node listros2 topic list等命令行工具或其他内省命令时,ROS 2守护进程会自动启动。如果当前没有守护进程在运行,这些工具会先在后台实例化一个新的守护进程,然后再执行请求的命令。

守护进程通过本地主机网络接口(127.0.0.1)进行通信,并将ROS_DOMAIN_ID环境变量用作端口号偏移量。这意味着,若要控制特定的守护进程实例(例如使用ros2 daemon stop命令),必须确保你的ROS_DOMAIN_ID与该守护进程使用的域ID匹配。不同的ROS_DOMAIN_ID值会对应不同的守护进程实例,这些实例将在不同的端口上运行。

运行ros2 daemon --help可查看与守护进程交互的更多选项,包括启动、停止守护进程或检查其运行状态的命令。

4、实现方式

ros2命令的源代码可在以下链接获取:https://github.com/ros2/ros2cli。

ros2工具被设计为一套可通过插件扩展的框架。例如,sros2功能包提供了security子命令——如果已安装sros2功能包,ros2工具会自动检测到该子命令。

九、Launch(启动)

一个ROS 2系统通常由多个节点组成,这些节点运行在不同的进程中(甚至可能分布在不同的机器上)。虽然可以手动启动每个节点,但这种方式很快就会变得繁琐。

ROS 2中的启动系统(launch system)旨在通过一条命令自动运行多个节点。它能帮助用户描述系统的配置信息,然后按照描述执行配置。系统配置包括:要运行的程序、程序的运行位置、传递给程序的参数,以及ROS特有的约定(这些约定通过为组件配置不同参数,使组件能在系统中轻松复用)。此外,启动系统还负责监控已启动进程的状态,并对进程状态的变化进行报告和/或做出响应。

上述所有配置都在“启动文件”(launch file)中指定,启动文件可使用XML、YAML或Python编写。之后,通过ros2 launch命令即可运行该启动文件,文件中指定的所有节点都会随之启动。

如需了解启动文件的编写和使用方法,请参考启动教程(launch tutorials)。

十、Client libraries(客户端库)

目录

  • 概述
  • 支持的客户端库
  • rclcpp 功能包
  • rclpy 功能包
  • 社区维护的客户端库
  • 通用功能:rcl
  • 语言特定功能
  • 演示示例
  • 与ROS 1的对比
  • 总结

1、概述

客户端库是允许用户实现ROS 2代码的API。通过客户端库,用户可以使用节点、话题、服务等ROS 2概念。客户端库支持多种编程语言,因此用户可以选择最适合其应用场景的语言编写ROS 2代码。例如,你可能更倾向于用Python编写可视化工具(因为Python能加快原型迭代速度),而对于系统中注重效率的部分,则更适合用C++实现节点。

使用不同客户端库编写的节点能够相互共享消息,因为所有客户端库都实现了代码生成器,可让用户在对应语言中与ROS 2接口文件进行交互。

除了语言特定的通信工具外,客户端库还向用户开放使ROS成为“ROS”的核心功能。例如,以下功能通常可通过客户端库访问:

  • 名称与命名空间
  • 时间(实时或仿真时间)
  • 参数
  • 控制台日志
  • 线程模型
  • 进程内通信

2、支持的客户端库

C++客户端库(rclcpp)和Python客户端库(rclpy)均基于rcl中的通用功能构建。

rclcpp 功能包

C++版ROS客户端库(rclcpp)是面向用户的C++风格接口,提供所有ROS客户端功能,如创建节点、发布者和订阅者。rclcpp 构建于 rclrosidl API 之上,设计用于配合 rosidl_generator_cpp 生成的C++消息使用。

rclcpp 充分利用C++和C++17的所有特性,力求让接口尽可能易用;同时,由于它复用了 rcl 中的实现,因此能与其他使用 rcl API 的客户端库保持一致的行为。

rclcpp 代码仓库位于GitHub的 ros2/rclcpp,包含 rclcpp 功能包。其生成的API文档可访问:https://docs.ros.org/en/kilted/p/rclcpp/。

rclpy 功能包

Python版ROS客户端库(rclpy)是C++客户端库的Python对应版本。与C++客户端库类似,rclpy 的实现同样构建于 rcl C API 之上。该接口提供符合Python风格的使用体验,支持原生Python类型和模式(如列表、上下文对象)。通过在实现中使用 rcl API,rclpy 在功能对等性和行为上与其他客户端库保持一致。

除了为 rcl API 提供符合Python风格的绑定、为每条消息提供Python类外,Python客户端库还负责处理执行模型——使用 threading.Thread 或类似机制运行 rcl API 中的函数。

与C++类似,rclpy 会为用户交互的每条ROS消息生成自定义Python代码;但与C++不同的是,它最终会将原生Python消息对象转换为C版本的消息。所有操作均在Python版本的消息上进行,直到需要将消息传入 rcl 层时,才会将其转换为纯C版本的消息,以便传入 rcl C API。若进程内的发布者与订阅者之间进行通信,会尽可能避免这种转换,以减少Python与C之间的转换开销。

rclpy 代码仓库位于GitHub的 ros2/rclpy,包含 rclpy 功能包。其生成的API文档可访问:https://docs.ros.org/en/kilted/p/rclpy/。

社区维护的客户端库

C++和Python客户端库由ROS 2核心团队维护,而ROS 2社区成员则维护着其他客户端库:

  • Ada:一组功能包(包含 rcl 绑定、消息生成器、tf2 绑定、示例和教程),支持用Ada编写ROS 2应用程序。
  • Crclc 并非在 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.jsrclnodejs 是ROS 2的Node.js客户端,为ROS 2编程提供简单易用的JavaScript API。
  • Rust:一组项目(包含 rclrs 客户端库、代码生成器、示例等),支持开发者用Rust编写ROS 2应用程序。
  • Flutter与Dart:ROS 2的Flutter和Dart绑定。

以下是已停止维护的旧客户端库:

  • C#
  • Objective-C与iOS
  • Zig

3、通用功能:rcl

客户端库中的大部分功能并不依赖于其编程语言。例如,参数的行为和命名空间的逻辑应在所有编程语言中保持一致。因此,客户端库不会从零开始实现这些通用功能,而是借助通用核心ROS客户端库(RCL)接口——该接口实现了与语言无关的ROS概念逻辑和行为。

最终,客户端库只需通过外部函数接口(foreign function interfaces)对RCL中的通用功能进行封装即可。这使得客户端库更精简,也更易于开发。正因如此,通用RCL功能通过C接口开放——因为C语言通常是客户端库最容易封装的语言。

除了让客户端库更轻量化外,通用核心的另一个优势是确保不同语言间的行为一致性。若对核心RCL中功能(如命名空间)的逻辑/行为进行修改,所有使用RCL的客户端库都会同步体现这些修改。此外,通用核心还意味着在修复漏洞时,维护多个客户端库的工作量会大幅减少。

rcl 的API文档可在此处查看。

4、语言特定功能

需要语言特定特性/属性的客户端库概念,不会在RCL中实现,而是在各个客户端库中单独实现。例如,“spin”函数使用的线程模型,其实现会因客户端库的编程语言而异。

5、演示示例

若你想了解如何通过 rclpy 编写的发布者与 rclcpp 编写的订阅者进行消息交互,建议观看ROSCon演讲(从17:25处开始),演讲幻灯片可在此处查看。

6、与ROS 1的对比

在ROS 1中,所有客户端库均“从零开始”开发。例如,这使得ROS 1的Python客户端库可纯用Python实现,带来了无需编译代码等优势。但这种方式也存在问题:客户端库之间的命名约定和行为并不总是一致;漏洞修复需在多个地方进行;许多功能仅在某一个客户端库中实现(如UDPROS)。

7、总结

通过借助通用核心ROS客户端库,用多种编程语言编写的客户端库不仅更易于开发,还能拥有更一致的行为。

posted @ 2025-09-17 09:15  yjbjingcha  阅读(7)  评论(0)    收藏  举报