Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

Preventing excessive callbacks in RosPy

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 to be 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.

Preventing excessive callbacks in RosPy

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 to be 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.')
        else:
            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
        actuator_write_callback(Bool(not uswift_actuator_out.data))

    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

        roomba_twist_write.publish(cmd_vel)
        # rospy.loginfo('Publishing a cmd_vel')
    else:
        # this is the equivalent of sending a uarm message
        keyboard_teleop_callback(twist)

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

    uswift_vector_write.publish(uswift_vector_out)

I have realized just by posting this that I can reduce the load by only using the joy callback and getting rid of the twist subscriber. But I would still like to know how to prevent callbacks in the future, in case next time the message info of the two topics is mutually exclusive.


My timer is actually not in my RosPy file; it's in my C++ uArm SwiftPro driver. The above program simply receives a message, repackages its data, and forwards a new message with the data to wherever it needs to go (in this case, my uSwift driver). What I do in my uSwift driver is set a variable to true every 0.2 seconds with a timer, then when I receive a message from the RosPy node, if that variable is true, I parse the message and send the data to the uSwift. (I know this is a terrible way of going about things, but this uSwift and this Pi are all my senior design team has. I'm trying to make do.)

This is my C++ uSwift driver. Note the use of the global variable accept_vector:

void vector_write_callback(const geometry_msgs::Vector3& msg_in)
{
    if (accept_vector && 
        (abs(msg_in.x) > 0.001 || 
         abs(msg_in.y) > 0.001 || 
         abs(msg_in.z) > 0.001))
    {
        // ...
    }
}

// ...

void vector_accept_callback(const ros::TimerEvent&)
{
    // ROS_DEBUG("Opening the uSwift floodgate");
    accept_vector = true;
}

int main(int argc, char** argv)
{
    ros::init(argc, argv, "pnr_swiftpro");
    ros::NodeHandle nh("pnr_swiftpro");
    // ...
    // the vector timer update manager (ensure the vectors don't flood the uSwift)
    ros::Timer vec_timer_manager
        = nh.createTimer(
            ros::Duration(vector_flood_delay), 
            vector_accept_callback);
    // ...
}

The C++ code above actually works just fine. However, I need to figure out a way to keep the Pi from getting flooded with messages now. I thought about doing it with a timer, like in the C++ above, but I would rather have the callbacks not called at all.

Thanks @gvdhoorn for the suggestion. I had never heard of topic_tools. I'll go check it out.