Dynamically create subcription callback functions (Python)
Hello, I am trying to generalize one of my nodes to subscribe to not just a different set of topics, but a different number of topics, depending on the situation. Specifically I want it to republish the data it receives on a given topic, to a topic with a similar name and the incoming data modified in some way.
Given a dictionary of topic names, I figured I could do this in a loop like so:
class DynamicSubscriptions(Node):
def __init__(self):
super().__init__("dynamic_subscriptions")
list_of_subs_to_make = [ # Could come from a parameter, be added as the node runs, ...
"one",
"two",
"three",
]
self.subscribers = {}
self.sub_callbacks = {}
self.pubs = {}
for topic in list_of_subs_to_make:
self.pubs[topic] = self.create_publisher(
Float64,
topic + '/republished',
QoSPresetProfiles.SENSOR_DATA.value
)
new_cb = self.make_callback(topic)
self.sub_callbacks[topic] = new_cb
self.subscribers[topic] = self.create_subscription(
Float64, # Leaving these the same for all topics, for simplicity of the example
topic,
new_cb,
QoSPresetProfiles.SENSOR_DATA.value
)
for topic, sub in self.subscribers.items():
self.get_logger().info(f"topic {topic} subscription {sub}")
def make_callback(self, topic_name):
def callback(self, msg):
self.get_logger().info("Inside {topic_name} callback, got {msg}")
return callback
If I run the node, and try publishing to the topics, I get this error:
root@thomas-Precision-5530-big:/workdir# ros2 run rloc_subsystems dynamic_subscriptions
[INFO] [1650860469.579535172] [dynamic_subscriptions]: topic one subscription <rclpy.subscription.Subscription object at 0x7fb30ce415b0>
[INFO] [1650860469.580131489] [dynamic_subscriptions]: topic two subscription <rclpy.subscription.Subscription object at 0x7fb30ce41760>
[INFO] [1650860469.580512294] [dynamic_subscriptions]: topic three subscription <rclpy.subscription.Subscription object at 0x7fb30ce41910>
Traceback (most recent call last):
File "/workdir/install/rloc_subsystems/lib/rloc_subsystems/dynamic_subscriptions", line 9, in <module>
main()
File "/workdir/install/rloc_subsystems/lib/python3.8/site-packages/rloc_subsystems/wheel_odometry/dynamic_minimal.py", line 65, in main
rclpy.spin(node)
File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/__init__.py", line 191, in spin
executor.spin_once()
File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/executors.py", line 711, in spin_once
raise handler.exception()
File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/task.py", line 239, in __call__
self._handler.send(None)
File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/executors.py", line 426, in handler
await call_coroutine(entity, arg)
File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/executors.py", line 351, in _execute_subscription
await await_or_execute(sub.callback, msg)
File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/executors.py", line 118, in await_or_execute
return callback(*args)
TypeError: callback() missing 1 required positional argument: 'msg'
root@thomas-Precision-5530-big:/workdir#
What am I missing? Is there a better way to achieve my goal? I understand this may be a problem with how I am using python, and not a ROS problem.
I am using ROS2 Foxy on Docker using osrf/ros:foxy-desktop
image (described here), Python 3.8.5