At Husarnet, we’re streamlining ROS 2 networking across the Internet to be as easy and reliable as possible.
We're introducing the new husarnet/ros2router
Docker image, which allows you to easily bridge ROS 2 nodes running across different robots, servers, or laptops in various networks. It features built-in topic filtering, enabling you to decide in real-time which ROS 2 interface should be exposed to remote hosts.
There's no need to change your DDS settings. Simply run our Docker image alongside your existing ROS 2 application, regardless of whether it's running on a host or inside Docker containers.
Discover how to integrate husarnet/ros2router
with your robots, computers, or multi-tenant cloud applications and understand how it works in the following sections.
Quick start
Let's jump straight into the essentials. Suppose you want to connect your laptop and robot over the Internet. Here's the briefest guide to get you started:
1. Install Husarnet Client on Each Host
Execute the following command to install the Husarnet Client:
curl -s https://install.husarnet.com/install.sh | sudo bash
Then, set your Join Code as an environment variable (retrieve your Join Code from the online dashboard):
export JOINCODE=fc94:b01d:1803:8dd8:b293:5c7d:7639:932a/XXXXXXXXXXXXXXXXXXXXXX
Now, add your device to the Husarnet network with:
sudo husarnet join $JOINCODE my-laptop # or my-robot - choose the easy to remember hostname
2. Create filter.yaml
on Each Host
You need a file to specify which ROS 2 topics are allowed. Only these topics will be permitted, all others will be blocked. Here's how you set up filter.yaml
:
allowlist:
- name: "rt/chatter"
type: "std_msgs::msg::dds_::String_"
blocklist: []
builtin-topics: []
3. Start the husarnet/ros2router
on Each Host
Choose the appropriate version for ROS 2 Humble or ROS 2 Iron and run the following Docker command:
- ROS 2 Humble
- ROS 2 Iron
docker run --rm -d \
--name ros2router \
--net host \
--volume $(pwd)/filter.yaml:/filter.yaml \
--env ROS_LOCALHOST_ONLY=1 \
husarnet/ros2router:1.3.0
docker run --rm -d \
--name ros2router \
--net host \
--volume $(pwd)/filter.yaml:/filter.yaml \
--env IGNORE_PARTICIPANTS_FLAGS=filter_different_host \
husarnet/ros2router:1.3.0
4. Run Your ROS 2 Nodes
To keep ROS 2 traffic local to your Husarnet network (which includes your laptop and robot), set the environment variables:
- ROS 2 Humble
- ROS 2 Iron
export ROS_LOCALHOST_ONLY=1
export ROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST
export ROS_STATIC_PEERS=127.0.0.1
Then launch any ROS 2 nodes you like. For example, use the talker/listener
demo from the demo_nodes_cpp
ROS 2 package.
On host 1 (laptop), execute:
ros2 run demo_nodes_cpp talker
And on host 2 (robot), run:
ros2 run demo_nodes_cpp listener
5. Enjoy the Secure, Peer-to-Peer Connection
All done! Your ROS 2 devices are now connected over the Internet with traffic limited to those on the same Husarnet network. Additionally, you've successfully set up topic filtering to allow only the /chatter
topic. Enjoy your secure, peer-to-peer ROS 2 setup.
What is ROS 2 Router?
A ROS 2 Router, also known as DDS Router, is an open source tool developed by eProsima as a solution to connect disparate DDS (Data Distribution Service) networks. This connectivity means you could, for instance, bridge a DDS network that relies on simple multicast discovery (the default in ROS 2) with a DDS network that operates using a Discovery Server setup or has a different ROS_DOMAIN_ID
.
This flexibility is crucial in designing ROS 2 systems because it removes the limitation of having a uniform DDS configuration across your entire robotic fleet.
The router works by setting up separate participants for each DDS network you want to interconnect, with specific configurations for each.
An important feature of the ROS 2 Router is its ability to filter topics, giving you the capability to control which ROS 2 topics are visible or shared between networks. This ensures that only necessary information, like a robot's position, is available for monitoring, while sensitive or potentially harmful commands, such as /cmd_vel
, remain private and secure.
Installation
Here’s how to install ROS 2 Router on a fresh Ubuntu 22.04 system, referencing the official guidelines:
apt update && apt install -y \
python3 \
cmake g++ pip wget git \
libyaml-cpp-dev \
libasio-dev libtinyxml2-dev
pip3 install -U \
colcon-common-extensions \
vcstool
mkdir -p ~/DDS-Router/src && cd ~/DDS-Router
wget https://raw.githubusercontent.com/eProsima/DDS-Router/main/ddsrouter.repos
vcs import src < ddsrouter.repos
colcon build
source ~/DDS-Router/install/setup.bash >> ~/.bashrc
Creating the Configuration File
Next, you'll want to create a configuration file for the ROS 2 Router:
version: v3.0
allowlist:
- name: "rt/chatter"
type: "std_msgs::msg::dds_::String_"
blocklist: []
builtin-topics: []
participants:
- name: SimpleParticipant
kind: local
domain: 0
transport: udp
ignore-participant-flags: no_filter
whitelist-interfaces:
- 127.0.0.1
- name: RemoteParticipant
kind: initial-peers
listening-addresses:
- ip: fc94:1a20:95ec:f20a:02d3:9971:6f74:9874
port: 11811
connection-addresses:
- ip: fc94:b01d:18a3:8dc8:abd3:5c7d:7639:932a
port: 11811
- ip: fc94:4fef:2f42:4efe:e79a:d97d:af0a:d474
port: 11811
- ip: fc94:5fa1:c3f3:3abc:a87d:7485:23c4:ab45
port: 11811
This configuration includes:
- A
SimpleParticipant
for the local, multicast-based DDS network typically used in ROS 2, limited to localhost (127.0.0.1
). - A
RemoteParticipant
with a set of specified peers' Husarnet IPv6 addresses, to establish connections with remote DDS networks.
The allowlist
and blocklist
sections control which topics the ROS 2 Router can forward or should reject.
For more detailed information on the configuration file format, the official documentation is a helpful resource.
Running the ROS 2 Router
To start the ROS 2 Router, simply execute:
ddsrouter -c ./ros2router-config.yaml
Streamlining ROS 2 Router Configuration with Husarnet
Managing a ROS 2 Router configuration file can be cumbersome and prone to errors. To address this, we’ve created a set of scripts for automation within an accessible husarnet/ros2router
Docker image.
Access the source code and releases for the husarnet/ros2router
at our GitHub repository: Husarnet ROS 2 Router on GitHub.
This Docker image automates both the Husarnet and local configurations using environmental variables you provide. It interfaces with the Husarnet Client through its HTTP API.
Installing Husarnet
Before deploying the container, ensure Husarnet is installed on your devices (refer to the Husarnet installation guide):
curl -s https://install.husarnet.com/install.sh | sudo bash
Set your Join Code as an environment variable (obtain it from the Husarnet Dashboard):
export JOINCODE='your-join-code-here'
Join your device to the Husarnet network with:
sudo husarnet join $JOINCODE my-laptop # or my-robot - choose the easy to remember hostname
Integrating Devices from Different Husarnet Groups
For devices under the same Husarnet account but in different networks, consolidate them using the Online Dashboardor the Husarnet Dashboard CLI:
husarnet dashboard login
husarnet dashboard add my-robot my-network
Creating filter.yaml
file
Implement topic filtering by creating a filter.yaml
file with the desired allowlist
, blocklist
, and builtin-topics
, such as:
allowlist:
- name: "rt/chatter"
type: "std_msgs::msg::dds_::String_"
blocklist: []
builtin-topics: []
Launching husarnet/ros2router
Docker Conatiner
Mount the filter.yaml
file and run the Docker container:
docker run --rm -d \
--name ros2router \
--net host \
--volume $(pwd)/filter.yaml:/filter.yaml \
--env ROS_LOCALHOST_ONLY=1 \
husarnet/ros2router:1.3.0
The filter.yaml
is dynamically merged with the auto-generated ROS 2 Router config, allowing you to modify it at runtime. Deploy the specified Docker image on each device you wish to interconnect, and it will bridge the local DDS networks across these devices automatically.
Proceed to operate your ROS 2 nodes on your host in the usual manner. For instance, you can use the talker/listener demo from the demo_nodes_cpp package in ROS 2 Humble.
For the first host, enter:
# This ensures that ROS 2 communication is routed
# through the local DDS Router and not across the LAN
# if your hosts share the same local network.
export ROS_LOCALHOST_ONLY=1
ros2 run demo_nodes_cpp talker
On the second host, use:
export ROS_LOCALHOST_ONLY=1
ros2 run demo_nodes_cpp listener
Now, regardless of whether your hosts are situated in different local networks or on separate continents, you will observe that the ROS 2 traffic between them is seamlessly bridged.
Modes
The husarnet/ros2router
Docker image serves as a versatile utility for a variety of configurations, catering to both on-host ROS 2 nodes and ROS 2 nodes within Docker environments.
With an auto-configured Husarnet setup, you can link remote hosts using either Initial Peers
or Discovery Server
Configurations:
Initial Peers setup: This configuration injects all detected Husarnet peer IPv6 addresses into the
connection-addresses
field within the Initial Peers (WAN) Participant. Optimal for straightforward ROS 2 networks, this mode supports an all-to-all communication pattern and is quite straightforward to implement.Discovery Server setup: This configuration initializes a Discovery Server, enabling the Husarnet participant to function as a client, a server, or both concurrently. It effectively establishes a Local Discovery Server Participant, scaling better by minimizing discovery traffic.
Below you'll find detailed examples on how to execute both setups, including the necessary environment variables.
Initial Peers setup
Create a run.sh
script with the following content:
#!/bin/bash
if [[ "$ROS_DISTRO" == "iron" ]]; then
echo "ROS_DISTRO is set to iron"
docker run --rm -d \
--name ros2router \
--net host \
--env IGNORE_PARTICIPANTS_FLAGS=filter_different_host \
husarnet/ros2router:1.3.0
export ROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST
export ROS_STATIC_PEERS=127.0.0.1
else
echo "ROS_DISTRO is set to humble"
docker run --rm -d \
--name ros2router \
--net host \
--env ROS_LOCALHOST_ONLY=1 \
husarnet/ros2router:1.3.0
export ROS_LOCALHOST_ONLY=1
fi
# Run the corresponding ROS2 node based on the argument
ros2 run demo_nodes_cpp "$1"
Execute on the first ROS 2 host:
./run.sh talker
On the second host:
./run.sh listener
Discovery Server setup
- Server
- Client
Create a compose.client.yaml
file with the following content for the client setup:
#!/bin/bash
if [[ "$ROS_DISTRO" == "iron" ]]; then
echo "ROS_DISTRO is set to iron"
docker run --rm -d \
--name ros2router \
--net host \
--env ROS_DISCOVERY_SERVER=my-robot:11811 \
--env DISCOVERY_SERVER_ID=10 \
--env IGNORE_PARTICIPANTS_FLAGS=filter_different_host \
husarnet/ros2router:1.3.0
export ROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST
export ROS_STATIC_PEERS=127.0.0.1
else
echo "ROS_DISTRO is set to humble"
docker run --rm -d \
--name ros2router \
--net host \
--env ROS_DISCOVERY_SERVER=my-robot:11811 \
--env DISCOVERY_SERVER_ID=10 \
--env ROS_LOCALHOST_ONLY=1 \
husarnet/ros2router:1.3.0
export ROS_LOCALHOST_ONLY=1
fi
# Run the corresponding ROS2 node based on the argument
ros2 run demo_nodes_cpp "$1"
Run it with:
./run_client.sh listener
Create a compose.client.yaml
file with the following content for the server setup:
#!/bin/bash
if [[ "$ROS_DISTRO" == "iron" ]]; then
echo "ROS_DISTRO is set to iron"
docker run --rm -d \
--name ros2router \
--net host \
--env DISCOVERY_SERVER_LISTENING_PORT=11811 \
--env DISCOVERY_SERVER_ID=0 \
--env IGNORE_PARTICIPANTS_FLAGS=filter_different_host \
husarnet/ros2router:1.3.0
export ROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST
export ROS_STATIC_PEERS=127.0.0.1
else
echo "ROS_DISTRO is set to humble"
docker run --rm -d \
--name ros2router \
--net host \
--env DISCOVERY_SERVER_LISTENING_PORT=11811 \
--env DISCOVERY_SERVER_ID=0 \
--env ROS_LOCALHOST_ONLY=1 \
husarnet/ros2router:1.3.0
export ROS_LOCALHOST_ONLY=1
fi
# Run the corresponding ROS2 node based on the argument
ros2 run demo_nodes_cpp "$1"
Run it with:
./run_server.sh talker
Exploring Various Setups for Discovery Server with husarnet/ros2router
The husarnet/ros2router
Docker image is versatile, allowing ROS 2 nodes to operate in various environments: inside Docker containers, directly on the host operating system, or both. Configurations may vary depending on whether you use UDP connections between ROS 2 nodes and ROS 2 Router, or opt for a more efficient shared memory approach. Here we will examine different scenarios and configurations.
Focusing on the Discovery Server setup, let's look at the examples below.
Docker: UDP Setup
For Docker containers using a UDP connection:
- Server
- Client
services:
ros2router:
image: husarnet/ros2router:1.3.0
network_mode: host
environment:
- DISCOVERY_SERVER_LISTENING_PORT=11811
- DISCOVERY_SERVER_ID=0
- WHITELIST_INTERFACES=172.28.0.1
talker:
image: husarion/ros2-demo-nodes:humble
networks:
- network_talker
command: ros2 run demo_nodes_cpp talker
networks:
network_talker:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/16
To run:
docker compose -f compose.server.yaml up
services:
ros2router:
image: husarnet/ros2router:1.3.0
network_mode: host
environment:
- ROS_DISCOVERY_SERVER=my-robot:11811
- DISCOVERY_SERVER_ID=10
- WHITELIST_INTERFACES=172.29.0.1
listener:
image: husarion/ros2-demo-nodes:humble
command: ros2 run demo_nodes_cpp listener
networks:
default:
ipam:
config:
- subnet: 172.29.0.0/24
To run:
docker compose -f compose.client.yaml up
Utilizing the Default docker0 Network Interface
Rather than setting up a bespoke Docker network with a specific subnet for the purpose of appending to the WHITELIST_INTERFACES
, the default docker0
network interface, which typically has the gateway address 172.17.0.1
, can be employed.
Here is how you can set it up:
- Server
- Client
services:
ros2router:
image: husarnet/ros2router:1.2.0
network_mode: host
environment:
- DISCOVERY_SERVER_LISTENING_PORT=11811
- DISCOVERY_SERVER_ID=0
- WHITELIST_INTERFACES=172.17.0.1 # docker0 IP address
talker:
image: husarion/ros2-demo-nodes:humble
network_mode: bridge # using docker0 network interface
command: ros2 run demo_nodes_cpp talker
services:
ros2router:
image: husarnet/ros2router:1.2.0
network_mode: host
environment:
- ROS_DISCOVERY_SERVER=my-robot:11811
- DISCOVERY_SERVER_ID=10
- WHITELIST_INTERFACES=172.17.0.1 # docker0 IP address
listener:
image: husarion/ros2-demo-nodes:humble
network_mode: bridge # using docker0 network interface
command: ros2 run demo_nodes_cpp listener
To ensure 172.17.0.1
is indeed your docker0
interface's gateway address, execute the following command:
ip addr show docker0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1
This command extracts the IP address of the docker0
interface, which you can then verify against the gateway address specified in your Docker service configuration.
Docker: Shared Memory Setup
For a Docker setup with shared memory communication:
- Server
- Client
services:
ros2router:
image: husarnet/ros2router:1.3.0
network_mode: host
ipc: host
environment:
- LOCAL_TRANSPORT=builtin
- DISCOVERY_SERVER_LISTENING_PORT=11811
- DISCOVERY_SERVER_ID=0
- ROS_LOCALHOST_ONLY=1
talker:
image: husarion/ros2-demo-nodes:humble
network_mode: host
ipc: host
environment:
- ROS_LOCALHOST_ONLY=1
command: ros2 run demo_nodes_cpp talker
To run:
docker compose -f compose.server.yaml up
services:
ros2router:
image: husarnet/ros2router:1.3.0
network_mode: host
ipc: host
environment:
- LOCAL_TRANSPORT=builtin
- ROS_DISCOVERY_SERVER=my-robot:11811
- DISCOVERY_SERVER_ID=10
- ROS_LOCALHOST_ONLY=1
talker:
image: husarion/ros2-demo-nodes:humble
network_mode: host
ipc: host
environment:
- ROS_LOCALHOST_ONLY=1
command: ros2 run demo_nodes_cpp listener
To run:
docker compose -f compose.client.yaml up
Host: UDP setup
For running directly on the host with a UDP connection:
- Server
- Client
services:
ros2router:
image: husarnet/ros2router:1.3.0
network_mode: host
environment:
- DISCOVERY_SERVER_LISTENING_PORT=11811
- DISCOVERY_SERVER_ID=0
- ROS_LOCALHOST_ONLY=1 # use it with ROS 2 Humble
# - IGNORE_PARTICIPANTS_FLAGS=filter_different_host # use it with ROS 2 Iron
To run:
docker compose -f compose.server.yaml up -d
export ROS_LOCALHOST_ONLY=1
# Run ROS2 listener
ros2 run demo_nodes_cpp talker
services:
ros2router:
image: husarnet/ros2router:1.3.0
network_mode: host
environment:
- ROS_DISCOVERY_SERVER=my-robot:11811
- DISCOVERY_SERVER_ID=10
# - ROS_LOCALHOST_ONLY=1 # use it with ROS 2 Humble
- IGNORE_PARTICIPANTS_FLAGS=filter_different_host # use it with ROS 2 Iron
To run:
docker compose -f compose.client.yaml up -d
export ROS_LOCALHOST_ONLY=1
# Run ROS2 listener
ros2 run demo_nodes_cpp listener
Host: Shared Memory setup
For a shared memory setup directly on the host:
- Server
- Client
services:
ros2router:
image: husarnet/ros2router:1.3.0
network_mode: host
ipc: host
pid: host
user: ${MY_UID:-1000}:${MY_GID:-1000}
volumes:
- /etc/group:/etc/group:ro
- /etc/passwd:/etc/passwd:ro
- /etc/shadow:/etc/shadow:ro
environment:
- LOCAL_TRANSPORT=builtin
- DISCOVERY_SERVER_LISTENING_PORT=11811
- DISCOVERY_SERVER_ID=0
- ROS_LOCALHOST_ONLY=1 # use it with ROS 2 Humble
# - IGNORE_PARTICIPANTS_FLAGS=filter_different_host # use it with ROS 2 Iron
To run:
export MY_UID=$(id -u)
export MY_GID=$(id -g)
docker compose -f compose.server.yaml up -d
export ROS_LOCALHOST_ONLY=1
# Run ROS2 listener
ros2 run demo_nodes_cpp talker
services:
ros2router:
image: husarnet/ros2router:1.3.0
network_mode: host
ipc: host
pid: host
user: ${MY_UID:-1000}:${MY_GID:-1000}
volumes:
- /etc/group:/etc/group:ro
- /etc/passwd:/etc/passwd:ro
- /etc/shadow:/etc/shadow:ro
environment:
- LOCAL_TRANSPORT=builtin
- ROS_DISCOVERY_SERVER=my-robot:11811
- DISCOVERY_SERVER_ID=10
# - ROS_LOCALHOST_ONLY=1 # use it with ROS 2 Humble
- IGNORE_PARTICIPANTS_FLAGS=filter_different_host # use it with ROS 2 Iron
To run:
export MY_UID=$(id -u)
export MY_GID=$(id -g)
docker compose -f compose.client.yaml up -d
export ROS_LOCALHOST_ONLY=1
# Run ROS2 listener
ros2 run demo_nodes_cpp listener
Enabling Exclusive Shared Memory Communication
The configuration outlined below ensures that ROS 2 nodes communicate solely via shared memory, bypassing UDP transport. This requires a precise setup using a custom DDS XML configuration file:
Create a file named localhost-shm-only.xml
with the following content:
<?xml version="1.0" encoding="UTF-8" ?>
<profiles xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles">
<transport_descriptors>
<transport_descriptor>
<transport_id>shm_transport</transport_id>
<type>SHM</type>
</transport_descriptor>
</transport_descriptors>
<participant profile_name="SHMParticipant" is_default_profile="true">
<rtps>
<userTransports>
<transport_id>shm_transport</transport_id>
</userTransports>
<useBuiltinTransports>false</useBuiltinTransports>
</rtps>
</participant>
</profiles>
With this configuration, there's no need for ROS_LOCALHOST_ONLY
or IGNORE_PARTICIPANTS_FLAGS
environmental variables, as the local transport is set exclusively to shared memory, ensuring communication is localized to the host.
- Server
- Client
services:
ros2router:
image: husarnet/ros2router:1.3.0
network_mode: host
ipc: host
pid: host
user: ${MY_UID:-1000}:${MY_GID:-1000}
volumes:
- /etc/group:/etc/group:ro
- /etc/passwd:/etc/passwd:ro
- /etc/shadow:/etc/shadow:ro
environment:
- LOCAL_TRANSPORT=shm
- DISCOVERY_SERVER_LISTENING_PORT=11811
- DISCOVERY_SERVER_ID=0
To deploy the ROS 2 talker
node through ROS 2 Router:
export MY_UID=$(id -u)
export MY_GID=$(id -g)
docker compose -f compose.server.yaml up -d
export FASTRTPS_DEFAULT_PROFILES_FILE=$(pwd)/localhost-shm-only.xml
# Run ROS2 talker
ros2 run demo_nodes_cpp talker
services:
ros2router:
image: husarnet/ros2router:1.3.0
network_mode: host
ipc: host
pid: host
user: ${MY_UID:-1000}:${MY_GID:-1000}
volumes:
- /etc/group:/etc/group:ro
- /etc/passwd:/etc/passwd:ro
- /etc/shadow:/etc/shadow:ro
environment:
- LOCAL_TRANSPORT=shm
- ROS_DISCOVERY_SERVER=my-robot:11811
- DISCOVERY_SERVER_ID=10
To deploy the ROS 2 listener
node through ROS 2 Router:
export MY_UID=$(id -u)
export MY_GID=$(id -g)
docker compose -f compose.client.yaml up -d
export FASTRTPS_DEFAULT_PROFILES_FILE=$(pwd)/localhost-shm-only.xml
# Run ROS2 listener
ros2 run demo_nodes_cpp listener
This localhost-shm-only.xml
must be applied to every participant across all machines within the Husarnet network. Detailed instructions can be found in the DDR Router and Fast DDS documentation.
Host + Docker: Mixed Setup
For a mixed environment where some nodes run in Docker and some on the host:
- Server
- Client
services:
ros2router:
image: husarnet/ros2router:1.3.0
network_mode: host
environment:
- DISCOVERY_SERVER_LISTENING_PORT=11811
- DISCOVERY_SERVER_ID=0
- WHITELIST_INTERFACES=${DOCKER0_IP:-172.17.0.1};127.0.0.1
talker:
image: husarion/ros2-demo-nodes:humble
network_mode: bridge # using docker0 network interface
command: ros2 run demo_nodes_cpp talker
To run:
export DOCKER0_IP=$(ip addr show docker0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)
nohup docker compose -f compose.server.yaml up
export ROS_LOCALHOST_ONLY=1
# just to make a time difference between two talkers
sleep 10
# Run ROS2 listener
ros2 run demo_nodes_cpp talker
services:
ros2router:
image: husarnet/ros2router:1.3.0
network_mode: host
environment:
- ROS_DISCOVERY_SERVER=my-robot:11811
- DISCOVERY_SERVER_ID=10
- WHITELIST_INTERFACES=${DOCKER0_IP:-172.17.0.1};127.0.0.1
listener:
image: husarion/ros2-demo-nodes:humble
network_mode: bridge # using docker0 network interface
command: ros2 run demo_nodes_cpp listener
To run:
export DOCKER0_IP=$(ip addr show docker0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)
docker compose -f compose.client.yaml up -d
export ROS_LOCALHOST_ONLY=1
# Run ROS2 listener
ros2 run demo_nodes_cpp listener
Enhanced Client Connection Methods
The husarnet/ros2router
in conjunction with a Discovery Server configuration offers a streamlined approach. It's important to note that deploying husarnet/ros2router
on every host isn't necessary. For instance, each robot in your fleet can host its own husarnet/ros2router
Docker container with the server setup, while your laptop or cloud system need not run it.
We will delve into how to employ the ROS_DISCOVERY_SERVER
environment variable or create a custom XML file for a local DDS with SUPER_CLIENT
settings in the upcoming sections.
Utilizing ROS_DISCOVERY_SERVER
Env (Specific to ROS Iron)
For users of ROS 2 Iron, simply setting the ROS_DISCOVERY_SERVER environment variable is required. This variable can hold multiple discovery server addresses, delimited by semicolons (;
). The server ID corresponds to the order in the list, starting with 0
. An empty entry between semicolons denotes an available ID. For example, ROS_DISCOVERY_SERVER=";;abc:123;;;def:456"
implies that the Discovery Server at abc:123
has an ID of 2
, and the one at def:456
has an ID of 5
.
Note on IPv6 Support in ROS_DISCOVERY_SERVER
env:
- ROS 2 Iron includes Fast-DDS version 2.10.2 and supports IPv6 addresses from Fast-DDS version 2.8.0.
- ROS 2 Humble includes Fast-DDS version 2.6.6, which may not support this feature.
export ROS_DISCOVERY_SERVER=my-robot:11811
# Run ROS2 listener
ros2 run demo_nodes_cpp listener
Configuring FASTRTPS_DEFAULT_PROFILES_FILE
Environment Variable
You can modify your local DDS settings to use a Discovery Server, bypassing the typical local multicast-based discovery. Here's what to include in your config file:
- IPv6 address of the host running the Discovery Server "server" configuration within ROS 2 Router in the
<address>
section. - The designated port.
- The remote server prefix determined by the
DISCOVERY_SERVER_ID
environment variable provided tohusarnet/ros2router
.
<?xml version="1.0" encoding="UTF-8" ?>
<dds>
<profiles xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles">
<transport_descriptors>
<transport_descriptor>
<transport_id>HusarnetTransport</transport_id>
<type>UDPv6</type>
</transport_descriptor>
</transport_descriptors>
<participant profile_name="client_profile" is_default_profile="true">
<rtps>
<userTransports>
<transport_id>HusarnetTransport</transport_id>
</userTransports>
<useBuiltinTransports>true</useBuiltinTransports>
<defaultUnicastLocatorList>
<locator>
<udpv6>
<address>husarnet-local</address>
</udpv6>
</locator>
</defaultUnicastLocatorList>
<builtin>
<discovery_config>
<discoveryProtocol>SUPER_CLIENT</discoveryProtocol>
<discoveryServersList>
<RemoteServer prefix="44.53.00.5F.45.50.52.4F.53.49.4D.41">
<metatrafficUnicastLocatorList>
<locator>
<udpv6>
<address>fc94:4fef:1952:4efe:579a:d97d:af0a:d474</address>
<port>11811</port>
</udpv6>
</locator>
</metatrafficUnicastLocatorList>
</RemoteServer>
</discoveryServersList>
</discovery_config>
</builtin>
</rtps>
</participant>
</profiles>
</dds>
After preparing the file:
export FASTRTPS_DEFAULT_PROFILES_FILE=$(pwd)/superclient.xml
# Run ROS2 node
ros2 run demo_nodes_cpp listener
For ease of use, the husarnet/ros2router
can be employed to automatically generate the superclient.xml
:
# Auto-generation of superclient.xml
docker run --rm -it \
--network host \
-e ROS_DISCOVERY_SERVER=my-robot:11811 \
-e DISCOVERY_SERVER_ID=10 \
husarnet/ros2router:1.3.0 \
cat /var/tmp/superclient.xml | awk '/\?xml version="1.0" encoding="UTF-8" \?/,0' > superclient.xml
export FASTRTPS_DEFAULT_PROFILES_FILE=$(pwd)/superclient.xml
# Run ROS2 listener
ros2 run demo_nodes_cpp listener
Initiating husarnet/ros2router
Though previously discussed, here's how to initiate the husarnet/ros2router to consolidate all methods in one section:
docker run --rm -d \
--network host \
--name ros2router \
-e ROS_DISCOVERY_SERVER=my-robot:11811 \
-e DISCOVERY_SERVER_ID=10 \
husarnet/ros2router:1.3.0
# Run ROS2 listener
ros2 run demo_nodes_cpp listener
Cloud Deployments
The previous sections assumed that Husarnet Client is running directly on the Host OS. If you want to run a multitenant cloud system, you may want to lauch a separate Husarnet Client, for each user and everything running on the same host.
To do so you can use Husarnet Sidecar Docker image. Below you will find the examples showing how to run Husarnet Client, ROS 2 Router and ROS 2 nodes totally in Docker:
- Server
- Client
x-husarnet-net:
&husarnet-net
network_mode: service:husarnet-talker
ipc: service:husarnet-talker
services:
husarnet-talker:
image: husarnet/husarnet:2.0.180
ipc: shareable
networks:
- network_talker
volumes:
- /var/lib/husarnet
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun
environment:
- HOSTNAME=talker
- JOINCODE
- HUSARNET_DEBUG=1
ros2router-talker:
image: husarnet/ros2router:1.3.0
<<: *husarnet-net
environment:
- LOCAL_TRANSPORT=builtin
- DISCOVERY_SERVER_LISTENING_PORT=11811
- DISCOVERY_SERVER_ID=0
talker:
image: husarion/ros2-demo-nodes:humble
<<: *husarnet-net
command: ros2 run demo_nodes_cpp talker
networks:
network_talker:
driver: bridge
To run the example execute:
export JOINCODE=... # copy here the joincode from app.husarnet.com
docker compose -f compose.server.yaml up
x-husarnet-net:
&husarnet-net
network_mode: service:husarnet-listener
ipc: service:husarnet-listener
services:
husarnet-listener:
image: husarnet/husarnet:2.0.180
ipc: shareable
networks:
- network_listener
volumes:
- /var/lib/husarnet
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun
environment:
- HOSTNAME=listener
- JOINCODE
- HUSARNET_DEBUG=1
- WAIT_HOSTNAMES=talker
ros2router-listener:
image: husarnet/ros2router:1.3.0
<<: *husarnet-net
depends_on:
husarnet-listener: { condition: service_healthy }
environment:
- LOCAL_TRANSPORT=builtin
- ROS_DISCOVERY_SERVER=talker:11811
- DISCOVERY_SERVER_ID=10
listener:
image: husarion/ros2-demo-nodes:humble
<<: *husarnet-net
command: ros2 run demo_nodes_cpp listener
networks:
network_listener:
driver: bridge
To run the example execute:
export JOINCODE=... # copy here the joincode from app.husarnet.com
docker compose -f compose.client.yaml up
Note that in the Client setup in the ros2router-listener
service there is:
depends_on:
husarnet-listener: { condition: service_healthy }
Thanks to that ros2router
will be started only when the talker
host (launched in the Server setup) will be online. This is important because DDS Discovery Server IPv6 address is needed to be known before running clients connecting to it.
husarnet/husarnet
Docker image includes the Docker healthcheck setting that return that the service is healthy only if all hosts listed in WAIT_HOSTNAMES
env are available. This feature is used above.
Also note, that both ROS 2 Router and ROS 2 nodes in the example above are launched with network_mode: service:husarnet
setting to share network namespace with a Husarnet service. The ipc: service:husarnet
is used to enable a shared memory communication in DDS.
Using Husarnet Dashboard CLI
After you run the examples above you will have unnecessary talker
and listener
peers on your account at https://app.husarnet.com . You can remove them manually in the Online Dashboard or by using the CLI:
export HUSARNET_DASHBOARD_LOGIN=user@acme.com # your login to app.husarnet.com
export HUSARNET_DASHBOARD_PASSWORD=qwerty # your password to app.husarnet.com
husarnet dashboard login $HUSARNET_DASHBOARD_LOGIN $HUSARNET_DASHBOARD_PASSWORD
husarnet dashboard device remove talker
husarnet dashboard device remove listener
Conclusion
The husarnet/ros2router
Docker image from Husarnet facilitates effortless ROS 2 networking by enabling connections between ROS 2 hosts over the Internet, circumventing complex DDS configurations and introducing efficient topic filtering capabilities. Deployable alongside existing ROS 2 nodes, either on the host or within Docker containers, husarnet/ros2router
ensures seamless integration for bridging nodes across varied hosts online. Its adaptability extends to robots, laptops, and cloud environments with multi-tenant architectures, rendering it an optimal tool for topic-filtered communication within distributed robotic fleets.
If you have any questions, or simply wish to share your thoughts regarding this blog post, check out this thread on the Husarnet Community Forum.