09/12/2019: Topics Quiz error help

Hi,

This following is the code i came up with but i’m don’t know why i’m getting an Index Error. (IndexError: list index out of range).

 #! /usr/bin/env python

import rospy
from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import Twist

class Move_Robot:

    def __init__(self):
        rospy.init_node('topics_quiz_node')
        self.sub = rospy.Subscriber('/kobuki/laser/scan', LaserScan, self.callback)
        self.pub = rospy.Publisher('/cmd_vel', Twist, queue_size=1)
        self.laser_scan = LaserScan()
        self.move = Twist()
        self.rate = rospy.Rate(2)
        self.move.linear.x = 0.5
        self.move.angular.z = 0.5


    def callback(self,msg):
        laser_scan = msg

    def front_laser_scan(self):
        return self.laser_scan.ranges[360]

    def right_laser_scan(self):
        return self.laser_scan.ranges[0]

    def left_laser_scan(self):
        return self.laser_scan.ranges[719]

    def move_rob(self):
        while not rospy.is_shutdown():
            self.pub.publish(self.move)
            if self.front_laser_scan()>1:
                print("Moving forward!")
                return self.move.linear.x
            elif self.front_laser_scan()<1:
                print("Wall in front. Turning Left!")
                return self.move.angular.z==-0.5
            elif self.right_laser_scan()<1:
                print("Wall on the right. Turning Left!")
                return self.move.angular.z==-0.5
            elif self.left_laser_scan()<1:
                print("Wall on the left. Turning Right!")
                return self.move.angular.z
            else:
                self.move.linear.x = 0
            self.rate.sleep()

Run = Move_Robot()
Run.move_rob()

Kindly help me understand why I’m getting this Index Error.

Thanks.

Hi @sanjayatj,

You’re getting the Index Error because self.laser_scan is not properly assigned the laser scan message - it is NOT a list. You can fix that by correcting the callback function:

def callback(self,msg):
        # laser_scan = msg - the `laser_scan` here is not the same as self.laser_scan
        self.laser_scan = msg

Also, here a few suggestions to ensure your program will work properly:

  • Set angular movement to zero in def init: self.move.angular.z = 0.0 so that your robot does not rotate right at the beginning!
  • Replace all return statements in the if block with assignment statements. For example, instead of
return self.move.linear.x

write

self.move.linear.x = 0.2 # or whatever value you wish

Thank you @bayodesegun .:blush:

After making the above changes i’m still getting the same error. I’m once again adding the code here and a screenshot of the error. I once again request for your guidance.


#! /usr/bin/env python

import rospy
from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import Twist

class Move_Robot:

def __init__(self):
    rospy.init_node('topics_quiz_node')
    self.sub = rospy.Subscriber('/kobuki/laser/scan', LaserScan, self.callback)
    self.pub = rospy.Publisher('/cmd_vel', Twist, queue_size=1)
    self.laser_scan = LaserScan()
    self.move = Twist()
    self.rate = rospy.Rate(2)
    self.move.linear.x = 0.0
    self.move.angular.z = 0.0

def callback(self,msg):
    self.laser_scan = msg

def front_laser_scan(self):
    return self.laser_scan.ranges[360]

def right_laser_scan(self):
    return self.laser_scan.ranges[0]

def left_laser_scan(self):
    return self.laser_scan.ranges[719]

def move_rob(self):
    while not rospy.is_shutdown():
        self.pub.publish(self.move)
        if self.front_laser_scan() > 1.0:
            print("Moving forward!")
            return self.move.linear.x == 0.5
        elif self.front_laser_scan() < 1.0:
            print("Wall in front. Turning Left!")
            return self.move.angular.z==-0.5
        elif self.right_laser_scan() < 1.0:
            print("Wall on the right. Turning Left!")
            return self.move.angular.z==-0.5
        elif self.left_laser_scan() < 1.0:
            print("Wall on the left. Turning Right!")
            return self.move.angular.z ==0.5
        else:
            self.move.linear.x = 0
        self.rate.sleep()

Run = Move_Robot()
Run.move_rob()


Oh, I see now that rospy.spin() is missing. We need this for the subscriber to work properly. Try modifying the code as follows (you might need to tweak the numbers to get a smooth run):

def __init__(self):
    rospy.init_node('topics_quiz_node')
    self.sub = rospy.Subscriber('/kobuki/laser/scan', LaserScan, self.callback)
    self.pub = rospy.Publisher('/cmd_vel', Twist, queue_size=1)
    self.laser_scan = LaserScan()
    self.move = Twist()
    self.move.linear.x = 0.2
    self.move.angular.z = 0.0
    rospy.spin()

def callback(self,msg):
        self.laser_scan = msg
        if self.front_laser_scan() > 1.0:
            print("Moving forward!")
            self.move.linear.x = 0.5
        elif self.front_laser_scan() < 1.0:
            print("Wall in front. Turning Left!")
            self.move.angular.z = -0.5
        elif self.right_laser_scan() < 1.0:
            print("Wall on the right. Turning Left!")
            self.move.angular.z = -0.5
        elif self.left_laser_scan() < 1.0:
            print("Wall on the left. Turning Right!")
            self.move.angular.z = 0.5
        else:
            self.move.linear.x = 0
        
        self.pub.publish(self.move)

def front_laser_scan(self):
    return self.laser_scan.ranges[360]

def right_laser_scan(self):
    return self.laser_scan.ranges[0]

def left_laser_scan(self):
    return self.laser_scan.ranges[719]

Run = Move_Robot()

@bayodesegun Oh! got it. I see that you have added the conditions for moving the robot in the call back function. I understand the logic. But, was my program, where i defined a separate function for move robot wrong? I don’t understand why it didn’t work then.

1 Like

Hi @sanjayatj,

Glad you got it!

Not really, the main reason is that the subscriber was not working because you didn’t call rospy.spin() anywhere.

I made the other changes because I thought the code was not optimal and might not work well. Here’s why:

I think it’s best for the robot to decide which direction to go immediately it gets feedback from the laser. In order for this reaction to as realtime as possible, this decision logic is better placed in the callback function, since the callback function gets called when a new message arrives from the sensor. Or you could create a separate function that contains the code to move the robot (everything in the latest callback code except the first line), but only call it from the callback function.

move_rob was acting somewhat independent of the callback function, and will definitely be out of sync with it sometimes, even if it gets the updated laser message via the instance variable self.laser_scan.

Another important thing about move_rob was that it created another loop for the robot to react (using rospy.Rate), whereas there’s already an existing “loop” in the subscriber sending the laser message at a given rate to the callback function. I think it’s better to have the robot react to the beat of the subscriber rather than create its own. Imagine someone sending you a ball and you have to catch it…

This explanation is quite long, I hope it’s not too boring and you understand the reasoning. Please don’t hesitate to ask if you have any doubts.

PS: I edited the callback a little - the publishing statement should come last.

@bayodesegun Thank you very much for your detailed explanation. It was not boring at all. I rather prefer this kind of clarification and for that I thank you very much.
I totally understand your explanation given that move_rob runs a separate loop from the call back function, because I tried just adding rospy.spin() to my previous program and error was not showing this time. But, as you correctly pointed out there was nothing happening. Probably the program is not moving on to the move_rob loop from the call back. It worked once i put the code in the call back loop.

Thank you once again for your detailed reply! :blush:

1 Like