pythonrosodometry

Python class has not attribute "x", using odometry data in a action server


I'm struggling making an action server where I have to use the subscribe to odometry data to use it in the action callback. I managed to get the pose data and I know its okay cause I have printed it inside the subscriber callback. My problem is that when I want to use this data outside I get this error:

ERROR] [1672904624.503538, 1251.206000]: Exception in your execute callback: 'move_turtle' object has no attribute 'xOdom'
Traceback (most recent call last):
  File "/home/simulations/public_sim_ws/src/all/actionlib/actionlib/src/actionlib/simple_action_server.py", line 289, in executeLoop
    self.execute_callback(goal)
  File "/home/user/catkin_ws/src/real_robot/scripts/action_server.py", line 51, in callback
    print(self.xOdom)
AttributeError: 'move_turtle' object has no attribute 'xOdom'

Here is my code:

#! /usr/bin/env python


from math import degrees, sqrt
import rospy
import actionlib
import numpy as np

from real_robot.msg import OdomRecordAction, OdomRecordResult, OdomRecordFeedback
from nav_msgs.msg import Odometry
from geometry_msgs.msg import Point32
from tf.transformations import euler_from_quaternion

odom_record_data=Point32()

class move_turtle(object):
    
    # create messages that are used to publish feedback and results
    _result = OdomRecordResult()
    _feedback = OdomRecordFeedback()

    def __init__(self):
        # creates the action server
        self._as = actionlib.SimpleActionServer("record_odom", OdomRecordAction, self.callback, False)
        self._as.start()
        self.ctrl_c = False
        self.rate = rospy.Rate(1)
        self.subodom = rospy.Subscriber('/odom', Odometry, self.call_record_odom)

    def call_record_odom(self,data):

        self.xOdom = data.pose.pose.position.x
        self.yOdom = data.pose.pose.position.y
        self.thOdom = data.pose.pose.orientation.z

    def callback(self,goal):

        rospy.loginfo("executing odometry record")
        i=0
        distance = 0
        dist_x = np.array((1, ), dtype = float)
        dist_y = np.array((1, ), dtype = float)
        orient_z = np.array((1, ), dtype = float)
        lap=100

        flag=False

        while distance<=lap:

            #calc traveled distance
            print(self.xOdom)
            dist_x= np.append(dist_x,self.xOdom)
            dist_y = np.append(dist_y,self.yOdom)
            orient_z = np.append(orient_z,self.thOdom)

            distance += sqrt( pow(dist_x[i] - dist_x[i-1], 2 ) + ( dist_y[i] - dist_y[i-1] )**2 ) 
            
            print("travelled distance:", distance)
            print(dist_x)
            
            # check that preempt (cancelation) has not been requested by the action client
            if self._as.is_preempt_requested():
                rospy.loginfo('The goal has been cancelled/preempted')
                # the following line, sets the client in preempted state (goal cancelled)
                self._as.set_preempted()
            

            # build and publish the feedback message
            self._feedback.current_total = distance
            self._as.publish_feedback(self._feedback)
            # the sequence is computed at 1 Hz frequency
            self.rate.sleep()
            flag=True
            i+=i

        if flag is True:
            self._result.list_of_odoms.x = dist_x
            self._result.list_of_odoms.y = dist_y
            self._result.list_of_odoms.z = orient_z

            self._as.set_succeeded(self._result.list_of_odoms)
            rospy.loginfo("Returning odometry values")

      
if __name__ == '__main__':
  rospy.init_node('Odom_record_node')   
  move_turtle()
  rospy.spin()

I dont understand whats the error telling me.

I would like to know first whats the meaning of the error.

second, I would like to append the xOdom data into the dist_x variable for each sensor reading.


Solution

  • The error is because your action is being called before your subscriber. This is because the subscriber is what actually creates the attribute that gets used in the action callback. If you have someone publish on the topic before the action is called the code will work fine. However, you should instead make sure your odom attributes are initialized in __init__ and before you set up a subscriber or action callback like so:

    def __init__(self):
        # creates the action server
        self.xOdom = None
        self.yOdom = None
        self.thOdom = None
        self._as = actionlib.SimpleActionServer("record_odom", OdomRecordAction, self.callback, False)
        self._as.start()
        self.ctrl_c = False
        self.rate = rospy.Rate(1)
        self.subodom = rospy.Subscriber('/odom', Odometry, self.call_record_odom)
    

    Then you may have to check that the values are initialized before using them.