2
$\begingroup$

I'm fairly new to ROS2 after having worked with ROS for a number of years. A typical pattern we would often use was to have a ROS node subscribe to a number of inputs, and then combine and process them into a single output. Depending on the application, the node might be required not to produce output at a fixed rate, but rather matching one of the input streams (let's call it the primary input) with the newest or interpolated data from each of the other streams (herafter refered to as the auxilary inputs). In order to keep the callbacks light, we would implement this where each auxilary input would simply save the latest msg, or add it to an interpolator, and the primary input callback would then pack each of it's inputs with the newest value for each of the auxilary inputs and add this to a processig queue. The node would then have a main loop consuming the data in the processing queue. We would implement this using the AsyncSpinner for the callbacks and have the consumer run in a while loop in the main thread.

In ROS2, I really like how the Timer class now has a callback so that your fixed rate loops can be run in a homogenous mannor with your callbacks, however this doesn't fit the usecase described above, as the main loop should run full-rate only waiting when the queue is empty. I can of course just start a thread manually, but this breaks the neat homogenous execution control, leaving the node threading to be handled directly for that specific thread.

Is there a mechanism in ROS2 for such a pattern, and otherwise, do people think this might be a good suggestion for an addition, as a construct along side pub/sub, service, action, and timer callbacks?

$\endgroup$
3
  • $\begingroup$ Welcome to Robotics, felix_blix. This is a great first question, and hits at themes I'm going to need to address with my own work as well. Thanks for the effort you put into this post and I'm saving it so I can read all the answers you get! $\endgroup$
    – Chuck
    Commented Apr 4 at 13:46
  • $\begingroup$ I'm hoping this comment reaches your email inbox. There's an answer posted below, but you haven't been logged in since I put the bounty on this question. Please review the answer below and let us know if it answers your question :) $\endgroup$
    – Chuck
    Commented Apr 22 at 17:32
  • 1
    $\begingroup$ Thanks for the bounty, and the ping @Chuck. I had to call it an early night yesterday, so I was just down for maintenance Zz. $\endgroup$
    – felix_blix
    Commented Apr 23 at 3:38

1 Answer 1

2
+100
$\begingroup$

If I understand correctly, you want:

  • Multiple subscribers to receive data,
  • Upon msg of the 'primary subscriber':
    • Aggregate the data of all subscribers (either latest msg or interpolated),
    • Signal another thread to process the data,
  • But not manually deal with spawning a thread etc. (i.e. rather use standard ROS 2 functionality for this).

I would implement this with a MultiThreaded executor and two mutually exclusive callback groups. For more info on callback groups, see this previous answer.

More specifically:

  • Create a node with:
    • Two MutuallyExclusive callback groups,
    • As many subscribers as needed,
    • One timer,
  • Add all subscribers to the first callback group,
  • Add the timer to the second callback group,
  • Run the node in a MultiThreadedExecutor.

The timer is not used as a timer, just as a means to run a callback that blocks indefinitely until it is signalled that new data is available, by the primary input callback. The timer period should be set smaller than the wait + process duration, so that the timer instantly retriggers its callback as soon as the data is processed. See also this Discourse thread for more info on ROS 2 timer behavior when callbacks take longer than the timer period.

You will need some thread synchronisation primitive between the 'primary subscriber' callback and the timer callback, to signal the blocking wait of the timer callback and to avoid concurrent access to the data, as these are executed by different threads.

$\endgroup$
2
  • $\begingroup$ Thanks for the detailed answer @JRTG. I guess that makes a lot of sense to use a timer with a very small period for an unconstrained loop. $\endgroup$
    – felix_blix
    Commented Apr 23 at 3:36
  • $\begingroup$ Glad it helps. I added a reference to more detailed info timer behavior when callbacks take longer than the timer period, as I think this is relevant knowledge in this context. $\endgroup$
    – JRTG
    Commented Apr 23 at 7:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.