Understanding ROS 2 publisher size, subscriber size, and topic size
(I asked this question in robotics.stackexchange.com but did not received any answer! That is why asking in here with some modifications.) I have the following ROS 2 scripts for a publisher node:
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
import time
class MinimalPublisher(Node):
def __init__(self):
super().__init__('minimal_publisher')
self.publisher_ = self.create_publisher(String, 'topic', 1)
self.timer1 = self.create_timer(1, self.timer1_callback)
self.timer2 = self.create_timer(2, self.timer2_callback)
self.timer3 = self.create_timer(3, self.timer3_callback)
self.i = 1
self.j = 1
self.k = 1
def timer3_callback(self):
msg = String()
msg.data = 'timer3: %d' % self.k
self.publisher_.publish(msg)
self.get_logger().info('Publishing: "%s"' % msg.data)
self.k += 1
def timer2_callback(self):
msg = String()
msg.data = 'timer2: %d' % self.j
self.publisher_.publish(msg)
self.get_logger().info('Publishing: "%s"' % msg.data)
self.j += 1
def timer1_callback(self):
msg = String()
msg.data = 'timer1: %d' % self.i
self.publisher_.publish(msg)
self.get_logger().info('Publishing: "%s"' % msg.data)
self.i += 1
# self.create_rate(4).sleep()
def main(args=None):
rclpy.init(args=args)
minimal_publisher = MinimalPublisher()
time.sleep(2)
msg = String()
msg.data = 'Publishing garbage 1'
minimal_publisher.publisher_.publish(msg)
msg = String()
msg.data = 'Publishing garbage 2'
minimal_publisher.publisher_.publish(msg)
msg = String()
msg.data = 'Publishing garbage 3'
minimal_publisher.publisher_.publish(msg)
msg = String()
msg.data = 'Publishing garbage 4'
minimal_publisher.publisher_.publish(msg)
msg = String()
msg.data = 'Publishing garbage 5'
minimal_publisher.publisher_.publish(msg)
msg = String()
msg.data = 'Publishing garbage 6'
minimal_publisher.publisher_.publish(msg)
time.sleep(3)
# rate = minimal_publisher.create_rate(5)
# rate.sleep()
try:
rclpy.spin(minimal_publisher)
except KeyboardInterrupt:
pass
# minimal_publisher.destroy_node()
# rclpy.shutdown()
if __name__ == '__main__':
main()
I used timer to hold after publishing some garbage messages, and then started spinning using 3 timers. I have a simple subscriber node as follows:
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
import time
class MinimalSubscriber(Node):
def __init__(self):
super().__init__('minimal_subscriber')
self.subscription = self.create_subscription(String,'topic',self.listener_callback, 4)
self.subscription # prevent unused variable warning
def listener_callback(self, msg):
self.get_logger().info('I heard: "%s"' % msg.data)
def main(args=None):
rclpy.init(args=args)
minimal_subscriber = MinimalSubscriber()
# delay = minimal_subscriber.create_rate(2.5)
rclpy.spin(minimal_subscriber)
if __name__ == '__main__':
main()
I used a launch file to run both publisher and subscriber at once. I did some experiments by varying the publisher and subscriber queue size in the above code in order to understand topic size, publisher queue, subscriber queue as follows:
Experiment 1: pub queue size 1, sub queue size 1. Results: Only the 6th garbage message is shown. After that every message is received from all 3 timers (5 garbage messages lost)
Experiment 2: pub queue size 100, sub queue size 1. Results: EXACTLY SAME AS EXPERIMENT 1 (also checked for pub size 10, 5, 6 and results are same)
Experiment 3: pub queue size 1, sub queue size 100. Results: All 6 garbage messages are shown (nothing lost). After that every message is received from all 3 timers.
Experiment 4: pub queue size 1, sub queue size 4. Results: Last 4 garbage messages are shown (First two messages are lost!). After that every message is received from all 3 timers.
It seems like only the subscriber queue size is the one that matters. The publisher size does not matter at all. I haven't been able to generate any case where increasing the publisher size when the subscriber size is fixed (say 1) is making any difference! Can anybody explain? Moreover, does the topic has any internal size, or only the publisher and subscriber sizes are the one that exists.
Asked by choton on 2023-04-14 08:13:15 UTC
Answers
I only skimmed your question/code since there was a lot to unpack here, so apologies if I'm missing the mark (I'm also definitely not an expert on DDS)...
But it seems like you are using the default QoS (Quality of Service) settings. I think the Durability
policy might be relevant to your experiment:
By default, publishers and subscriptions in ROS 2 have “keep last” for history with a queue size of 10, “reliable” for reliability, “volatile” for durability, and “system default” for liveliness. Deadline, lifespan, and lease durations are also all set to “default”.
Volatile
durability means the publisher will make no attempt to persist samples for late coming subscribers. You could try changing this (for both the publisher and subscriber) to Transient Local
which allows the publisher to store old messages in the queue for late coming subscribers. There's more details here that you may find interesting: https://github.com/ros2/ros2/issues/464
Notably, it seems that the publisher queue size in DDS is used to store old messages (if Durability is set to Transient Local) and to support "bursts" of new messages (e.g. if messages are generated faster than they can be sent, the queue should still enable the "new" messages to get sent when possible).
Asked by shonigmann on 2023-04-14 11:44:35 UTC
Comments
I only want to understand the default settings for now (will check the other settings later on). Just tell me if the publisher queue size does matter in the default setting or is only the subscriber size is the only one that matters? Moreover, if the size is always 10, then what does the 3rd parameter in create_publisher() and the 4th parameter in create_subscription means? Does it takes as 10 whatever values I put in these two methods?
Asked by choton on 2023-04-14 12:42:06 UTC
Comments