Ask Your Question
0

How to thread a specific computationally expensive callback

asked 2016-12-19 05:59:31 -0500

hamzamerzic gravatar image

Hello ROS community.

I am building a specific application where I have two subscriber callbacks as written below:

def callback1(msg):
    X = msg.X

def callback2(msg):
    desX = msg.desX
    publish(desX)
    while duration < 1 sec:
        if equal(X, desX):
            return True
    return False

The callbacks are oversimplified for easier understanding, and I also assume X is some global variable (or object attribute), so that both callbacks can access it.

callback1 is a sensor measurement coming in at 20 Hz.

callback2 is called on user request, but let's assume it can be called at more than 1 Hz.

In a single threaded mode, this will not work, since callback2 blocks callback1 and the sensor measurement is not read properly. I would like to implement this multi-threaded, but with following requirements:

  1. callback1 is blocking, i.e. two of them cannot be performed at the same time,
  2. callback2 is preemptable, i.e. if another call is made the previous one will be canceled,
  3. It should be done in C++.

My initial idea was to have a separate callback queue for callback2 and have the callback1 in the global queue in the single-threaded callback manner, like mentioned here under point 2.

I would like to know how to implement this given the additional requirement that the callback2 is preemptable. Also, any other design recommendations are more than welcome!

edit retag flag offensive close merge delete

Comments

1

Why not have the two callbacks simply update the global/member variables and then implement the logic in a main loop or a roscpp Timer ? You could also use a ROS Action for callback2 (instead of a plain subscriber)

spmaniato gravatar image spmaniato  ( 2016-12-19 13:26:51 -0500 )edit

In case of using a timer, what would be a good design? Have a timer which periodically checks if equal(X, desX), and takes into consideration the timestamp to publish False on exceeding the max duration? Also, regarding placing the logic in main, are there any pr's of doing that?

hamzamerzic gravatar image hamzamerzic  ( 2016-12-19 20:42:26 -0500 )edit
1

You can do a ros::Timer that triggers every so often and does that check or you can have a while loop running at some ros::Rate. They are similar, although the ROS wiki recommends Timers.

spmaniato gravatar image spmaniato  ( 2016-12-19 22:04:48 -0500 )edit

Thanks a lot, this solution works fine.

hamzamerzic gravatar image hamzamerzic  ( 2016-12-20 04:10:21 -0500 )edit

2 Answers

Sort by ยป oldest newest most voted
0

answered 2016-12-20 04:21:57 -0500

hamzamerzic gravatar image

I decided to go with the solution of having multiple callback queues by adapting the answer found here.

callback1 is thus kept in the global ROS queue, while callback2 is run asynchronously.

To make the callback2 preemptable, I added a variable which stores the time stamp of the most recent callback, and a check for a more recent time stamp in the while loop of the callback which is used for preemption. Some care had to be taken to avoid race condition, but it was rather simple with atomic variables or mutex locks.

edit flag offensive delete link more
2

answered 2016-12-19 12:37:16 -0500

ahendrix gravatar image

(1) sounds like a pretty typical use for locks, or just a single callback queue.

(2) ROS doesn't natively support callback preemption. If you want it, you'll have to build it yourself. This probably means having a few locks or mutexes and inserting preemption points into your callback, where it can abort early if there's another callback waiting.

(3) Do it in C++

edit flag offensive delete link more

Comments

1

What about modelling this on top of Actions?

gvdhoorn gravatar image gvdhoorn  ( 2016-12-19 13:41:19 -0500 )edit
1

Actionlib would make this easier, but even in actionlib, the server needs to have preemption points within the algorithm where it actively checks if the goal has been cancelled.

ahendrix gravatar image ahendrix  ( 2016-12-19 14:21:28 -0500 )edit
1

Obviously. It would probably be good if the OP could describe his requirements in a bit more detail. I'm not sure whether we're not actually solving an xy-problem here.

gvdhoorn gravatar image gvdhoorn  ( 2016-12-19 15:02:38 -0500 )edit

I am aware this could be implemented using actionlib, but it seems like an overkill here? Could you please elaborate more on 1? Do you mean separating callback1 and callback2 into different single callback queues? If so, how would I spin that so that they don't block each other?

hamzamerzic gravatar image hamzamerzic  ( 2016-12-19 20:48:53 -0500 )edit

you think actionlib is overkill, but creating your own solution isn't? I think you have it backwards.

ahendrix gravatar image ahendrix  ( 2016-12-19 22:07:35 -0500 )edit

IMHO if adding a Timer and two extra variables can solve it, then using actionlib is probably an overkill. But if that solution is the most maintainable one, I would definitely go for it.

hamzamerzic gravatar image hamzamerzic  ( 2016-12-20 04:13:42 -0500 )edit
1

From reading your own answer, I think actionlib would have been much cleaner and a more conceptually nicer way to model this all. But if it works for you the way you have it now, I'm not going to suggest anything different.

gvdhoorn gravatar image gvdhoorn  ( 2016-12-20 05:09:41 -0500 )edit

Thank you for the feedback! I will try implementing the solution using actionlib as well. I will post it here, since the way I imagined the solution to be did not seem that clean, so I might be missing something.

hamzamerzic gravatar image hamzamerzic  ( 2016-12-20 05:29:12 -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: 2016-12-19 05:55:34 -0500

Seen: 427 times

Last updated: Dec 20 '16