Using data from callback function in other scripts - No global variables

Hello.
My question is on obtaining the data used by the callback function of a subscriber later in the scripts or as an attribute of a class without having to use global variables. I find it weird that when I create a subscriber there are no methods that allows me to then later obtain the return statement from the callback function or the data used in the callback function. A simple subscriber could be:
sub = rospy.Subscriber("/cmd_vel", Twist, callback)
What I want to be able to do is:
sub.callback — (Should give me the return statement I have specified in the callback)
or
sub.data — (Should give me the data used by the callback function)

But none of these options are available to me. I have seen several posts here on the forum suggesting that people use global variables (which will mess up any larger node structure) or classes, but using classes still only gives access to the data within the class. To illustrate my point I will post a script (there should be no need to run this script):

#!/usr/bin/env python
import rospy
from std_msgs.msg import Int64
from std_srvs.srv import SetBool
from geometry_msgs.msg import Twist

class NumberCounter:
    def __init__(self):
        self.counter = 0
        self.number_subscriber = rospy.Subscriber("/cmd_vel", Twist, self.callback_number)
        self.reset_service = rospy.Service("/reset_counter", SetBool, self.callback_reset_counter)
    def callback_number(self, msg):
        self.counter += msg.linear.x
        self.asd = 'IAmUsedInTheOtherCallback'
        new_msg = Int64()
        new_msg.data = self.counter

    def callback_reset_counter(self, req):
        print(self.asd)
        if req.data:
            self.counter = 0
            return True, "Counter has been successfully reset"
        return False, "Counter has not been reset"

if __name__ == '__main__':
    rospy.init_node('number_counter')
    NumberCounter()
    # Now I want to use the data used in the callback_number function but there is no way for me to call it 
    #either as an attribute (because I cannot return outside the function) or
    #as a method (because I do not have the input 'msg')
    #NumberCounter.data <----- How do I get this so I can use it in other statements?
    rospy.spin()

Looking at the comment just above rospy.spin() I want to be able to obtain either the raw data used in the callback function or the return value of that callback but there seems to be no way to do it?

The last solution we arrived at was to publish within every callback and then have new subscribers subscribe to that topic, this is the only way we could get around this issue. It does however seem foolish to have to create a whole new publisher and subscriber simply to use the data or return statement of the callback function.

To sum it up:
If I have a class ‘NumberCounter’ and I create a subscriber within that class with a certain callback function. I now wish to use the result of that callback function in a different script or class by importing my NumberCounter class and calling NumberCounter(). But how can I use the NumberCounter.result?
(where NumberCounter.result is the result or data from the callback function)

I should note that I am novice python programmer so something may have gone over my head.

Hello @sjeppesen ,

In your example, you could access the counter value like this:

if __name__ == '__main__':
    rospy.init_node('number_counter')
    num_counter = NumberCounter()
    print (num_counter.counter)
    rospy.spin()

Hello Alberto. Thank you for the reply. I tried this

if __name__ == '__main__':
    rospy.init_node('number_counter')
    num_counter = NumberCounter()
    print (num_counter.asd)  #I used this instead of .counter because it is not in the __init__
    rospy.spin()

But I get an error.
AttributeError: NumberCounter instance has no attribute ‘asd’

It seems I cannot access the data from my callback function like this. Could you please elaborate.
Best from Simon.

Hello @sjeppesen ,

If it’s not an attribute of the class (declared inside the constructor) you won’t be able to access it from outside the class. So, I would just declare asd as an attribute and then modify its value inside the callback if needed.

Hello again. Thanks for getting back to me. Unfortunately if i make it an attribute of the class (for example in the _init_ method) it is not updated later if I update it in the callback (i.e. it does not return the updated value to my attribute of the _init_ class. If you would be so kind to provide a quick example on how this can be done I would very much appreciate it. Have a good day.

Hi,

What you are trying to do is complicated and may never work reliably. What I would recommend is that you reconsider your design. What exact problem are you trying to solve?

Precisely, you can’t “use data from a callback function in other scripts” per se, class-based or procedural, global variables or not. Depending on what you want to do, you could create a service or an action that contains a subscriber and returns what you want when called.

Hello Bayodesegun. Thank you for the response. That’s good to know, I guess this is very specific to ros, as you would otherwise in python simply add a return statement to a function and save that using something like: saved_data = my_function_call(). We ended up using publishers to publish the data ‘out’ of the callback and then subscribe to it in other callbacks. I guess this way is also more in line with the ros way of thinking in terms of nodes and topics.
I do however still find it odd that the original programmers of ros did not add an attribute of a subscriber called ‘data’ that you could call using something like sub.data().
Global variables do solve the problem but as you mentioned it’s not reliable. Thanks again. Have a good day.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.