Ask Your Question
0

[ROS2] Correct usage of spin_once to receive multiple callbacks synchronized

asked 2019-04-06 06:05:28 -0500

nzlz gravatar image

Hello everyone,

I can't figure out which is the correct way of using rclpy.spin_once() to get the output from multiple existing callbacks at once. Right now I'm using a simple while that loops until all callbacks have new values. Simplified code:

def __init__(self):
     rclpy.init(args=None)
     self.node = rclpy.create_node(self.__class__.__name__)
     self.sub1 = self.node.create_subscription( ... obs1)
     ...
     self.sub6 = self.node.create_subscription( ... obs6)

// callbacks
def obs1(self, message):
     self.obsJointStates[0] = message.position
...
def obs6(self, message):
     self.obsJointStates[5] = message.position

// This is the function that is called constantly in every new step (Reinforcement Learning)
def take_observation(self): 
    rclpy.spin_once(self.node)
    while None in self.obsJointStates:
        rclpy.spin_once(self.node)
    self.obsJointStates = np.array([None, None, None, None, None, None])

Inside take_observation function, the while loop waiting for complete callbacks can loop around 100 times currently. I believe there has to be a solution to get fast callbacks and not use this hacky code. Meaning one single call to spin_once() or whatever it is, should be enough.

Few things I tried with no success:

  1. Callback group
  2. Multithread executor
  3. Use timeout_sec in spin_once

Any idea which is the correct approach?

Thanks in advance,

Nestor

edit retag flag offensive close merge delete

1 Answer

Sort by ยป oldest newest most voted
0

answered 2019-04-08 18:38:58 -0500

William gravatar image

spin_once() is typically used to integrate with another thing that needs the same thread (like if ROS and Qt need the main thread, they can take turns "spinning" to share the time), but can be used to do what you're doing.

A better way to do this (in my opinion) would be to use a future and store the data in it and complete the future when the data set is completed, that way you can use:

https://github.com/ros2/rclpy/blob/1a...


However, are you just waiting for "at least one sample" in each slot or do you want a matching set? I.e. do you want sequence number 1 of each reading? Put another way, what happens when obs1() gets called more than once before obs6() is called for the first time? Is that ok?


More generally, however, this is something that is commonly needed and is traditionally covered in the message_filters package from ROS 1. This has been paritially ported to ROS 2, but I don't know the state of it:

https://github.com/ros2/message_filters/blob/master/src/message_filters/__init__.py

edit flag offensive delete link more

Comments

Thanks for the feedback William, I thought spin_until_future_complete was intended to be used in service calls, I will give it a try.

> However, are you just waiting for "at least one sample" in each slot or do you want a matching set?

I can get observation from different sets. This is solved using the timestamp of the messages and looping until the stamp has a higher value than a synchronization clock. Example code:

# Check that the observation is not prior to the action
# self.obsJointStates_stamp is an array[6], each element is filled in every callback (6 callbacks)
# ros_clock gets a new value after the last of 6 joint publisher is executed (putting it simple)

while None in self.obsJointStates or not all(i >= ros_clock for i in self.obsJointStates_stamp):
      self.executor.spin_once()
last_observations = self.obsJointStates
# reset helper variables obsJointStates and obsJointStates_stamp

spin_until_future could simplify this check.

nzlz gravatar imagenzlz ( 2019-04-08 21:59:13 -0500 )edit
1

Isn't this what message_filters is supposed to solve?

gvdhoorn gravatar imagegvdhoorn ( 2019-04-09 01:14:57 -0500 )edit

Yes. However Im noticing the rclpy implementation of pub/sub is less stable than rclcpp, so this is turning to be a bigger issue than the original question. What i mean is that using message_filters the performance is not going to be any better than what it is now, and all we care is fast pub/sub rates.

nzlz gravatar imagenzlz ( 2019-04-09 01:27:46 -0500 )edit
1

It may not be better, but it would at least be more standardised, as the pattern(s) message_filters implements are common to many ROS nodes, so it makes sense to try and (re)use implementations as much as possible (if only to make those patterns more easily recognisable in your own code, instead of a custom implementation).

gvdhoorn gravatar imagegvdhoorn ( 2019-04-09 03:55:16 -0500 )edit

Yeah, I think message_filters is the best solution, but in the meantime if my answer has addressed your original question, then please accept it. :+1:

William gravatar imageWilliam ( 2019-04-10 15:22:30 -0500 )edit

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

2 followers

Stats

Asked: 2019-04-06 06:05:28 -0500

Seen: 123 times

Last updated: Apr 08