与 docker 容器中的 ROS2 节点通信时出现问题
Troubles communicating with ROS2 node in docker container
我正在研究ROS2。我有一个 docker 容器,里面安装了 ROS2 foxy。
这个容器安装了很多其他东西,所以我最好处理它而不是从 DockerHub 下载的。
容器基于Ubuntu18.04,而我的主机运行s Ubuntu20.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,运行ning。
如果我在容器中的另一个 /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 套接字。可以吗?
更新:是的,是:
上面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 的进程是来自主机的 运行ning,我不小心切断了 ps.
的输出
更新 3
如果我 运行 docker 没有 --net=host
的容器,那么我的订阅者会看到来自发布者的消息。我负担不起,因为 docker 容器在网络中看不到。
我已将容器中的订阅者替换为 netcat (netcat -l -u 42900
) - 容器中的 netcat 已收到来自在其外部工作的发布者的消息。容器是 运行 和 --net=host
它表明容器中的网络一切正常,但 ROS2 以某种方式不正确地使用它。
如何更正?
Fast-DDS 的最新版本默认带有 SharedMemory 传输。使用 --net=host
意味着两个 DDS 参与者都认为他们在同一台机器上,并且他们尝试使用 SharedMemory 而不是 UDP 进行通信。 Fast-DDS 团队将努力实施一种机制来检测这种情况。同时,我可以给你两个解决方案:
- 使用 XML 在其中一个 DDS 参与者中禁用 SharedMemory 传输。
<?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>
- 在主机和容器之间启用 SharedMemory。为此,您应该分享
/dev/shm
:
docker run -ti --net host -v /dev/shm:/dev/shm <DOCKER_IMAGE>
此外,这两个应用程序应该 运行 具有相同的 UID。在我的例子中,我的 docker 容器的用户是 root
(UID=0)。然后我不得不 运行 主机应用程序作为 root。
我正在研究ROS2。我有一个 docker 容器,里面安装了 ROS2 foxy。
这个容器安装了很多其他东西,所以我最好处理它而不是从 DockerHub 下载的。
容器基于Ubuntu18.04,而我的主机运行s Ubuntu20.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,运行ning。
如果我在容器中的另一个 /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 套接字。可以吗?
更新:是的,是:
上面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 的进程是来自主机的 运行ning,我不小心切断了 ps.
的输出更新 3
如果我 运行 docker 没有
--net=host
的容器,那么我的订阅者会看到来自发布者的消息。我负担不起,因为 docker 容器在网络中看不到。我已将容器中的订阅者替换为 netcat (
netcat -l -u 42900
) - 容器中的 netcat 已收到来自在其外部工作的发布者的消息。容器是 运行 和--net=host
它表明容器中的网络一切正常,但 ROS2 以某种方式不正确地使用它。
如何更正?
Fast-DDS 的最新版本默认带有 SharedMemory 传输。使用 --net=host
意味着两个 DDS 参与者都认为他们在同一台机器上,并且他们尝试使用 SharedMemory 而不是 UDP 进行通信。 Fast-DDS 团队将努力实施一种机制来检测这种情况。同时,我可以给你两个解决方案:
- 使用 XML 在其中一个 DDS 参与者中禁用 SharedMemory 传输。
<?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>
- 在主机和容器之间启用 SharedMemory。为此,您应该分享
/dev/shm
:
docker run -ti --net host -v /dev/shm:/dev/shm <DOCKER_IMAGE>
此外,这两个应用程序应该 运行 具有相同的 UID。在我的例子中,我的 docker 容器的用户是 root
(UID=0)。然后我不得不 运行 主机应用程序作为 root。