Enables pub/sub messaging between two robots in different networks over NATS. The NATS Connector acts as a message forwarder between the local ROS network and the NATS network. Data flows from a publisher in ROS to a publisher in NATS which sends the messages to any subscriber connected to the NATS server, anywhere in the world. On the other end, a NATS subscriber forwards the data to a ROS subscriber which then uses that data from the original publisher.
To use this package, add a single instance of the following node somewhere in your main launch-file and make sure to update the parameters according to your needs. Check out the Examples to see this in action.
<node name="nats_connector" pkg="nats_ros_connector" type="nats_connector.py" output="screen">
<param name="host" value="nats://host.docker.internal:4222" />
<rosparam>
publishers:
- topicA
- /topicB
- /topicC/A
- /topicD/A/B
subscribers:
- topicE
- /topicF
- /topicG/A
- /topicH/A/B
services:
- serviceA
- /serviceB
- /ServiceC/A
- /ServiceD/A/B
service_proxies:
- name: serviceE
type: [serviceE type] # eg. std_srvs/Trigger
- name: /serviceF
type: [serviceF type] # eg. std_srvs/Bool
- name: /serviceG/A
type: [serviceG/A type]
- name: /serviceH/A/B
type: [serviceH/A/B type]
</rosparam>
</node>
- In
host
you provide the address to your NATS server (in the example above, the NATS server is running locally, while ROS is running in a Docker container both on the same machine, sohost.docker.internal
is used to get the address of the localhost relative to the container.
Inside of the <rosparam>
tags you provide the lists of publishers
, subscribers
, services
, and service_proxies
in the YAML format.
-
In
publishers
, you provided a list of all the topics which you would like to publish from ROS to NATS. -
In
subscribers
, you provided a list of all the topics which you would like to subscribe to from NATS to ROS. -
In
services
, you provide a list of all services are advertised in your ROS nodes and should in turn be advertised to other NATS clients -
In
service_proxies
, you provide a sequence of mappings each mapping containing thename
andtype
of the service. This will make sure that NATS reaches out to a client advertising the service when you create a service proxy in your ROS nodes. Note thatwait_for_message
does not guarantee that the client advertising the services over NATS is guaranteed to exists. Therefore, you might encounter the error:NATS Service Proxy ERROR: REQUEST FOR [service_name] has no NATS responders available (No NATS Client is advertising this service yet)
, which means that that a request was sent, but NATS found no launched client advertising the service. So far, we leave it up to the user to ensure that a service is up.The topic names may begin with or without a forward-slash
/
. Furthermore, the namespaces in ROS, created with/
are translated to the NATS token separator.
. For instance the topic name/topicH/A/B
, in ROS, becomestopicH.A.B
in NATS. This happens under the hood and you don't need to worry about it. To learn more about the naming system in ROS, read this article. To learn more about the naming system in NATS, read this article to under
Other supported parameters include
name=None, pedantic=False, verbose=False, allow_reconnect=True, connect_timeout=2, reconnect_time_wait=2, max_reconnect_attempts=60, ping_interval=120, max_outstanding_pings=2, dont_randomize=False, flusher_queue_size=1024, no_echo=False, tls=None, tls_hostname=None, user=None, password=None, token=None, drain_timeout=30, signature_cb=None, user_jwt_cb=None, user_credentials=None, nkeys_seed=None
These are used when creating the NATS Client connection. See the nats.py documentation.
-
Clone this repository into the
src
folder in your ROS workspace.git clone https://github.com/aljanabim/nats-ros-connector
-
Install
nats-py
pip3 install nats-py==2.2.0
Ensure pip is installed
sudo apt update
sudo apt install python3-pip
pip3 --version
then, install nats-py
pip3 install nats-py==2.2.0
The NATS ROS Connector includes an example which demonstrates bi-directional messaging between two clients ClientA
and ClientB
. Each client is meant run on separate machine with distinct ROS Cores. With a NATS Server that's accessible to both clients, it's possible to bridge the networking gap between the clients and enable bi-directional communication. The example can scale to as many number of clients and topics as the network bandwidth and the NATS Server can handle. The messaging and services layout in the example is follows:
ClientA
publishes a topic calledclientA_talker
of msg typestd_msgs/Header
and subscribers to a topic calledclientA_listener
of msg typestd_msgs/String
.ClientB
publishes a topic calledclientA_listener
of msg typestd_msgs/String
and subscribers to a topic calledclientA_talker
of msg typestd_msgs/Header
.ClientA
advertises a service calledtrigger_clientA
of service typestd_srvs/Trigger
and creates a service proxy totrigger_clientB
of service typestd_srvs/SetBool
.ClientB
advertises a service calledtrigger_clientB
of service typestd_msgs/SetBool
and creates a service proxy totrigger_clientA
of service typestd_srvs/Trigger
.
graph TB
subgraph "Client B (node on machine B)"
d["clientA_listener (publisher)"]
c["clientA_talker (subscriber)"]
end
subgraph "Client A (node on machine A)"
a["clientA_talker (publisher)"]
b["clientA_listener (subscriber)"]
end
N[NATS Server]
a -- Msg Class: Header--> N -- Msg Class: Header--> c
d -- Msg Class: String--> N -- Msg Class: String--> b
-
Start by running a NATS Server and make sure it is accessible to both clients.
-
Modify the url to your NATS Server inside the launch file corresponding to each client. For ClientA, edit the
host
param in example_clientA.launch and for ClientB, edit thehost
param in example_clientB.launch -
On Machine A start ClientA by running
roslaunch nats_ros_connector example_clientA.launch
-
On Machine B start ClientB by running
roslaunch nats_ros_connector example_clientB.launch
Both clients sleep for 2
seconds before they start calling the service proxies, giving you time to launch both ends.
On ClientA
you should see output similar to the following, with new messages arriving at 1Hz
SERVICE RESULT OF trigger_clientB
success: False
message: "Client B just got Bowling!"
SERVICE RESULT OF trigger_clientB
success: False
message: "Client B just got Bowling!"
SERVICE RESULT OF trigger_clientB
success: False
message: "Client B just got Bowling!"
SERVICE RESULT OF trigger_clientB
success: False
message: "Client B just got Bowling!"
SERVICE RESULT OF trigger_clientB
success: False
message: "Client B just got Bowling!"
SERVICE RESULT OF trigger_clientB
success: False
message: "Client B just got Bowling!"
from clientA_listener
data: "1683315536801697492"
SERVICE RESULT OF trigger_clientB
success: False
message: "Client B just got Bowling!"
SERVICE RESULT OF trigger_clientB
success: False
message: "Client B just got Bowling!"
from clientA_listener
data: "1683315538801133871"
SERVICE RESULT OF trigger_clientB
success: False
message: "Client B just got Bowling!"
SERVICE RESULT OF trigger_clientB
success: False
message: "Client B just got Bowling!"
from clientA_listener
data: "1683315540808658599"
On ClientB
you should see output similar to the following, with new messages arriving at .5Hz
from clientA_talker
seq: 0
stamp:
secs: 1683315593
nsecs: 274091482
frame_id: ''
SERVICE RESULT OF trigger_clientA
success: True
message: "Hello Trigger from Client A"
SERVICE RESULT OF trigger_clientA
success: True
message: "Hello Trigger from Client A"
from clientA_talker
seq: 0
stamp:
secs: 1683315594
nsecs: 242599487
frame_id: ''
from clientA_talker
seq: 0
stamp:
secs: 1683315595
nsecs: 246570825
frame_id: ''
SERVICE RESULT OF trigger_clientA
success: True
message: "Hello Trigger from Client A"
from clientA_talker
seq: 0
stamp:
secs: 1683315596
nsecs: 248357772
frame_id: ''
The script for ClientA
is in scripts/example_clientA.py and the script for ClientB
is in scripts/example_clientB.py
- Support Publishers
- Support Subscribers
- Allow topic names that begin with forward-slash, ie. "/topic_name"
- Support ROS Namespaces (via translation into NATS Subject token)
- Support Authenticated NATS Servers
- Support Services
- Robustly Services and enable
wait_for_service
to work over NATS too.