Carlton Banks
Carlton Banks

Reputation: 365

Writing a ros node with both a publisher and subscriber?

I am currently trying to make a ROS node in Python which has both a subscriber and a publisher.

I've seen examples where a message is published within the callback, but I want it to "constantly" publish messages, and perform callbacks when it is the case.

Here is how I do it now:

#!/usr/bin/env python

import rospy
from std_msgs.msg import Empty
from std_msgs.msg import String
import numpy as np

pub = rospy.Publisher('/status', String, queue_size=1000)

def callback(data):
    print "Message received"

def listener():

    rospy.init_node('control', anonymous=True)

    rospy.Subscriber('control_c', Empty, callback)
    rospy.spin()

if __name__ == '__main__':
    print "Running"
    listener()

So where should I publish?

Upvotes: 10

Views: 29148

Answers (2)

luator
luator

Reputation: 5017

Simply replace rospy.spin() with the following loop:

while not rospy.is_shutdown():
    # do whatever you want here
    pub.publish(foo)
    rospy.sleep(1)  # sleep for one second

Of course you can adjust the sleep duration to whatever value you want (or even remove it entirely).

According to this reference subscribers in rospy are running in a separate thread, so you don't need to call spin actively.

Note that in roscpp (i.e. when using C++) this is handled differently. There you have to call ros::spinOnce() in the while loop.

Upvotes: 5

Vtik
Vtik

Reputation: 3130

Well, I think there's a lot of solutions here, you could even make use of a python process, but what I'm proposing is a ROS approach using a ros Timer.

I am not really that efficient in python but this code may gave you a heads up.

#!/usr/bin/env python

import rospy
from std_msgs.msg import Empty
from std_msgs.msg import String
import numpy as np

last_data = ""
started = False
pub = rospy.Publisher('/status', String, queue_size=1000) 

def callback(data):
    print "New message received"
    global started, last_data
    last_data = data
    if (not started):
        started = True

def timer_callback(event):
    global started, pub, last_data
    if (started):
        pub.publish(last_data)
        print "Last message published"


def listener():

    rospy.init_node('control', anonymous=True)

    rospy.Subscriber('control_c', String, callback)
    timer = rospy.Timer(rospy.Duration(0.5), timer_callback)

    rospy.spin()    
    timer.shutdown()

if __name__ == '__main__':
    print "Running"
    listener()

Here, your callback will update the message and your timer will fire up every 0.5sec and publishes the last data received.

you can test this code by publishing data on "/contriol_c" every 3 seconds and configuring you timer to 0.5 sec. start an echo on /status

$ rostopic echo /status

and you'll got your message published on a 2 Hz rate.

Hope that helps !

Upvotes: 6

Related Questions