Ask Your Question

Preventing excessive callbacks in RosPy

asked 2019-03-06 21:58:07 -0600

jakeman555 gravatar image

updated 2019-03-07 10:15:35 -0600

I have written a RosPy node for sensing input from the 3Dconnexions mouse, but the mouse's node publishes messages very often (at about 30Hz). My poor Raspberry Pi can't handle all of those threads, so I made a timer that prevented the function body from being executed, except every 0.2 seconds. Now, the Pi can handle all the messages and subscriber callbacks, but the whole program is still slow and not very responsive. I would like to prevent the callback methods from being executed at all, that way I don't have to start a new thread every time I receive a new message.

What would be the best way to prevent excessive callbacks? Is there any way to temporarily unsubscribe from a topic, then resubscribe? I know of the unregister() function, but that doesn't allow re-subscription.

I would rather not mess with the spacenav program, and I don't want to convert my Python code into C++ unless I have to.

Edit: Oops. For some reason I thought that the rospy.spin() method made RosPy start a new thread for each callback it received. Does it just call the functions asynchronously, then?

This is the callback for the '/spacenav/joy' topic:

# joystick callback for the spacenav
def spacenav_joy_callback(joy):
    global actuator_write_callback
    global control_roomba
    global spacenav_b0_pressed
    global spacenav_b1_pressed

    # rospy.loginfo('In the spacenav_twist callback')
    # rospy.loginfo('The first button is: %i', int(joy.buttons[0]))

    # in the future, we'll rely more on this basic type,
    # but for now, we're just using the buttons.
    if joy.buttons[0] and not spacenav_b0_pressed:
        # flip control between the roomba and the arm
        control_roomba = not control_roomba
        spacenav_b0_pressed = True
        if control_roomba:
            rospy.loginfo('Changing control to Roomba.')
            rospy.loginfo('Changing control to uArm.')

    if joy.buttons[1] and not spacenav_b1_pressed and not control_roomba:
        # toggle the actuator
        spacenav_b1_pressed = True

    if not joy.buttons[0] and spacenav_b0_pressed:
        # might have to create a threshold for bouncing
        spacenav_b0_pressed = False

    if not joy.buttons[1] and spacenav_b1_pressed:
        # might have to create a threshold for bouncing
        spacenav_b1_pressed = False

And this is the one for the '/spacenav/twist' callback:

# spacenav twist callback
def spacenav_twist_callback(twist):
    global roomba_twist_write
    global cmd_vel
    global roomba_vector_scale
    global roomba_angular_scale

    if control_roomba:
        cmd_vel.linear.x = roomba_vector_scale * twist.linear.x
        cmd_vel.linear.y = roomba_vector_scale * twist.linear.y
        cmd_vel.linear.z = roomba_vector_scale * twist.linear.z
        cmd_vel.angular.x = roomba_angular_scale * twist.angular.x
        cmd_vel.angular.y = roomba_angular_scale * twist.angular.y
        cmd_vel.angular.z = roomba_angular_scale * twist.angular.z

        # rospy.loginfo('Publishing a cmd_vel')
        # this is the equivalent of sending a uarm message

And this is keyboard_teleop_callback(), from above:

# teleop calllback
# does nothing fancy; just forwards the messages to the uSwift
def keyboard_teleop_callback(twist):
    global uswift_vector_write
    global uswift_vector_out
    global uswift_vector_scale

    # rospy.loginfo('Publishing a uSwift vector')

    uswift_vector_out.x = uswift_vector_scale * twist.linear.x
    uswift_vector_out.y = uswift_vector_scale * twist.linear.y
    uswift_vector_out.z = uswift_vector_scale * twist.linear.z


I have realized just by posting this that I can ... (more)

edit retag flag offensive close merge delete



rospy doesn't spawn a new thread for each callback, and I'm also a little confused about how you're solving this with a timer. I think it would be more clear if you could share your code, or a simplified (working) example that exhibits the same symptoms.

ahendrix gravatar image ahendrix  ( 2019-03-06 23:56:17 -0600 )edit

This does sound like either a throttle node or some other way of preventing messages from even reaching the subscriber would be better suited.

But I agree with @ahendrix that right now it's rather unclear what you're really doing.

gvdhoorn gravatar image gvdhoorn  ( 2019-03-07 03:51:06 -0600 )edit

@gvdhoorn: After writing that essay I think I'm just going to use the throttle node. It looks exactly like what I need. Thanks for the help!

jakeman555 gravatar image jakeman555  ( 2019-03-07 10:28:49 -0600 )edit

I would also suggest to take a look at your current architecture / dataflow. I haven't really tried to understand everything, but all the scaling of Twists and Joy msgs seems like it could be done by teleop_twist_joy or similar nodes.

Btw: a couple publishers/subscribers at 30 Hz should not be a problem for a Pi running @ > 1 GHz.

gvdhoorn gravatar image gvdhoorn  ( 2019-03-07 10:31:28 -0600 )edit

1 Answer

Sort by ยป oldest newest most voted

answered 2019-03-07 03:47:27 -0600

Okay by the sounds of it the processing in your callback is taking too long to run at 30 hz. @ahentrix is correct multiple threads are not being created but you're probably maxing out a cpu core with a single thread.

I'd recommend moving the processing out of your callback function. So the callback simply saves the content of the last message and the a slower timer callback then performs the processing on the most recent data.

Can you describe what processing you're doing on the 3d mouse data? It's possible this could be sped up if you can show us your code.

edit flag offensive delete link more



It looks like the C++ uSwift driver is probably calling some function to communicate with the uArm, so it may not be maxing out the core, but may just be waiting for I/O. The same recommendation still applies: have the callback store the message contents somewhere, and then communicate with the hardware from a slower timer.

ahendrix gravatar image ahendrix  ( 2019-03-07 11:14:15 -0600 )edit

You may also want to reduce the queue sizes on all of these nodes; that will allow ROS to drop old messages which will reduce the latency if one of the subscribers cannot keep up.

ahendrix gravatar image ahendrix  ( 2019-03-07 11:15:18 -0600 )edit

Ah yes... The IO. Actually, all of the IO is done through the single USB port on a Jetson. Now that you mention it, the problem may not be my Pi's RosPy at all. I'm going to try to factor in the throttle that gvdhoorn suggested. I think that will help.

jakeman555 gravatar image jakeman555  ( 2019-03-07 11:52:32 -0600 )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

1 follower


Asked: 2019-03-06 21:58:07 -0600

Seen: 422 times

Last updated: Mar 07 '19