9个版本 ()
1.0.0-alpha.6 | 2024年8月6日 |
---|---|
1.0.0-alpha.5 | 2024年7月26日 |
1.0.0-alpha.2 |
|
0.11.0 | 2024年6月4日 |
0.0.0 |
|
#2 in 机器人
272 每月下载量
195KB
3K SLoC
Eclipse Zenoh
Eclipse Zenoh:零开销的Pub/Sub、存储/查询和计算。
Zenoh(发音为/zeno/)统一了运动中的数据、静止数据和计算。它在传统pub/sub和地理分布式存储、查询和计算之间巧妙地结合,同时保持了时间效率和空间效率,远超任何主流堆栈。
DDS插件和独立zenoh-bridge-dds
👉 安装最新版本:见下文如何安装它
👉 Docker镜像:见下文如何获取Docker镜像
👉 构建"main"分支:见下文如何构建它
背景
数据分发服务(DDS)是一种以数据为中心的发布/订阅标准。虽然DDS已经存在很长时间,并在各个行业中有着悠久的部署历史,但最近由于机器人操作系统(ROS 2)的采用而引起了广泛关注——在ROS 2节点之间进行通信时使用。
⚠️ 在与ROS 2一起使用时 ⚠️
此插件基于DDS标准,因此可以在一定程度上与ROS 2协同工作。
然而,我们强烈建议ROS 2用户尝试使用新的 zenoh-plugin-ros2dds
,这是一个专门用于支持ROS 2与DDS的插件。由于与ROS 2概念的更好集成,此新插件具有以下优点:
- 更好地集成ROS图(所有ROS主题/服务/动作都可以在桥接器中查看)
- 更好地支持ROS工具(ros2 CLI,rviz2...)
- 在桥接器上配置ROS命名空间(而不是在每个ROS节点上)
- 将服务和动作作为Zenoh可查询对象,比通过DDS的RPC更高效和可扩展
- 桥接器之间有更多紧凑的发现信息(不会像这样转发所有
ros_discovery_info
消息)
此DDS Zenoh插件最终将因ROS 2使用而弃用。
插件或桥接器?
此软件有两种构建方式可供选择
zenoh-plugin-dds
:一个Zenoh插件——一个可以由Zenoh路由器加载的动态库zenoh-bridge-dds
:一个独立的可执行文件
本文档中描述的功能和配置适用于两者。这意味着本文档其余部分中,“插件”和“桥接器”这两个词可以互换使用。
如何安装它
要安装Zenoh路由器的最新版本的DDS插件或zenoh-bridge-dds
独立可执行文件,您可以按照以下步骤操作:
手动安装(所有平台)
所有发布包都可以从以下位置下载:
每个子目录都包含Rust目标名称。请参阅https://doc.rust-lang.net.cn/stable/rustc/platform-support.html上每个目标对应的平台。
选择您的平台并下载
- 插件文件
zenoh-plugin-dds-<version>-<platform>.zip
然后将它解压缩到与zenohd
相同的目录,或者到任何可以找到插件库的目录(例如/usr/lib) - 独立可执行文件文件
zenoh-bridge-dds-<version>-<platform>.zip
然后将它解压缩到您想要的位置,并运行提取的zenoh-bridge-dds
二进制文件。
Linux Debian
将Eclipse Zenoh私有存储库添加到源列表
echo "deb [trusted=yes] https://download.eclipse.org/zenoh/debian-repo/ /" | sudo tee -a /etc/apt/sources.list > /dev/null
sudo apt update
然后,您可以
- 使用以下命令安装插件:
sudo apt install zenoh-plugin-dds
。 - 使用以下命令安装独立可执行文件:
sudo apt install zenoh-bridge-dds
。
如何构建它
⚠️ 警告 ⚠️ : Zenoh及其生态系统正在积极开发中。当你从git构建时,确保你也从git构建你计划使用的任何其他Zenoh仓库(例如,绑定、插件、后端等)。可能发生的情况是,git中的某些更改与最新的Zenoh打包版本(例如,deb、docker、pip)不兼容。我们在维护Zenoh项目中各个git仓库之间的兼容性上投入了特别多的努力。
⚠️ 警告 ⚠️ : 由于Rust没有稳定的ABI,插件应该使用与
zenohd
相同的Rust版本进行构建,并且使用与zenoh
依赖项相同的版本(或提交号)。否则,在zenohd
和库之间的共享类型内存映射中的不兼容性可能导致"SIGSEV"
崩溃。
为了构建用于DDS的zenoh桥,您首先需要安装以下依赖项
-
Rust。如果您已经安装了Rust工具链,请确保它已经更新到
$ rustup update
-
在Linux上,确保已安装
llvm
和clang
开发包- 在Debian上:
sudo apt install llvm-dev libclang-dev
- 在CentOS或RHEL上:
sudo yum install llvm-devel clang-devel
- 在Alpine上:
apk install llvm11-dev clang-dev
- 在Debian上:
-
CMake(用于构建CycloneDDS,它是一个本地依赖项)
一旦这些依赖项就绪,您可以在您的机器上克隆仓库
$ git clone https://github.com/eclipse-zenoh/zenoh-plugin-dds.git
$ cd zenoh-plugin-dds
$ cargo build --release
独立的可执行二进制文件zenoh-bridge-dds
和插件共享库(在Linux上为*.so
,在Mac OS上为*.dylib
,在Windows上为*.dll
),它们将被动态加载到zenoh路由器zenohd
中,并生成在target/release
子目录中。
启用Cyclone DDS共享内存支持
Cyclone DDS共享内存支持由Iceoryx库提供。Iceoryx引入了额外的系统要求,这些要求在此处有文档说明。
为了构建支持共享内存的DDS的zenoh桥,必须在构建过程中启用dds_shm
可选功能,如下所示
- 插件库
$ cargo build --release -p zenoh-plugin-dds --features dds_shm
- 独立可执行二进制文件
$ cargo build --release -p zenoh-bridge-dds --features dds_shm
注意:当启用dds_shm
功能时,不需要安装Iceoryx来构建桥接器。Iceoryx将作为cargo构建过程的一部分自动下载、编译并静态链接到zenoh桥接器中。
当zenoh桥配置为使用DDS共享内存(请参阅配置)时,必须运行Iceoryx RouDi守护进程(iox-roudi
),以便桥接器能够成功启动。如果未启动,则桥接器将等待一段时间,等待守护进程变得可用,然后超时并终止。
当启用 dds_shm
功能构建 Zenoh 桥接器时,为了方便起见,还会构建 iox-roudi
守护进程。守护进程可以在以下位置找到:target/debug|release/build/cyclors-<hash>/out/iceoryx-build/bin/iox-roudi
。
有关 Cyclone DDS 中共享内存支持的更多详细信息,请参阅此处。
ROS 2 软件包
⚠️ 请考虑使用专为 ROS 2 优化的zenoh-bridge-ros2dds
。
如果您是 ROS 2 用户,您也可以将 zenoh-bridge-dds
作为运行
rosdep install --from-paths . --ignore-src -r -y
colcon build --packages-select zenoh_bridge_dds --cmake-args -DCMAKE_BUILD_TYPE=Release
rosdep
命令将自动安装构建依赖项 Rust 和 clang。
如果您想在 x86 设备上为任何目标交叉编译软件包,可以使用以下命令
rosdep install --from-paths . --ignore-src -r -y
colcon build --packages-select zenoh_bridge_dds --cmake-args -DCMAKE_BUILD_TYPE=Release --cmake-args -DCROSS_ARCH=<target>
其中 <target>
是目标架构(例如,aarch64-unknown-linux-gnu
)。架构列表可以在此处找到。
交叉编译使用 zig
作为链接器。您可以通过此处的说明进行安装。此外,还需要在目标设备上安装 zigbuild
软件包。您可以通过此处的说明进行安装。
Docker 镜像
zenoh-bridge-dds
独立可执行文件也作为适用于 amd64 和 arm64 的 Docker 镜像 提供。要获取它,请执行以下操作:
docker pull eclipse/zenoh-bridge-dds:latest
获取最新版本docker pull eclipse/zenoh-bridge-dds:main
获取主分支版本(夜间构建)
⚠️ 但是,请注意,它的使用仅限于 Linux 上的 Docker 和使用 --net host
选项。
原因在于 DDS 使用 UDP 组播,而 Docker 不支持容器与其主机之间的 UDP 组播(请参阅案例 moby/moby#23659,moby/libnetwork#2397 或 moby/libnetwork#552)。使其工作的唯一已知方法是使用 --net host
选项,该选项仅支持在 Linux 主机上使用。
用法: docker run --init --net host eclipse/zenoh-bridge-dds
它支持与 zenoh-bridge-dds
相同的命令行参数(请参阅下面或使用 -h
参数检查)。
用法
此 Zenoh 插件用于 DDS 的用例多种多样
- DDS系统与Zenoh系统的集成
- 借助zenoh-pico实现DDS系统与嵌入式设备的通信
- 通过Zenoh基础设施(例如一些路由器或桥接器之间的点对点)在各个传输之间桥接不同的DDS系统
- 使用Zenoh路由器将DDS系统扩展到云
- 与其他Zenoh插件(MQTT、ROS 2 ...)或存储技术(InfluxDB、RocksDB)集成的任何技术
配置
zenoh-bridge-dds
可以通过通过-c
参数传递的JSON5文件进行配置。您可以在以下链接中查看此类配置文件的注释示例:DEFAULT_CONFIG.json5
。
此配置文件中相同的"dds"
部分也可以用于Zenoh路由器的配置文件(在其"plugins"
部分中)。路由器将在启动时自动尝试加载插件库(zenoh-plugin_dds
)并应用其配置。
zenoh-bridge-dds
还接受以下参数。如果设置,则每个参数将覆盖配置文件中的类似设置
- 与Zenoh相关的参数
-c, --config <FILE>
: 配置文件-m, --mode <MODE>
: Zenoh会话模式。默认值:peer
可能的值:peer
或client
。
有关更多详细信息,请参阅Zenoh文档。-l, --listen <LOCATOR>
: 此路由器将监听传入会话的定位器。重复此选项以打开多个监听器。定位器示例:tcp/localhost:7447
。-e, --peer <LOCATOR>
: 此路由器将尝试连接到的对等定位器(通常是另一个桥接器或Zenoh路由器)。重复此选项以连接到多个对等点。定位器示例:tcp/<ip-address>:7447
。--no-multicast-scouting
: 禁用允许自动发现Zenoh对等点和路由器的Zenoh侦察协议。-i, --id <hex_string>
: Zenoh桥必须使用的标识符(作为十六进制字符串 - 例如:0A0B23...)。警告:此标识符必须在系统中是唯一的! 如果未设置,将使用随机UUIDv4。--rest-http-port <rest-http-port>
: 设置REST API的http端口(默认:8000)
- DDS相关参数
-
-d, --domain <ID>
: DDS域ID。默认设置为0
,或者如果定义了环境变量"$ROS_DOMAIN_ID"
则设置为该变量。 -
--dds-localhost-only
: 如果设置,则仅在本机接口(127.0.0.1)上发生DDS发现和流量。默认设置为false,除非定义了环境变量“ROS_LOCALHOST_ONLY=1”。 -
--dds-enable-shm
: 如果设置,则配置DDS使用共享内存。此选项有效的前提是桥接程序使用具有“dds_shm”特性的构建。默认设置为false。 -
-f, --fwd-discovery
: 设置后,在发现本地DDS实体时,不是创建本地路由,而是将此发现信息转发到远程插件/桥接程序。这些程序将创建路由,包括发现实体的副本。更多详情请点击此处 -
-s, --scope <String>
: 当映射到zenoh键时,用作DDS流量作用域的前缀的字符串。 -
-a, --allow <String>
: 匹配必须通过zenoh路由的“分区/主题名称”集的正则表达式。默认情况下,允许所有分区和主题。
如果同时设置了“允许”和“拒绝”,则如果分区和/或主题仅匹配“允许”表达式,则允许其通过。
重复此选项以配置多个主题表达式。这些表达式通过“|”连接。.*/TopicA
将仅允许TopicA
路由,无论分区如何。PartitionX/.*
将允许所有主题路由,但仅限于PartitionX
。cmd_vel|rosout
将仅允许名称或分区名称中包含cmd_vel
或rosout
的主题通过。
-
--deny <String>
: 匹配必须不通过zenoh路由的“分区/主题名称”集的正则表达式。默认情况下,不允许任何分区和主题。
如果同时设置了“允许”和“拒绝”,则如果分区和/或主题仅匹配“允许”表达式,则允许其通过。
重复此选项以配置多个主题表达式。这些表达式通过“|”连接。 -
--max-frequency <String>...
: 指定每个主题通过zenoh的数据路由的最大频率。字符串必须具有格式"regex=float"
,其中"regex"
是一个正则表达式,匹配必须以不高于相关最大频率(与--allow选项语法相同)的速度路由数据的“分区/主题名称”集。"float"
是赫兹频率的最大值;如果发布速率更高,则在路由时会进行降采样。
(可多次使用)
-
--queries-timeout <Duration>
:当桥查询其他远程桥以获取发现信息和为它服务的 TRANSIENT_LOCAL DDS 读者历史数据时使用的超时时间(默认:5.0 秒)。如果查询远程桥超出了超时,一些历史样本可能不会路由到读者,但路由不会被永久阻塞。 -
-w, --generalise-pub <String>
:用于泛化 zenoh 发布声明的键表达式列表,从而最小化发现流量(可多次使用)。请参阅此博客获取更多详细信息。 -
-r, --generalise-sub <String>
:用于泛化 zenoh 订阅声明的键表达式列表,从而最小化发现流量(可多次使用)。请参阅此博客获取更多详细信息。
-
管理空间
DDS 的 zenoh 桥提供了一个管理空间,允许浏览已发现的 DDS 实体(及其 QoS),以及已在 DDS 和 zenoh 之间建立的路由。此管理空间可以通过任何 zenoh API 访问,包括您可以在 zenoh-bridge-dds
启动时使用 --rest-http-port
参数激活的 REST API。
从版本 0.11.0-rc.2
开始,zenoh-bridge-dds
使用以 @/<uuid>/dds
为前缀的路径(其中 <uuid>
是桥实例的唯一标识符)来公开此管理空间。然后,信息按这些路径组织
@/<uuid>/dds/version
:桥版本@/<uuid>/dds/config
:桥配置@/<uuid>/dds/participant/<gid>/reader/<gid>/<topic>
:在<topic>
上发现的 DDS 读者@/<uuid>/dds/participant/<gid>/writer/<gid>/<topic>
:在<topic>
上发现的 DDS 读者。@/<uuid>/dds/route/from_dds/<zenoh-resource>
:从 DDS 写入者到名为<zenoh-resource>
的 zenoh 键建立的路径(参见 映射规则)。@/<uuid>/dds/route/to_dds/<zenoh-resource>
:从名为<zenoh-resource>
的 zenoh 键建立的路径(参见 映射规则)。
对于旧版本,请参阅 README.md 的相应版本:[0.10.1-rc](https://github.com/eclipse-zenoh/zenoh-plugin-dds/blob/0.10.1-rc/README.md#admin-space)。
使用 REST API 和 curl
命令行工具对管理空间进行查询的示例(不要忘记使用 --rest-http-port 8000
参数激活 REST API)
- 列出所有已发现的 DDS 实体
curl https://127.0.0.1:8000/@/*/dds/participant/**
- 列出所有建立的路径
curl https://127.0.0.1:8000/@/*/dds/route/**
- 列出主题
cmd_vel
的所有已发现 DDS 实体和建立的路径curl https://127.0.0.1:8000/@/*/dds/**/cmd_vel
技巧:将结果传递到 jq 命令进行 JSON 美化打印或转换。
架构细节
DDS 用的 zenoh 桥 发现 DDS 系统中的所有 DDS 写入者和读者,并将每个主题 T
上的 DDS 发布作为 Zenoh 关键表达式 T
上的 Zenoh 发布路由。相反,假设已发现主题 T
上的 DDS 读者,它将关键表达式 T
上的每个 Zenoh 发布作为主题 T
上的 DDS 发布路由。
桥不会反序列化从 DDS 写入者接收的作为 SerializedPayload
的 DDS 数据,其表示形式在 DDS-RTPS 规范 §10 中定义。因此,任何 Zenoh 应用程序为桥提供的 DDS 读者发布的有效负载必须具有此格式
0...2...........8...............16..............24..............32
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| representation_identifier | representation_options |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~ ~
~ ... Bytes of data representation using a format that ... ~
~ ... depends on the RepresentationIdentifier and options ... ~
~ ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
其中前 4 个字节(representation_identifier 和 representation_options)通常是 Big Endian 编码的 {0x00, 0x0}
或 Little Endian 编码的 {0x00, 0x01}
,其余字节是 CDR 编码的数据。
具体来说,无论是作为库还是作为独立的可执行文件,它都执行相同的功能
-
在默认模式下
- 它通过标准DDS发现协议(使用UDP多播)发现任何DDS应用程序声明的DDS读取器和写入器
- 为每个发现的读取器或写入器创建一个镜像DDS写入器或读取器(使用相同的QoS)
- 将发现的DDS主题和分区映射到zenoh键(见以下映射详情)
- 它将用户数据从DDS主题转发到相应的zenoh键,反之亦然
- 它不会将任何DDS发现信息转发到远程网桥
-
在“转发发现”模式下
- 每个网桥将通过zenoh将本地DDS发现数据转发到远程网桥(比原始DDS发现流量更紧凑的方式)
- 每个通过zenoh接收DDS发现数据的网桥将创建DDS读取器或写入器的副本,具有类似的QoS。这些副本将服务于zenoh的路径,并将被ROS2节点发现。
- 对于ROS 2系统,每个网桥将以比原始发布更少的强度将
ros_discovery_info
数据(在远程网桥上)转发。接收后,远程网桥将原始实体的GIDs转换为相应副本的GIDs,并在DDS上重新发布ros_discovery_info
。然后,每个主机上的ROS 2节点可以发现完整的ROS图。
DDS主题到zenoh键的映射
DDS和zenoh之间的映射相当简单:给定一个没有设置分区QoS的用于主题A
的DDS读取器/写入器,则等效的zenoh键将具有相同的名称:A
。如果定义了分区QoS P
,则等效的zenoh键将命名为P/A
。
可选地,网桥可以配置一个作用域
,该作用域将用作每个zenoh键的前缀。也就是说,对于作用域S
,等效的zenoh键将为:
S/A
,对于没有分区的主题A
S/P/A
,对于主题A
和分区P
将ROS 2名称映射到zenoh键
从ROS 2主题和服务的名称到DDS主题的映射在此处指定。请注意,ROS 2不使用DDS分区。
根据上述映射以及DDS到zenoh的映射,以下是一些从ROS 2名称到zenoh键的映射示例
ROS2名称 | DDS主题名称 | zenoh键(无作用域) | zenoh键(如果作用域=myscope ) |
---|---|---|---|
主题:/rosout |
rt/rosout |
rt/rosout |
myscope/rt/rosout |
主题:/turtle1/cmd_vel |
rt/turtle1/cmd_vel |
rt/turtle1/cmd_vel |
myscope/rt/turtle1/cmd_vel |
服务:/turtle1/set_pen |
rq/turtle1/set_penRequest rr/turtle1/set_penReply |
rq/turtle1/set_penRequest rr/turtle1/set_penReply |
myscope/rq/turtle1/set_penRequest myscope/rr/turtle1/set_penReply |
动作:/turtle1/rotate_absolute |
rq/turtle1/rotate_absolute/_action/send_goalRequest rr/turtle1/rotate_absolute/_action/send_goalReply rq/turtle1/rotate_absolute/_action/cancel_goalRequest rr/turtle1/rotate_absolute/_action/cancel_goalReply rq/turtle1/rotate_absolute/_action/get_resultRequest rr/turtle1/rotate_absolute/_action/get_resultReply rt/turtle1/rotate_absolute/_action/status rt/turtle1/rotate_absolute/_action/feedback |
rq/turtle1/rotate_absolute/_action/send_goalRequest rr/turtle1/rotate_absolute/_action/send_goalReply rq/turtle1/rotate_absolute/_action/cancel_goalRequest rr/turtle1/rotate_absolute/_action/cancel_goalReply rq/turtle1/rotate_absolute/_action/get_resultRequest rr/turtle1/rotate_absolute/_action/get_resultReply rt/turtle1/rotate_absolute/_action/status rt/turtle1/rotate_absolute/_action/feedback |
myscope/rq/turtle1/rotate_absolute/_action/send_goalRequest myscope/rr/turtle1/rotate_absolute/_action/send_goalReply myscope/rq/turtle1/rotate_absolute/_action/cancel_goalRequest myscope/rr/turtle1/rotate_absolute/_action/cancel_goalReply myscope/rq/turtle1/rotate_absolute/_action/get_resultRequest myscope/rr/turtle1/rotate_absolute/_action/get_resultReply myscope/rt/turtle1/rotate_absolute/_action/status myscope/rt/turtle1/rotate_absolute/_action/feedback |
节点turtlesim 的所有参数 |
rq/turtlesim/list_parametersRequest rr/turtlesim/list_parametersReply rq/turtlesim/describe_parametersRequest rr/turtlesim/描述参数回复 rq/turtlesim/获取参数请求 rr/turtlesim/获取参数回复 rr/turtlesim/获取参数类型回复 rq/turtlesim/获取参数类型请求 rq/turtlesim/设置参数请求 rr/turtlesim/设置参数回复 rq/turtlesim/原子设置参数请求 rr/turtlesim/原子设置参数回复 |
rq/turtlesim/list_parametersRequest rr/turtlesim/list_parametersReply rq/turtlesim/describe_parametersRequest rr/turtlesim/描述参数回复 rq/turtlesim/获取参数请求 rr/turtlesim/获取参数回复 rr/turtlesim/获取参数类型回复 rq/turtlesim/获取参数类型请求 rq/turtlesim/设置参数请求 rr/turtlesim/设置参数回复 rq/turtlesim/原子设置参数请求 rr/turtlesim/原子设置参数回复 |
myscope/rq/turtlesim/list_parametersRequest myscope/rr/turtlesim/list_parametersReply myscope/rq/turtlesim/describe_parametersRequest myscope/rr/turtlesim/描述参数回复 myscope/rq/turtlesim/获取参数请求 myscope/rr/turtlesim/获取参数回复 myscope/rr/turtlesim/获取参数类型回复 myscope/rq/turtlesim/获取参数类型请求 myscope/rq/turtlesim/设置参数请求 myscope/rr/turtlesim/设置参数回复 myscope/rq/turtlesim/原子设置参数请求 myscope/rr/turtlesim/原子设置参数回复 |
特定的ROS发现话题 | ros发现信息 |
ros发现信息 |
myscope/ros发现信息 |
依赖项
~43–57MB
~1M SLoC