Robotics StackExchange | Archived questions

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

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

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