ROS2 how to do multi-threading, subscription callbacks, spinning , executors
What is the correct way of multi threading in ROS2? As I understand it, ROS1 multi-threading was implicit.
I've been trying to find the convention for ROS2 of solving the following, relatively simple problem:
- Run a node which is running a practically _forever_ loop.
- Have the node receive subscription callbacks succesfully.
An attempt was made by me here and I "solved" that (read: made it work - doesn't seem like the correct way) by forcing a call to rclpy.spin_once()
inside the loop in (1). So, it clear to me that I need a better understanding of what is happening under the hood with ROS2. Can I put the main loop on a rate or something so that the spin procedure can run as intended? Where can I read more about this?
Another attempt was made as follows (see below), here am using threading in a raw manner, which also seems to "work" but also seems very rough around the edges. I also attempted to use MultiThreadedExector
, however I fail to see how it is meant to help.
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
from rclpy.executors import MultiThreadedExecutor
import threading
import time
class LoopRunner(Node):
def __init__(self):
super().__init__('loop_runnner')
threading.Thread(target=self.run_loop).start()
def run_loop(self):
while True:
#self.get_logger().info("I work")
time.sleep(0.001)
pass
class MinimalSubscriber(Node):
def __init__(self):
super().__init__('minimal_subscriber')
self.subscription = self.create_subscription(
String,
'topic',
self.listener_callback,
10)
def listener_callback(self, msg):
self.get_logger().info('I heard: "%s"' % msg.data)
def main(args=None):
rclpy.init(args=args)
try:
minimal_subscriber = MinimalSubscriber()
loop_runner = LoopRunner()
executor = MultiThreadedExecutor()
executor.add_node(loop_runner)
executor.add_node(minimal_subscriber)
try:
executor.spin()
finally:
executor.shutdown()
minimal_subscriber.destroy_node()
loop_runner.destroy_node()
finally:
rclpy.shutdown()
if __name__ == '__main__':
main()
I guess this all comes down to better understanding the spinning process / callback queues and multi-threading of ROS2. I'll take any resources that can help out here as I've been looking around and while there is bits and pieces, I cannot find a comprehensive piece of code or documentation that paints the full picture.