Package dt_communication_utils
Contents
Package dt_communication_utils#
The dt_communication_utils package provides utility classes to implement simple communication between robots in Duckietown. It wraps around LCM and hides all the networking details from the user.
Groups and Subgroups#
Communication between processes within a local network happens within Communication Groups and Subgroups. A communication group is a logical group that processes can easily join. Communication groups (and subgroups) are fully distributed, the only thing two entities (e.g., processes) within the same network need to agree on in order to be able to communicate is a group name (e.g., a string). A group is uniquely identified by its name.
Subgroups work the same way, they exist only to provide an easy way of breaking a group into smaller subgroups. Think about them as breakout rooms for robots. For the sake of ease, the following sections will refer to communication groups, but everything we say about groups also apply to subgroups.
Messages#
Messages exchanged over communication groups are ROS messages. This makes it easier for the user to integrate multi-robot communication with intra-robot communication. A process can easily receive a message over ROS and forward it (as is) to a communication group. Isn’t it beautiful?
Get Started#
Let’s get started. The first thing to do is initialize a communication group.
Just like it happens with humans, for communication to be effective, your processes need to agree on where to talk (e.g., a group name) and what language to use (e.g., a message type).
Create a Group#
You can create a communication group by using the class
dt_communication_utils.DTCommunicationGroup.
from dt_communication_utils import DTCommunicationGroup
from std_msgs.msg import String
group = DTCommunicationGroup('my_group', String)
Create a Publisher#
Messages can be published to a group using a Publisher object. Use the method publish() to publish a new message.
publisher = group.Publisher()
message = String(data="Hello!")
publisher.publish(message)
Easy, right?
Create a Subscriber#
Now that we know how to publish a message, let’s see what we need to do to receive a message from the same group.
def callback(message, header):
print(message)
subscriber = group.Subscriber(callback)
Troubleshooting#
I don’t receive messages#
For UDP Multicast (UDPm) to work properly, the kernel of your Operating System has to have a route for UDPm traffic. This means that a network interface has to be identified as the gateway for such traffic. When no explicit route exists, the default gateway is selected.
This can result in missing messages on computers where multiple network interfaces exist.
For example, if you have a computer with two interfaces, say eth0 connected to a WAN and
eth1 connected to a LAN, your default gateway will (most likely) be eth0,
because it is the one with access to the internet.
If you create a Communication Group on such computer, the UDPm traffic will enter/leave
your computer through the interface eth0. This means that any message incoming from a
device within your LAN will not be received, and similarly, no messages leaving your computer
will ever reach any device on your LAN.
This can be fixed by explicitly telling your kernel which interface should be responsible for Duckietown UDPm traffic. You can do so by running the command,
sudo route add -net 239.255.0.0 netmask 255.255.240.0 dev DEVICE
where you replace DEVICE with the network interface your LAN is connected to (e.g., eth1
in the example above).
Note
UDPm networks can take IP addresses from the range 224/4, which means any IP address
between 224.0.0.0 and 239.255.255.255. Duckietown spans communication groups
across the range 239.255.0/20 hence the route above only routes Duckietown UDPm traffic.