ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
10

Suggest good example of multi-threading with ROS?

asked 2013-02-25 14:47:40 -0500

dcconner gravatar image

updated 2015-10-30 18:02:31 -0500

tfoote gravatar image

Can some someone point me to a good example of multi-threading using ROS?

I would like to move calculations outside the main ROS callbackQueue, and then publish a topic after the calculations are done.

Searching wiki and answers.ros 53055, I see suggestions to do this, and to use boost::thread, but it's not clear how best to tie the results back to publishing an advertised topic.

Should I create a separate node handle, and advertise from within the worker thread? Or, is publish/node handle thread safe so that I can call publish from within worker thread using a node handle and topic initialized/advertised in the main thread?

edit retag flag offensive close merge delete

Comments

Since this question is becoming very popular, I suggest that you modify it with a minimal example scenario. It does not need to be robot related, but it should showcase your use case well. Then people can discuss that in detail and provide solutions which hopefully will turn into design patterns.

Mani gravatar image Mani  ( 2015-12-15 00:51:33 -0500 )edit

3 Answers

Sort by ยป oldest newest most voted
6

answered 2016-02-12 08:59:27 -0500

dcconner gravatar image

updated 2016-02-12 09:01:33 -0500

This question is a bit old, and since I didn't get a response we worked out our own plan in the interim.

Since the question has recently got some traction, I'll briefly describe what we did.

We used boost::thread to create our worker threads. ros was set up in the main thread, and publishers/subscribers were created per the normal tutorials.

Data coming in via subscriptions would have the const ptr stored in a lock protected copy. The worker thread would then grab a copy of the latest const pointer at the appropriate cycle. Data processing and calculations would occur in the worker thread.

The worker thread would then use the publisher to send ROS data. Minimal processing between lock/unlock, and only publishing from one thread seemed to work well.

Using the worker thread maintained the responsiveness to handling ros messages that were coming in at 1kHz. Our control loop ran around 250-300 hz.

edit flag offensive delete link more

Comments

Do you have an example code with these optimizations? Currently I used a class to track data and keep my callbacks as fast as possible with heavy calculations done in the main loop.

uwleahcim gravatar image uwleahcim  ( 2016-04-19 04:53:13 -0500 )edit

Unfortunately, our main controller contained proprietary data and we could not open source it.

dcconner gravatar image dcconner  ( 2016-07-06 18:42:28 -0500 )edit
1

answered 2015-12-24 01:11:08 -0500

pablocesar gravatar image

updated 2015-12-24 01:45:15 -0500

I don't get what you are asking, if it is just an example using Multi-threaded spinner in ROS then: go to http://wiki.ros.org/roscpp/Overview/C...

4.1 Advanced: Using Different Callback Queues

You may have noticed the call to ros::getGlobalCallbackQueue() in the above implementation of spin(). By default, all callbacks get assigned into that global queue, which is then processed by ros::spin() or one of the alternatives. roscpp also lets you assign custom callback queues and service them separately. This can be done in one of two granularities:

Per subscribe(), advertise(), advertiseService(), etc.

Per NodeHandle

(1) is possible using the advanced versions of those calls that take a *Options structure. See the API docs for those calls for more information.

(2) is the more common way:

ros::NodeHandle nh;

nh.setCallbackQueue(&my_callback_queue);

This makes all subscription, service, timer, etc. callbacks go through my_callback_queue instead of roscpp's default queue. This means ros::spin() and ros::spinOnce() will not call these callbacks. Instead, you must service that queue separately. You can do so manually using the ros::CallbackQueue::callAvailable() and ros::CallbackQueue::callOne() methods:

  my_callback_queue.callAvailable(ros::WallDuration());
  // alternatively, .callOne(ros::WallDuration()) to only call a single callback instead of all available

The various *Spinner objects can also take a pointer to a callback queue to use rather than the default one:

  ros::AsyncSpinner spinner(0, &my_callback_queue);
  spinner.start();

or

 ros::MultiThreadedSpinner spinner(0);
 spinner.spin(&my_callback_queue);

4.1 Uses

Separating out callbacks into different queues can be useful for a number of reasons. Some examples include:

Long-running services. Assigning a service its own callback queue that gets serviced in a separate thread means that service is guaranteed not to block other callbacks.

Threading specific computationally expensive callbacks. Similar to the long-running service case, this allows you to thread specific callbacks while keeping the simplicity of single-threaded callbacks for the rest your application.

I've used Asynchronous spinner for this kind of situation:

I'll do it as a service, not a topic, I'll call the service from another node running somewhere else (device with a multi-core processor and/or a GPU) that is running in the same ROS master and that will provide the answer once it finished, but, I might get your question wrong (it is not clear).

edit flag offensive delete link more
0

answered 2015-12-14 04:02:12 -0500

Claudio gravatar image

Hi Conner

I would look at this from an OOP point of view and my answer would be to isolate scopes.

Your application will generate a comm interface, a worker object (threaded) and other elements: the inner working of the interface (generating a ROS node, or a ZeroMQ pub/sub or anything else really) should be self contained in the interface itself.

The worker takes data input and provides output, what flow this data follows should not be in the scope of the worker.

So the comm interface shouldn't need to know how the worker does its job, and the worker shouldn't need to know how to treat data (when to send it, how to send it, and so on).

edit flag offensive delete link more

Question Tools

2 followers

Stats

Asked: 2013-02-25 14:47:40 -0500

Seen: 5,500 times

Last updated: Feb 12 '16