Return msg.pose from Imported topic subscriber class

I have a simple subscriber class that I made but I’m having trouble using it.

#! /usr/bin/env python

import rospy
 #from std_msgs.msg import Int32 
from nav_msgs.msg import Odometry
#from sensor_msgs.msg import LaserScan

class OdomSub:

    def __init__(self):
        self.sub = rospy.Subscriber('/odom', Odometry, self.callback)

    def callback(self, msg): 
        if __name__ == '__main__':
        
            print msg.pose.pose.position.y
        return msg


if __name__ == "__main__":
    rospy.init_node('odom_subscriber')
    read_odom = OdomSub()
    rospy.spin()

And in another python file I want to return the msg from the callback whenever I call it. This is what I have.

import rospy
from odom_sub import OdomSub



rospy.init_node('odom_subscriber')
read_odom = OdomSub()
print read_odom
rospy.spin()

I want it to print the odometry msg but instead it prints this in the terminal.

 <odom_sub.OdomSub instance at 0x7f12e0f25128>

I’m still very new and i think i had this problem earlier in the course. Maybe i should be approaching this problem differently. If you could give me a hand to make it so I can use out the odometry msg from the other program that would be great. Thanks

Hello @ryleymcc,

In read_odom = OdomSub() what you actually have is the class OdomSub(). This is why you are getting this strange output because you are printing a Python class.

Instead of this, you should only print the Odometry message. You could try, for instance, something like this:

#! /usr/bin/env python

import rospy
from nav_msgs.msg import Odometry

class OdomSub:

    def __init__(self):
        self.sub = rospy.Subscriber('/odom', Odometry, self.callback)
        self.msg = Odometry()

    def callback(self, msg): 
        
        print msg.pose.pose.position.y
        self.msg = msg

    def get_msg(self):
        return self.msg

if __name__ == "__main__":
rospy.init_node('odom_subscriber')
read_odom = OdomSub()
rospy.spin()

Note that I’ve removed the if __name__ == '__main__': section inside the callback (since it doesn’t apply here). Also, I’m storing the Odometry message in the class variable self.msg and I’ve also created a specific function in the class in order to get this message.

import rospy
from odom_sub import OdomSub

rospy.init_node('odom_subscriber')
read_odom = OdomSub()
print read_odom.get_msg()
rospy.spin()

Now here, what I do is to call the function of the class which will return me the Odometry message instead of trying to print the whole class. Does this make sense to you?

Best,

I think I understand what’s going on in this code at some abstract level.
I am, however having trouble figuring out why I get zeros in all the attributes of the Odometry msg. I can see the constructor sets self.msg = Odometry(), but I don’t understand why self.msg doesn’t get updated with the odometry data coming from the robot in the callback() function.

In my mind, when I create an instance of the OdomSub class called read_odom, the constructor checks the /odom topic for a message and calls the callback() function when it receives data from the topic, and sets self.msg = the data received from the /odom in the callback() function. So when read_odom.get_msg() runs, it should output the most recent data from self.msg. I’m guessing this has something to do with the scope of the self.msg attribute in the get_msg() function, or the more likely scenario is this is way too complex for someone just starting to code lol.

I’m going to do more python class research.

I just looked at the project solution since I only started coding last week and I would be stuck for weeks.
It seems the code you gave me should have worked as I intended it to but for some reason I couldn’t get the newest odometry data, it would only return a blank odometry message.

That’s strange. Could you please check that the /odom topic actually contains something useful:

rostopic echo /odom

So I got the working code but it’s from one of the solutions. Yes, the /odom topic has good data.

Here is the code that gives me ALL ZEROS Odometry data. Notice that I commented out the callback print statement.

#! /usr/bin/env python

import rospy
from nav_msgs.msg import Odometry

class OdomSub:

def __init__(self):
    self.sub = rospy.Subscriber('/odom', Odometry, self.callback)
    self.msg = Odometry()

def callback(self, msg): 
    
    #print msg.pose.pose.position.y
    self.msg = msg

def get_msg(self):
    return self.msg

if __name__ == "__main__":
rospy.init_node('odom_subscriber')
read_odom = OdomSub()
rospy.spin()

It gets called by this test code. Note: I run this with the python file_name.py command in the terminal.

import rospy
from odom_sub import OdomSub

rospy.init_node('odom_subscriber')
read_odom = OdomSub()
print read_odom.get_msg()
rospy.spin()

This is the output of this code in the terminal.

user:~/catkin_ws/src/my_turtlebot_topics/src$ python odom_sub_test.py
header:
  seq: 0
  stamp:
    secs: 0
    nsecs:         0
  frame_id: ''
child_frame_id: ''
pose:
  pose:
    position:
      x: 0.0
      y: 0.0
      z: 0.0
    orientation:
      x: 0.0
      y: 0.0
      z: 0.0
      w: 0.0
  covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
twist:
  twist:
    linear:
      x: 0.0
      y: 0.0
      z: 0.0
    angular:
      x: 0.0
      y: 0.0
      z: 0.0
  covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

It prints once and then it stays running.

This is the working code.

    #! /usr/bin/env python

import time
import rospy
from nav_msgs.msg import Odometry

class OdomSub:
    def __init__(self):
        self.sub = rospy.Subscriber('/odom', Odometry, self.callback)
        self._odomdata = Odometry()

def callback(self, msg): 
    
   # print msg.pose.pose.position.y
    self._odomdata = msg
    #rospy.logdebug(self._laserdata)

def get_msg(self):
    """
    Returns the newest odom data

std_msgs/Header header
  uint32 seq
  time stamp
  string frame_id
string child_frame_id
geometry_msgs/PoseWithCovariance pose
  geometry_msgs/Pose pose
    geometry_msgs/Point position
      float64 x
      float64 y
      float64 z
    geometry_msgs/Quaternion orientation
      float64 x
      float64 y
      float64 z
      float64 w
  float64[36] covariance
geometry_msgs/TwistWithCovariance twist
  geometry_msgs/Twist twist
    geometry_msgs/Vector3 linear
      float64 x
      float64 y
      float64 z
   geometry_msgs/Vector3 angular
      float64 x
      float64 y
      float64 z
  float64[36] covariance

    """
    return self._odomdata

if __name__ == "__main__":
    rospy.init_node('odom_subscriber', log_level=rospy.INFO)
    read_odom = OdomSub()
    time.sleep(2)
    rate = rospy.Rate(0.5)
    #print read_odom.get_msg()
    #rospy.spin()

ctrl_c = False
def shutdownhook():
    # works better than the rospy.is_shut_down()
    global ctrl_c
    print "shutdown time!"
    ctrl_c = True

rospy.on_shutdown(shutdownhook)

while not ctrl_c:
    data = read_odom.get_msg()
    rospy.loginfo(data)
    rate.sleep()

It gets called by this test code. Note: I run this with the python file_name.py command in the terminal.

import time
import rospy
from odom_sub_working import OdomSub

rospy.init_node('odom_subscriber', log_level=rospy.INFO)
read_odom = OdomSub()
time.sleep(2)
rate = rospy.Rate(0.5)
#print read_odom.get_msg()
#rospy.spin()

ctrl_c = False
def shutdownhook():
    # works better than the rospy.is_shut_down()
    global ctrl_c
    print "shutdown time!"
    ctrl_c = True

    rospy.on_shutdown(shutdownhook)

while not ctrl_c:
    data = read_odom.get_msg()
    rospy.loginfo(data)
    rate.sleep()

This is the output of this code in the terminal.

user:~/catkin_ws/src/my_turtlebot_topics/src$ python odom_sub_working_test.py
[INFO] [1609453410.281640, 2110.769000]: header:
seq: 40788
stamp:
secs: 2110
nsecs: 740000000
frame_id: “odom”
child_frame_id: “base_footprint”
pose:
pose:
position:
x: 0.0182664104166
y: 0.000271202608295
z: -0.000247013469396
orientation:
x: 0.000391829501503
y: -0.00720836061116
z: 0.000984281916918
w: 0.999973458246
covariance: [1e-05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1000000000000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1000000000000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1000000000000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001]
twist:
twist:
linear:
x: 0.00016888573746
y: -0.000189647791245
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.000240832274845
covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

this prints repeatedly with the while loop.

I think it might have something to do with the while loop. Whenever I take it out of the while loop (to print once) it prints the blank message.

I found the issue(not really an issue it’s just the nature of the system)! I need to make sure i have a delay from when i call the get_msg() and print the msg.I wonder if there is a way round this quirk.

Yeah, I think the issue is trying to print once. The subscriber might not be ready by then so you get the zeros.

1 Like