与 docker 容器中的 ROS2 节点通信时出现问题



我正在学习ROS2。我有一个 docker 容器,里面安装了 ROS2 foxy。

这个容器安装了许多其他东西,所以我最好处理它而不是从 DockerHub 下载的那些。

该容器基于 Ubuntu 18.04,我的主机运行 Ubuntu 20.04。

以下操作不起作用:

在主机上:$ docker run --net host -it <container name>

容器内:

# env | grep ROS_
ROS_DOMAIN_ID=142
ROS_VERSION=2
ROS_LOCALHOST_ONLY=0
ROS_PYTHON_VERSION=3
ROS_DISTRO=foxy

# ros2 run examples_rclpy_minimal_publisher publisher_local_function
[INFO] [1611658788.451254349] [minimal_publisher]: Publishing: "Hello World: 0"
[INFO] [1611658788.930325228] [minimal_publisher]: Publishing: "Hello World: 1"
[INFO] [1611658789.430629464] [minimal_publisher]: Publishing: "Hello World: 2"
...

在另一个终端的同一主机上:

$ source /opt/ros/foxy/setup.zsh
$ export ROS_DOMAIN_ID=142
$ env | grep ROS_
ROS_DISTRO=foxy
ROS_LOCALHOST_ONLY=0
ROS_PYTHON_VERSION=3
ROS_VERSION=2
ROS_DOMAIN_ID=142

$ ros2 run examples_rclpy_minimal_subscriber subscriber_member_function

订阅者没有输出。

同时,我看到打开的UDP端口:

$ sudo netstat -unlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
udp        0      0 0.0.0.0:35379           0.0.0.0:*                           2103557/python3
udp        0      0 127.0.0.1:41750         0.0.0.0:*                           1867221/python3
udp        0      0 0.0.0.0:42900           0.0.0.0:*                           2103557/python3
udp        0      0 0.0.0.0:42900           0.0.0.0:*                           1867221/python3
udp        0      0 0.0.0.0:42912           0.0.0.0:*                           2103557/python3
udp        0      0 0.0.0.0:42913           0.0.0.0:*                           2103557/python3
udp        0      0 0.0.0.0:42916           0.0.0.0:*                           1867221/python3
udp        0      0 0.0.0.0:42917           0.0.0.0:*                           1867221/python3
udp        0      0 127.0.0.1:47375         0.0.0.0:*                           2103557/python3

以 186xxxx 开头的 PID 属于主机上的ros2_daemon,以 210xxxx 开头的 PID 属于 python,在容器中运行。

如果我在容器中的另一个/bin/bash中执行订阅者,它会起作用,也就是说,订阅者打印它从发布者收到的消息。

组播 UDP 数据报也有效:

在容器中:

# ros2 multicast receive
Waiting for UDP multicast datagram...
Received from 106.xxx.xxx.xxx:45829: 'Hello World!'

在主机上:

$ ros2 multicast send
Sending one UDP multicast datagram...

更新。 我试过拉标准容器osrf/ros:foxy-desktop...示例按预期工作。

容器中的发布者:

$ docker pull osrf/ros:foxy-desktop
$ docker run --net host -it osrf/ros:foxy-desktop
# export ROS_DOMAIN_ID=142
# env | grep ROS_
ROS_VERSION=2
ROS_PYTHON_VERSION=3
ROS_DOMAIN_ID=142
ROS_LOCALHOST_ONLY=0
ROS_DISTRO=foxy
#ros2 run examples_rclpy_minimal_publisher publisher_local_function
[INFO] [1611670054.887068490] [minimal_publisher]: Publishing: "Hello World: 0"
[INFO] [1611670055.367854925] [minimal_publisher]: Publishing: "Hello World: 1"
...

主机上的订阅者:

$ ros2 run examples_rclpy_minimal_subscriber subscriber_member_function
[INFO] [1611670073.075589355] [minimal_subscriber]: I heard: "Hello World: 7"
[INFO] [1611670073.540520496] [minimal_subscriber]: I heard: "Hello World: 8"
[INFO] [1611670074.040020703] [minimal_subscriber]: I heard: "Hello World: 9"
...

更新 2

回到原始容器。我在netstat中看到两个端口号为7400的UDP套接字。可以吗?

更新:是的,它是:https://stackoverflow.com/a/1694148

在上面的 netstat 输出中观察到了相同的现象,但端口号不同。

$ sudo netstat -unlp
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
...
udp        0      0 0.0.0.0:39604           0.0.0.0:*                           2319288/python3
udp        0      0 0.0.0.0:7400            0.0.0.0:*                           2319288/python3
udp        0      0 0.0.0.0:7400            0.0.0.0:*                           2319267/python3
udp        0      0 0.0.0.0:7412            0.0.0.0:*                           2319267/python3
...

和流程:

$ ps axf
...
2319287 pts/4    S+     0:00      _ /usr/bin/python3 /opt/ros/foxy/bin/ros2 run examples_rclpy_minimal_publisher publisher_local_function
2319288 pts/4    Sl+    0:01          _ /usr/bin/python3 /opt/ros/foxy/lib/examples_rclpy_minimal_publisher/publisher_local_function
...
2319050 ?        Sl     0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id ae2da482416
2319075 pts/0    Ss+    0:00  _ /bin/bash
2319266 pts/0    S      0:00      _ /usr/bin/python3 /root/git/ros2_foxy/install/bin/ros2 run examples_rclpy_minimal_subscriber subscriber_member_function
2319267 pts/0    Sl     0:00          _ /usr/bin/python3 /root/git/ros2_foxy/install/lib/examples_rclpy_minimal_subscriber/subscriber_member_function

ID 2319288的进程正在从主机运行,我不小心切断了 ps 的输出。

更新 3

  1. 如果我在没有--net=host的情况下运行 docker 容器,那么我的订阅者会看到来自发布者的消息。我负担不起,因为在网络中看不到 docker 容器。

  2. 我已经将容器中的订阅者替换为 netcat (netcat -l -u 42900) - 容器中的 netcat 收到了来自在其外部工作的发布者的消息。容器使用--net=host运行

这表明容器中的网络一切正常,但 ROS2 以某种方式错误地使用它。

我该如何纠正它?

默认情况下,Fast-DDS 的最新版本带有共享内存传输。使用--net=host意味着两个DDS参与者都认为他们在同一台机器上,并且他们尝试使用共享内存而不是UDP进行通信。Fast-DDS 团队将努力实施一种机制来检测这种情况。同时,我可以给你两个解决方案:

  1. 使用 XML 禁用其中一个 DDS 参与者中的共享内存传输。
<?xml version="1.0" encoding="UTF-8" ?>
<profiles xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles" >
<transport_descriptors>
<transport_descriptor>
<transport_id>CustomUdpTransport</transport_id>
<type>UDPv4</type>
</transport_descriptor>
</transport_descriptors>
<participant profile_name="participant_profile" is_default_profile="true">
<rtps>
<userTransports>
<transport_id>CustomUdpTransport</transport_id>
</userTransports>
<useBuiltinTransports>false</useBuiltinTransports>
</rtps>
</participant>
</profiles>
  1. 在主机和容器之间启用共享内存。为此,您应该分享/dev/shm
docker run -ti --net host -v /dev/shm:/dev/shm <DOCKER_IMAGE>

此外,这两个应用程序应使用相同的 UID 运行。就我而言,我的 docker 容器的用户是root(UID=0)。然后我必须以 root 身份运行主机应用程序。

我也遇到了这个问题,但无法提出修复方法,但有一些解决方法。该错误似乎源于Fast-DDS在使用--net=host选项时尝试使用SHM。该问题也被多次描述,例如 https://github.com/eProsima/Fast-DDS/issues/3475 https://github.com/eProsima/Fast-DDS/issues/2956

在容器内和主机上的 ROS 2 节点之间使用 SHM 的解决方法是:

  1. 使用以下选项启动容器:--network=host --ipc=host --pid=host
  2. 使用与主机上的节点相同的用户 ID 在容器内运行节点

为方便起见,我创建了一个存储库来构建(作为 root 用户)并在容器内运行(使用主机的用户 ID)ROS 2 包/节点。这允许在容器内和主机上运行的 ROS 2 节点之间使用共享内存传输 (SHM)。

最新更新