I and at least 1 other person and several other people online ran into this issue where if you create_subscription
after rclpy.spin
(say in a thread) it will not execute any callbacks.
I googled and googled and found no answers. ROS1 to ROS2 migration documentation is still in a pretty sad state unfortunately since in ROS1 this stuff just works. Then I pulled my hair out for 3 hours and figured out a voodoo black magic answer. Here’s the answer, in case more people go googling and hopefully stumble upon this:
-
Create at least one subscriber before rclpy.spin(). It can be a dummy subscriber and doesn’t need to actually do anything
-
After
self.create_subscription(...)
, add aself.guards
line. It looks weird like that on a line by itself, I have no idea what it does, but this magic incantation works. If you use the non-classy way to do it, i.e.node.create_subscription(...)
, then it will benode.guards
.
Yes, literally like that.
class MyFooNode(Node):
def __init__(self)
super().__init__(node_name = "my_foo_node")
self.create_subscriber(...) # any old dummy subscriber, won't work without a dummy
threading.Thread(target = some_thread, daemon = True).start()
def some_thread(self):
# Dynamically subscribe to 10 topics
for i in range(10):
time.sleep(10)
self.create_subscription(
String,
"/some_dynamic_topic_%d" % i,
functools.partial(on_message, topic_num = i) # they took away the args= feature of ROS1
10
)
self.guards # MAGIC !! won't call the callback without this!
def on_message(msg, topic_num = 0):
print("Received a message on topic number %d:" % topic_num, msg)
def main(args=None):
# boilerplate verbose ros2 stuff
rclpy.init(args=args)
node = MyFooNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
If someone can can explain why this magic works I’d love it, but until then just posting a solution online for the other people googling for this!