20.重映射名称

话题、参数和服务都是由名称来标识的。在ROS节点中名称是硬编码的,但可以在运行时通过重映射来更改名称。如果没有重映射,则节点的每个实例都需要更改代码。本文描述了ROS 2中重映射名称的要求、原理和机制。

作者:Shane Loretz(肖恩·洛雷兹)

撰写日期:2017-03

最后修改时间:2020-03

20.1 为什么要重映射名称

重映射名称允许在系统的不同部分重用同一个节点的可执行文件。具有多个相同类型传感器的机器人可以启动同一个节点的多个实例,并将输出重映射到不同的话题(见下图)。

20.2 名称的结构

名称的完整定义可以在这篇设计文章(即本专栏上一篇设计文章“话题和服务名称到DDS的映射”)中找到。在阅读本文之前应该先阅读该设计文章中名称完整定义的相关内容。

20.2.1 快速小结

如果名称以正斜杆(/)开头,就称为完全限定名称(FQN),否则称为相对名称。正斜杆(/)之间的字符串称为令牌(tokens)。可以从概念上将名称分为两部分:命名空间基本名称。基本名称就是名称中的最后一个令牌。命名空间是基本名称之前的所有字符串。

20.2.2 示例名称

● /foo

● /foo/bar

● ~/foo/bar

● {node}/bar

● bar

20.3 重映射规则的结构

重映射规则是描述节点应如何更改其所使用名称的一些指令。 重映射规则包括两个组成部分。第一部分用于确定该规则是否要应用于某个名称。第二部分是对某个匹配名称的替换。用另一个名称替换某个名称的行为就是重映射。

20.4 ROS 2中的重映射用例

在ROS 2 的设计中,考虑了下述重映射用例:

● 重映射进程中的节点

● 更改命名空间

● 更改基本名称

● 更改令牌

● 完全限定名称FQN 扩展前重映射

● 完全限定名称FQN的准确替换

● 相对名称的准确替换

● 通过命令行重映射

● 更改默认命名空间

● 更改节点名称

● 分别重映射话题和服务名称

20.4.1 重映射进程中的节点

这是能将重映射规则应用于一个进程中的某个节点而不会影响其他节点的能力。因为ROS 2中的进程可以包含多个节点,所以一个进程中的多个节点可能会出于不同目的而使用相同的名称。用户可能希望更改一个节点中使用的名称而不影响其余节点的名称。

20.4.2 更改命名空间

节点均位于某个命名空间中,或者具有一个默认命名空间。这个命名空间会被添加到节点使用的所有相对名称之前。本用例可以使ROS 2具有用一条规则就能更改多个名称的命名空间这一能力。

ROS 1中一个流行的软件包actionlib会创建使用同一命名空间的5个话题。在ROS 1中,重映射一个actionlib客户端或服务器意味着要创建5条重映射规则。而在ROS 2中,只需一条规则就可以重映射这5个话题。

示例:

● 节点提供一个actionlib服务器move_head并检查一个名为move_head的参数

● 一条规则将命名空间move_head重映射到 move_head_check_collision

● 命名空间actionlib中的全部5个话题都会被重映射到命名空间/move_head_check_collision中,但参数名称仍保持不变。

20.4.3 更改基本名称

本用例可以使ROS 2具有用一条规则就能更改多个名称的基本名称这一能力。用户可能会希望将某个基本名称的多个实例更改为另一个令牌的多个实例。

示例:

● 某个节点使用名称/scan/head/scan、/base/scan

● 用户希望该节点在一些处理后订阅相同的数据

● 用户将基本名称scan重映射到scan_filtered

● 最终的话题名称为/scan/head/scan_filtered、/base/scan_filtered

20.4.4 更改令牌

本用例可以使ROS 2具有更改多个名称中任意位置上某一令牌的能力。某个令牌可能会在整个界面中使用,但终端用户并不想要该令牌。这就意味着应该可以制定一条规则来替换该令牌的所有使用。

示例:

● 某家公司销售具有ROS 2驱动程序的一种通用移动机器人底座

● 该驱动程序中使用很多带有该公司名称的名字UmbrellaCorp

● 另一家公司将该底座集成到公司产品中,而且该公司的客户想要一个ROS 2界面

● 第二家公司不希望他们的界面包含UmbrellaCorp,因此在启动该驱动程序时将UmbrellaCorp令牌重映射为mobile_base

20.4.5 完全限定名称FQN扩展前重映射

本用例可以使ROS 2具有根据名称在代码中的使用方式来匹配名称的能力。这要求在完全限定名称FQN扩展前进行匹配。当两个不同的名称扩展为相同的FQN时,这会很有用。

示例:

● 某个节点使用了cat和/ns/cat这两个名称

● 该节点运行在命名空间/ns/中,因此这两个名称的完全限定名称(FQN)都是/ns/cat

● 某条规则将完全限定名称扩展前的名称cat重映射到lion

● 这样最终名称就分别是/ns/lion和/ns/cat

20.4.6 完全限定名称FQN的准确替换

本用例可以使ROS 2具有通过准确匹配来替换名称的能力。这是ROS 1重映射行为的一部分,因此已被证明是很有用的,并且包含该能力可以简化向ROS 2的迁移。

示例:

● 某个节点使用名称/ns/bar和/ns/barista

● 创建了一条会将/ns/bar重映射为/ns/foo的重映射规则

● 这样最终名称就分别是/ns/foo和/ns/barista

20.4.7 相对名称的准确替换

本用例使ROS 2具有允许用户将相对名称重映射到另一个名称的功能。该功能首先会扩展相对名称,然后再进行FQN替换。这也是ROS 1重映射行为的一部分。

示例:

● 某个节点位于命名空间/ns/中且使用名称bar

● 创建了一条会将bar重映射为foo的规则

● 该节点将名称bar的使用扩展为/ns/bar

● 重映射规则的两侧都扩展为/ns/bar和/ns/foo

● 由于完全限定名称FQN匹配,最终名称就是/ns/foo

20.4.8 通过命令行重映射

用户可以通过命令行提供节点特定的重映射参数。因为一个进程可以包含多个节点,所以必须有一种方法来唯一标识一个进程中的某个节点。这是ROS 1重映射的一项功能。

示例:

● 节点有一个可执行文件/bin/my_node

● 用户想将/cat更改为/dog

● 用户键入命令/bin/my_node /cat:=/dog就可以在命令行中进行重映射

20.4.9 更改默认命名空间

默认命名空间就是相对名称会扩展至的命名空间。默认命名空间应该可以在不影响完全限定名称FQN的情况下进行更改。ROS 1中使用环境变量ROS_NAMESPACE或参数__ns来更改默认命名空间。

示例:

● 节点位于命名空间/ns中并使用相对名称bar

● 用户将默认命名空间更改为/foo

● 这样最终名称就会是/foo/bar

20.4.10 更改节点名称

节点名称可用于日志消息和创建私有名称。ROS 1中使用参数__name来更改节点名称。

示例:

● 节点被命名为camera_driver并使用私有名称camera_info

● 用户将节点名称更改为left_camera_driver

● 这样最终名称就会是/ns/left_camera_driver/camera_info,并且日志消息会使用节点名称left_camera_driver

20.4.11 分别重映射话题和服务名称

本用例使ROS 2具有创建只重映射话题或服务名称的规则的能力。

示例:

● 节点订阅话题/map并提供服务/map

● 用户将话题名称更改为/map_stream

● 这样该节点就会订阅话题/map_stream并提供服务/map

20.5 在ROS 1中重映射名称

重映射也是ROS 1中就有的一项功能。在ROS 1中,是通过将参数传递给每个节点来进行重映射的。客户端库在代码中也有API以在节点初始化时传递重映射规则。重映射规则由两个名称组成:一个应该会用另一个名称替换的名称。

ROS 1重映射是在完全限定名称(FQN)之上工作的。一条规则两侧的名称都会扩展为FQN。在重映射一个名称之前,该名称也会扩展为FQN。仅当该名称与规则左侧名称完全相同时,该名称才会重映射为右侧的名称。

20.6 重映射规则的语法

本节对ROS 2重映射规则的语法提出了一个提案。该提案尽可能保持与ROS 1语法相同。

这个提案语法支持的用例有:

● 重映射进程中的节点

● 更改命名空间

● 更改基本名称

● 完全限定名称FQN的准确替换

● 相对名称的准确替换

● 通过命令行重映射

● 更改默认命名空间

● 更改节点名称

● 分别重映射话题和服务名称

该提案语法不支持以下两个用例:

● 更改令牌

● 完全限定名称FQN扩展前重映射

20.6.1 该语法的工作方式

重映射规则的结构为match:=replacement。其中match部分测试是否应该重映射某个名称;而replacement部分说明新名称会是什么;符号:=的行为与ROS 1中的行为相同。

重映射规则的示例有:

● foo:=bar

● /foo/bar:=fiz/buzz

● nodename:~/foo:=foo

● **/foo:=\1/bar

(1)规则的match部分

重映射规则的match部分可以使用以下运算符:

● 运算符*用于匹配单个令牌

● 运算符**用于匹配由正斜杆分隔的零个或多个令牌

● 前缀rosservice://使规则仅适用于服务

● 前缀rostopic://使规则仅适用于话题

● 前缀nodename:使规则仅适用于具有该名称的节点

运算符*和**类似于bash中的通配行为。运算符**行为类似于其在具有globstar选项设置的 bash>=4.0 中的使用。

URL模式rosservice://和rostopic://只能用于话题或服务名称的重映射规则。它们不能作为节点名称或命名空间替换规则(__name、__node或__ns)的前缀。如果同时给出节点名称前缀和URL模式,则节点名称前缀必须放在首位。

运算符*和**只匹配完整的令牌。*bar看起来好像会匹配foobar,但这意味着仅匹配令牌的一部分。为避免混淆,这两个运算符需要用正斜杆/与令牌、替换、这两个符号彼此分割开来。例如*/bar、**/*、~/*都是允许的,但*bar、***、~*则都是无效的。

匹配仅适用于完全限定名称FQN。当要测试某个名称时,该名称和规则中的替换运算符(~和{})会替换为它们所代表的内容。然后会将该名称扩展为完全限定名称FQN。如果某条重映射规则的match部分不以/、*或**开头,则会用/namespace/作为前缀以使其成为完全限定名称FQN。最后会将该名称与该重映射规则match部分进行比较。如果该名称相匹配,则会被重映射。

(2)规则的replacement部分

下面这些特殊运算符是重映射规则replacement部分所独有的:

● \1 - \9会被运算符*或**的匹配内容所替换

\1到\9的语法取自POSIX BRE中的反向引用,但不使用括号;通配符始终会捕获。

这些引用要求使用正斜杆/与令牌分开。当这样做会创建一个带有//的名称时,会自动删除一个正斜杠。例如:**/bar:=/bar/\1会匹配用**捕获/foo的名称/foo/bar ,而新名称会是/bar/foo。

规则的replacement部分可能没有URL模式。这是为了避免匹配端和替换端的模式类型不相同的问题。

替换运算符(~和{})首先会被替换。之后,引用运算符会被​​替换为相匹配的内容。然后,如果替换名称不是以正斜杆(/)开头,则它会自动以节点的默认命名空间作为前缀,使其成为完全限定名称FQN。最后,该名称会用替换名称进行替换。例如,/bar/*:=\1/bar与具有默认命名空间/ns、用*捕获foo的节点所使用的名称/bar/foo相匹配,且替换名称是/ns/foo/bar。

(3)更改默认命名空间的特殊规则

可以在规则的match部分给出字符串__ns以表示对默认命名空间的更改。在match端,__ns必须单独使用或带有nodename:前缀。规则的replacement端必须有一个完全限定名称FQN,该FQN将会成为新的默认命名空间。

(4)更改节点名称的特殊规则

可以在规则的match部分给出字符串__name或__node以表示对节点名称的更改。在match端,该字符串可以单独使用或带有nodename:前缀。而replacement端必须是单个令牌,该令牌将会成为节点的新名称。

(5)应用重映射规则的顺序

重映射规则按以下顺序进行应用:

1)首先进行节点名称重映射

2)然后进行命名空间重映射

3)最后应用所有其他重映射规则

在上述三个类别内,重映射规则按照用户给出的顺序进行应用。

①话题/服务重映射顺序示例:

● 某个节点使用名称/foo/bar

● 用户给该节点一条重映射规则/*/*:=/asdf,然后给该节点另一条重映射规则/foo/bar:=fizzbuzz

● 最终名称会是/asdf,因为该名称在应用第一条规则进行重映射后,不会与第二条规则相匹配

②节点/命名空间重映射顺序示例:

● 某个节点的名称为talker

● 用户给出一条重映射规则talker:__ns:=/my_namespace,然后给出另一条规则talker:__node:=foo

● 最终的命名空间会是默认的/,因为在命名空间重映射之前会先应用节点名称重映射,也就是说会先应用后一条规则

③默认和节点特定的命名空间重映射示例:

● 某个节点的名称为talker

● 用户给出一条规则talker:__ns:=/foo,然后再给出另一条规则__ns:=/bar

● talker的命名空间会是/foo,这是因为用户首先给出了那条重映射规则

20.6.2 该语法的应用

下面各个小节解释了该语法如何应用于上述各个用例。

(1)支持:重映射进程中的节点

对一个进程中的某个节点进行重映射需要一种唯一标识节点的方法。假设该节点的名称在一个进程中是唯一的,则可以在规则前面加上目标节点名称和符号:。如果在规则中没有用节点名称作前缀,则该规则将会应用于该进程中的所有节点。

示例:

● 一个进程中的多个节点使用名称scan

● 用户给该进程中的某个节点一条重映射规则node1:scan:=scan_filtered

● 这样只有名称为node1的节点会使用该规则

(2)支持:更改命名空间

有两种情况:更改命名空间的一部分和更改整个命名空间。第一种情况需要用通配符来匹配命名空间的其余部分。第二种情况需要用通配符来匹配末尾的基本名称。

1)部分命名空间替换示例:

● 节点使用名称/foo、/foo/bar、/foo/bar/baz

● 给节点一条重映射规则/foo/**:=/fizz/\1

● 重映射后的结果名称将会是/foo、/fizz/bar、/fizz/bar/baz

2)完整命名空间替换示例:

● 节点使用名称/foo/bar/baz、/foo/bar/fee/biz

● 给节点一条重映射规则/foo/bar/*:=/bar/foo/\1

● 重映射后的结果名称将会是/bar/foo/baz、/foo/bar/fee/biz

(3)支持:更改基本名称

更改基本名称需要与整个命名空间相匹配的通配符。通配符**很有用,因为它在与正斜杠组合时可以匹配所有可能的命名空间。

示例:

● 节点使用名称/foo、/buz/foo、/biz/buz/foo

● 给节点重映射规则**/foo:=\1/bar

● 重映射后的结果名称将会是/bar、/buz/bar、/biz/buz/bar

(4)支持:完全限定名称FQN的准确替换

完全限定名称FQN的准确替换不需要通配符。此语法与ROS 1的语法相同。

示例规则:

● /foo/bar:=/fiz/buz

● /foo:=/foo/bar

(5)支持:相对名称的准确替换

相对名称的准确替换也不需要通配符。这意味着相对名称首先会扩展为完全限定名称FQN,然后再进行完全限定名称FQN替换过程中的处理。此语法与ROS 1的语法相同。

示例规则:

● 命名空间/ns中的foo:=/foo/bar等同于/ns/foo:=/foo/bar

● 命名空间/ns中的foo:=bar等同于/ns/foo:=/ns/bar

● 命名空间/ns中的/foo/bar:=foo等同于/foo/bar:=/ns/foo

(6)支持:通过命令行重映射

此处语法可以通过命令行传递给某个节点。选中的语法不会与bash中的特殊shell字符冲突。例如,在bash中字符*仅在被空格包围时才具有特殊行为,而重映射规则不会包含空格。这个字符在其他shell(如zsh)上可能仍然存在困难。

(7)支持:更改默认命名空间

这并不是真正的重映射规则,但其语法相似。在ROS 1中,参数__ns:=可以更改默认命名空间。这里语法是一样的,而且还可以用某个节点的名称作为前缀。replacement端必须有一个没有特殊运算符的完全限定名称FQN。在将任何重映射规则应用于它们之前,所有相对名称都会被扩展到新的命名空间。

示例:

● __ns:=/new/namespace

● node1:__ns:=/node1s/new/namespace

(8)支持:更改节点名称

这也不是真正的重映射规则,但其语法相似。在ROS 1中,参数__name:=可以更改节点名称。这里语法是相同的,而且还可以以节点当前名称作为前缀。参数__node:=具有相同的效果。Replacement端必须有单个令牌。日志消息会立即使用该新名称。在应用任何重映射规则之前,所有私有名称都会被扩展为新名称。

示例:

● __name:=left_camera_driver

● __node:=left_camera_driver

● camera_driver:__name:=left_camera_driver

(9)支持:分别重映射话题和服务名称

在规则的match端指定URL模式会使其专属于某种类型的名称。如果没有给出URL模式,则该规则同时适用于话题和服务名称。

示例:

● rosservice:///foo/bar:=/bar/foo

● rostopic://foo/bar:=bar/foo

● nodename:rosservice://~/left:=~/right

(10)不支持:更改令牌

该语法不能通过一条规则来更改一个令牌的所有使用。用单条规则支持此用例并不是优先事项。

使用两条规则的解决方法:

● 第一条规则重映射命名空间中使用的令牌,例如**/foobar/**:=\1/fizzbuz/\2

● 第二条规则重映射用作基本名称的令牌,例如**/foobar:=\1/fizzbuz

(11)不支持:完全限定名称FQN扩展前重映射

该语法无法指定在完全限定名称FQN扩展之前应该应用某条规则。对此用例没有解决方法。

20.6.3 Fnmatch语法

正在考虑使用fnmatch之类的语法。选中了通配符*字符来匹配fnmatch。因为重映射需要在替换过程中捕获要使用的文本,所以不能使用C函数fnmatch()作为实现。额外的通配符?和[]似乎并不能启用上述更多用例。Fnmatch语法可能会也可能不会匹配带有正斜杠的文本,具体取决于选项FNM_PATHNAME。

20.7 静态与动态重映射

静态重映射在启动节点时给该节点重映射规则。动态重映射是在节点运行时对名称进行重映射的能力。对于已经启动节点并希望将其连接到不同源的开发人员来说,动态重映射可能很有用。由于用户会在由静态规则对其进行重映射后看到某个名称,所以应该在静态规则之后应用动态规则。这样,新规则会匹配用户使用自省工具看到的名称,而不是代码中使用的原始名称。

*英语原文网址:design.ros2.org/article