Service server doesn't get the subscriber_objects readings

I wrote this class for a laser/scan subscriber:

#! /usr/bin/env python
import rospy
from sensor_msgs.msg import LaserScan
import time

class laser_sub_class(object):
    def __init__(self):
        self.subscriber = rospy.Subscriber("/kobuki/laser/scan", LaserScan, self.laser_callback)
        self.ctrl_c = False
        rospy.on_shutdown(self.shutdownhook)
        self.rate = rospy.Rate(10)
       

    def shutdownhook(self):
        rospy.loginfo("laser shutdownhook! stopped!")
        self.ctrl_c=True
    
    def laser_callback(self, msg):
        rospy.loginfo("laser_callback")
        self.range= msg.ranges

if __name__=="__main__":
    rospy.init_node("laser_scan_subscriber")
    laserScan_sub_ob = laser_sub_class()
    rospy.spin()

and, I wrote this service_server that makes an object of mentioned class:

#! /usr/bin/env python

import rospy
from std_srvs.srv import Trigger, TriggerResponse
from laser_scan_subscriber import laser_sub_class
import time


def serv_callback(request):
    rospy.loginfo("The Service obstacle_avoidance_service has been called")
    laser_sub = laser_sub_class()
    #time.sleep(0.3)
    
    r = laser_sub.range
    r_min= min(r)
    min_ind = r.index(r_min)

    response = TriggerResponse()
    response.success = True
    response.message = "straight"

    if r_min <0.4:
        response.success = False
        if min_ind<360:
            response.message = "left"
        elif min_ind>360:
            response.message = "right"
        elif min_ind==360:
            if r[0]>r[710]:
                response.message = "right"
            else:
                response.message = "left"
    
    return response


rospy.init_node("obst_avoid_service_server")
rospy.Service("obstacle_avoidance_service", Trigger, serv_callback)
rospy.loginfo("Service /obstacle_avoidance_service Ready")

rospy.spin()

When I call this service, I get this:
‘laser_sub_class’ object has no attribute ‘range’.

why???

and another issue:
when I uncomment the time.sleep(0.3) in serv_callback, the code works properly, but why?!

The attribute is ranges not range. You also should look at you’re solution to the first quiz where turtlebot had to avoid the wall. In their you should see what else needs changing. One small hint this is not the place to make your responses think about python and the structures available for storing and returning the laser directions.

Thanks,

in self.range= msg.ranges, it is msg.ranges and it is correct.
then it is saved to object (from laser_sub_class class) in self.range.

but when I call the object in service_server file, I get this error:
‘laser_sub_class’ object has no attribute ‘range’.

the “range” in self.range can be any name and that is not the problem.

You can’t import an entire class unless you turn it into a module. At this point in the project starting to turn you’re classes into modules that can be imported like numpy or some other common python package will only add to the complexity of the project. Import methods from you’re classes as needed. In this case you could do something like:

def laser_directions(self):
directions…

put what you need in this method and then from laser_scan_sub import laser_directions. You will find this much easier.

laser_sub is an object from laser_sub_class class and it is made in the service call_back function (serv_callback).

in laser_sub_class , subscriber is called and in its call_back function (laser_callback), ranges of laser/scan are read and saved in self.range.

now, the object (laser_sub) starts the subscriber, but it doesn’t have the self.range.

thanks for your comment, but I don’t understand the problem yet!

The problem is in this statement:
from laser_scan_subscriber import laser_sub_class you can’t turn an entire class into an object which is what you are trying to do with this import and the statement laser_sub = laser_sub_class() you can only turn a class into a module and then import the methods from their. You should do this from laser_sub_class import laser_callback this just imports the method(function). So a method is basically a function that has been defined within a class. Write it once and import it into any number of other classes as well as using it within the class itself. This is the main concept of object oriented programming, write once use everywhere. So fill your class with the required method(functions) that can be imported with: from class A import method a, method b and so on theirs no limit to the number of methods you can import like this. All you’re classes should be written with this in mind. I hope this makes it a little more clear.

2 Likes

Thanks again,
But still, I don’t truly understand the problem!

this is a code from section “using python classes”:

#! /usr/bin/env python
import rospy
from std_srvs.srv import Empty, EmptyResponse
from bb8_move_circle_class import MoveBB8
def my_callback(request):
rospy.loginfo("The Service move_bb8_in_circle has been called")
movebb8_object = MoveBB8()
movebb8_object.move_bb8()
rospy.loginfo("Finished service move_bb8_in_circle")
return EmptyResponse()
rospy.init_node('service_move_bb8_in_circle_server')
my_service = rospy.Service('/move_bb8_in_circle', Empty , my_callback)
rospy.loginfo("Service /move_bb8_in_circle Ready")
rospy.spin() # mantain the service open.

bb8_move_circle_class is the python file with the class MoveBB8 defined in it,
and
movebb8_object = MoveBB8()
is like the code I’ve written and it is correct.

The difference is you’re not importing a class, you are just making an object of the class which python allows. Importing a class is where you run into trouble.
You can use a class to make an object like movebb8_object = MoveBB8 since you are not importing
this is the key difference. If you tried to import the MoveBB8 class into another class like this:
from bb8_class import MoveBB8 and tried to make an object like movebb8_object = MoveBB8() it will give you an error. I would need to so the full code regarding the time.sleep(0.3) and you should really start a new topic if you have new question. This not only helps you by keeping each question/answer focused on one topic, but also the community. Should someone else have a similar question. To sum it up:

  1. It’s OK to do this movebb8_object = MoveBB8() because no importing is required .
  2. If you were to try and import the MoveBB8() class and try movebb8_object = MoveBB8() it would fail with an error.
    This is what distinguishes a module from a class.
1 Like