Threads when using ros2 and python
Hello, I want to implement a planner, which needs continous updates from other vehicles. Therefore the planner should be started in its own thread. I have read that in rospy with ROS1 each timer callback created a own thread. So I tried this in ROS2 as well, but it seems, that my program is stuck in the timer callback, where I create my planner. Is there another way to archive this?
class UploadPlan(Node):
def __init__(self):
super().__init__('upload_plan')
self.publisher_ = self.create_publisher(String, '/topic', 10)
self.subscription = self.create_subscription(
String,
'/topic',
self.sub_callback,
10)
timer_period = 1 # seconds
self.timer = self.create_timer(timer_period, self.timer_callback)
def timer_callback(self):
blocking_function() # needs to be in its own thread
self.timer.cancel()
def sub_callback(self, msg):
print("callback")
def main(args=None):
rclpy.init(args=args)
upload_plan = UploadPlan()
rclpy.spin(upload_plan)
upload_plan.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Thanks.
Asked by fnax8902 on 2021-09-10 05:09:30 UTC
Answers
One thing to note is that the timer is not creating it's own thread. In your example, the reason your timer_callback
is not ending is because you're expecting that function to be in it's own thread, which it is not. All the timer does is create callback events that an executor would pick up to run. In your example, you create the executor when you call rclpy.spin()
which initializes the default, single-threaded executor, adds the node upload_plan
to it, and then calls spin()
on that executor. Whenever your subscription
object or timer
object need to respond to do something, they create an event, or callback, for the executor to actually do the executing.
What you're trying to do is wrong because your events should not be continuous, blocking events, lest they clog up the executor from being able to do other things. I don't quite understand why you think you need a separate thread inside the timer_callback
. I'm guessing from your example that you're are simply trying to create a periodic calculation that listens to topics and publishes a calculation as the given timer_period
. To do that all you would need to do is modify your blocking function to be one run of that calculation and then publish that result for the other vehicles to use. It would look something like this:
class UploadPlan(Node):
def __init__(self):
super().__init__('upload_plan')
self.publisher_ = self.create_publisher(String, '/topic', 10)
self.subscription = self.create_subscription(
String,
'/topic',
self.sub_callback,
10)
self.state = "foo"
timer_period = 1 # seconds
self.timer = self.create_timer(timer_period, self.timer_callback)
def timer_callback(self):
if self.state == 'bar':
self.publisher_.publish(String("foobar"))
else:
self.publisher_.publish(String("barfoo"))
def sub_callback(self, msg):
self.state = msg.data
def main(args=None):
rclpy.init(args=args)
upload_plan = UploadPlan()
rclpy.spin(upload_plan)
upload_plan.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Asked by Jaron on 2021-09-10 08:43:36 UTC
Comments
I know about the multi-threaded executer in cpp. How do I use it in python?
Asked by fnax8902 on 2021-09-10 12:02:47 UTC
Asked by flo on 2021-09-11 15:54:14 UTC
Comments
There's definitely ways to achieve what you want to do. Please edit your question to provide a compact example for us to be able to help you.
Asked by tfoote on 2021-09-10 05:11:27 UTC
I added an example
Asked by fnax8902 on 2021-09-10 05:25:26 UTC
Why does the planner calculation need to be in its own thread?
Asked by Jaron on 2021-09-10 15:02:10 UTC