Help Sphero Robot Get Out of Maze


Coding publisher and subscriber were easy but they are getting confusing for me because of rospy.spin(), rospy.on_shutdown() and other related functions that are used many times in different ways in tutorials and lectures. I was about to provide some samples from lectures and tutorials but it is also messy so that I will ask rather simple questions to get rid of my doubts.

  1. I understand that rospy.spin() is used when a subscriber is involved. But where in the code? Where should it be placed? Can i place it in a subscriber at a random place? Also, why did not you use spin function in odom_topic_subscriber in this tutorial? That collapses what I learnt in ROS courses.

  2. rospy.on_shutdown() is used but how and why? For instance as below, shutdownhook function is passed to rospy.on_shutdown().

    2.1. Is that just to modify variable of ctrl_c as True so that the while loop is interrupted? If so, I have read on internet that rospy.on_shutdown() function does not take any argument. Is passing shutdownhook function to on_shutdown for preventing getting odometry data and nothing related with shutting down the node?

    2.2. rospy.on_shutdown() is inside the main function and is not called from another function. As soon as the program runs, isn’t it executed and shutdown the rosnode? Or, is rospy.on_shutdown() called ONLY WHEN ctrl+c is pressed by the user? In short, how and when is it called?

    ctrl_c = False
    def shutdownhook():
    # works better than the rospy.is_shut_down()
      global ctrl_c
      print "shutdown time!"
      ctrl_c = True
    while not ctrl_c:
      data = odom_reader_object.get_odomdata()
  1. For the code below, you already subscribe to /odom topic and call topic_callback function. That is to read Odometry data. So why did you also create get_odomdata function and call it from while loop in the main function? That seems to me like doing the same thing with more function. I mean, I think we could achieve the same result without while loop in the main function since we subscribe to /odom topic. Also, i did not understand this assignment: self._odomdata = msg. Isn’t the msg already Odometry(). Can you please elaborate the code below line-by-line if possible? I really need an answer. I am so confused.

    class OdomTopicReader(object):
        def __init__(self, topic_name = '/odom'):
           self._topic_name = topic_name
           self._sub = rospy.Subscriber(self._topic_name, Odometry, self.topic_callback)
           self._odomdata = Odometry()
        def topic_callback(self, msg):
           self._odomdata = msg
        def get_odomdata(self):
           return self._odomdata

Thanks in advance.

Hi @enderayhan,

I’ll try to address your questions.

Check this link / explanation:
You basically use it to keep your node running, when you have a node that doesn’t do anything. A topic subscriber is a good example. The subscribers run in their own thread and execute their callbacks. The main node you launched has to be open and running though, otherwise the script would stop. So you use rospy.spin() to keep it in an infinite loop

  1. Usually you use infinite loops like this:
    while rospy.is_shutdown() == false:
    This will run loops, as long as rospy is running. So the code breaks, when you interrupt ROS. Sometimes this doesnt work for some loops, so we set a global variable ctrl_c, which gets set to True, when we shutdown ros. And we check in our loops, that that variable is false. This is especially the case, when we have multiple threads running, as ctrl-c (pressing it on the keyboard), usually only aborts the active thread we started, and not necessarily the threads that our code started.

rospy.on_shutdown() gets triggered, when ROS shuts down, which is usually done by either pressing ctrl-c or the code finishing

This code is not meant to be run alone, like the topic subscribers before, but for the class to be imported by our main program.

class OdomTopicReader(object): #create a class object, we can import into main program
    def __init__(self, topic_name = '/odom'): #initialize class
       self._topic_name = topic_name
       self._sub = rospy.Subscriber(self._topic_name, Odometry, self.topic_callback) #create subsciber
       self._odomdata = Odometry() # create a class variable of the type Odometry()

# the subscriber runs in its own thread and calls this function every time, it receives a new topic
    def topic_callback(self, msg): 
       self._odomdata = msg #the odometry data gets saved in our class variable

#in our main code we can call this function, to immediately return the last odometry 
#that was received
    def get_odomdata(self):
       return self._odomdata
1 Like

Dear @simon.steinmann91,

To be honest, I could not relate your answers to my questions exactly.

  1. I was trying to ask that why didn’t you use spin function in the odom_topic_subscriber in sphero tutorial? is it because the node does something outside of it callback function? Here is not clear :confused:
  1. The question is:
  1. you define self._odomdata = Odometry()inside init function. You also define self._odomdata = msg inside callback funtion. What is the difference? I mean self._odomata = Odometry() becomes pointless. Am i wrong? Soo confused.
1 Like
  1. Because you import the odom_topic_subscriber class into your main code (which already has a rosnode), you dont need to keep a node open with rospy.spin.

  2. Read section 2 of this link:

  3. It is possible for us to call
    OdomTopicReader.get_odomdata() from our main code, BEFORE the first callback from the subscriber comes in. So it is good practice to initialize the variable with the correct data structure, which is Odometry() in this case


Adding to what @simon.steinmann91 already said:

  1. Actually, it’s not mandatory to use rospy.spin(), for ROS Python subscribers (it’s mandatory for C++). It can be replaced by the kind of while loop in the code above (just to keep the main program from exiting). See this post for further clarifications:

  2. rospy.on_shutdown() is not meant to be called the way you think. It’s a “configuration” statement that tells ROS the function to be called when it’s shutting down. In other words, when we write rospy.on_shutdown(shutdownhook), we are saying “Hey ROS, whenever you are shutting down please call the shutdownhook() function!”

  3. The line self._odomdata = Odometry() initializes self._odomdata with an empty odometry message while the line self._odomdata = msg actually assigns it a real odometry message from the subscriber.

1 Like

@bayodesegun Thanks a lot. That’s the understandable answer !