0

I am basically trying to get a node that both publishes and subscribes.

Imagine I have two nodes, node1 publishes and count_node subscribes and publishes as well.

When node1 publishes "start count" I want count_node to start counting and publish the count value.

If node1 publishes to "stop count" while count_node is counting, I want count_node to stop the active count.

My attempt is below in code. I use ROS Melodic on Ubuntu 18.04. A similar problem is given in this question but the part I wanted the answer to was never answered.

My attempt thus far fails because when I receive the message to start the count the callback function calls a function (startCount) that uses a while loop to increment.

Thus, until the count it finished, count_node cannot process the message to stop the count and the stopCount function is called AFTER the count is finished, not while its counting.

Is there a way to do what I want?

Here is my attempt at count_node:

import rospy
from std_msgs.msg import String
from std_msgs.msg import Int32

def callback(data):
    rospy.loginfo(rospy.get_caller_id() + ' I heard: %s', data.data)
    if (data.data=="start count"):
        startSanitization()        
    elif (data.data=="stop count"):
        stopSanitization()

def startCount():
    percent = 0

    while percent < 101 :
        rospy.loginfo(percent)
        pub.publish(percent)
        percent = percent + 1
        rate.sleep()     

def stopCount():
    percent = 0
    rospy.loginfo(percent)
    pub.publish(percent)

def listener():

    # In ROS, nodes are uniquely named. If two nodes with the same
    # name are launched, the previous one is kicked off. The
    # anonymous=True flag means that rospy will choose a unique
    # name for our 'listener' node so that multiple listeners can
    # run simultaneously.

    #check for message on Topic
    rospy.Subscriber('brwsrButtons', String, callback)

    rospy.loginfo("hello")
    # spin() simply keeps python from exiting until this node is stopped
    rospy.spin()

if __name__ == '__main__':
    #create a unique node
    rospy.init_node('count_node', anonymous=True)
    #create a publisher object
    pub = rospy.Publisher('progress', Int32, queue_size=10)
    rate = rospy.Rate(10) #10Hz
    #start the subscribing and publishing process
    listener()

1 Answers1

1

Assuming your goal is to:

  1. Subscribe to brwsrButtons, which publishes a string, whose value can be start count or stop count
  2. If the value is start count, you want to increment percent value till it reaches 100 (not sure what you would like to do after that)
  3. If the value is stop count, you want to reset percent to 0
  4. Publish this percent on progress topic

Also assuming by startSanitisation and stopSanitisation you meant startCount and stopCount methods.

This can be done by having a global percent variable, which keeps a track of the percentage. In your callback method, just like you check string value, also check whether percentage is less than 100, and if it is, increment it by one, if not, put a 0. Then, in the main method, you have a loop which runs until you shutdown the node, which publishes percent to /progress topic.

Here's modified code:

#!/usr/bin/env python

#imports
import rospy
from std_msgs.msg import String, Int32

percent = 0

def callback(data):
    global percent
    rospy.loginfo(rospy.get_caller_id() + ' I heard: %s', data.data)
    if (data.data=="start count") and percent<100:
        percent += 1        
    elif (data.data=="stop count"):
        percent = 0 

if __name__ == '__main__':
    #create a unique node
    rospy.init_node('count_node', anonymous=True)
    #create a publisher object
    rospy.Subscriber('brwsrButtons', String, callback)
    pub = rospy.Publisher('progress', Int32, queue_size=10)
    rate = rospy.Rate(10) #10Hz
    #start the subscribing and publishing process
    while not rospy.is_shutdown():
        percentPubMsg = Int32()
        percentPubMsg.data = percent
        pub.publish(percentPubMsg)
        rate.sleep()

A better way is to use OOP:

#!/usr/bin/env python

#imports
import rospy
from std_msgs.msg import String, Int32

class NodeClass:
    def __init__(self):
        #initialize node
        rospy.init_node("count_node")
        self.nodename = rospy.get_name()
        rospy.loginfo("Started node %s" % self.nodename)
        
        #vars
        self.percent = 0

        #params and ROS params
        self.rate = rospy.get_param('~rate',10)

        #subscribers
        rospy.Subscriber("brwsrButtons", String, self.callback)
        #publishers
        self.progressPub = rospy.Publisher("progress", Int32, queue_size=10)

    def callback(self, msg):
        if msg.data == 'start_count' and self.percent<100:
            self.percent += 1
        else:
            self.percent = 0

    def spin(self):
        self.r = rospy.Rate(self.rate)
        while not rospy.is_shutdown():
            percentPubMsg = Int32()
            percentPubMsg.data = self.percent
            self.progressPub.publish(percentPubMsg)
            self.r.sleep()

if __name__ == '__main__':
    """main"""
    try:
        nodeClass = NodeClass()
        nodeClass.spin()
    except rospy.ROSInterruptException:
        pass
jash101
  • 189
  • 2
  • 13
  • Thank you, your assumptions were correct and this was helpful but I guess I didn't state it clearly; the counter should count on its own, once the message to start is received, not increment whenever a message to start is received. For example, imagine pushing and releasing a button and a display starts to count from 0 to 100. When I push the stop button the counter will stop counting and display the value it reached. Any idea how to do this? – Ian Daniel Sooknanan Jun 26 '21 at 14:38
  • It isn't quite clear to me. Could you add example data in your answer to explain your requirement better? – jash101 Jun 26 '21 at 15:33
  • I don't really have data but I can try a different explanation, the node will subscribe to brwsrButtons and look out for either a start count or stop count message. When start count is received I want the same node to start publishing numbers counting from 0 to 100 (with a frequency of 10Hz). This should happen until it reaches 100 or receives a stop count message. (I may need a reset option later on but I need to fix this first) Another way to look at it: Imagine a timer app on your phone or PC. You press start and it starts counting until you press stop. That is what I need to do. Thanks. – Ian Daniel Sooknanan Jun 26 '21 at 15:45