Help with code... Unit 5, topics_quiz

Hi!

In the last days, I started the “ROS Basics In 5 Days” Course. Now, I’m in unit 5. Unfortunately, I have been stuck in the topics_quiz. I tried, again and again, debugging my code according to the error messages printed by: roslaunch topics_quiz topics_quiz.launch. Below is my code:

#! /usr/bin/env python

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

rospy.init_node('topics_quiz_node')

def give_distances(msg):
    global distance_read
    distance_read=msg.ranges
    return distance_read

class distances:
    def __init__(self):
        self.distances=[]

    def measurer(self,readings):  
        self.take=self.distances
        self.final_array=self.take.append(readings)
        self.length=len(self.final_array)
        return length

sub = rospy.Subscriber('/kobuki/laser/scan',LaserScan, give_distances)

pub = rospy.Publisher('/cmd_vel',Twist)

rate = rospy.Rate(10)

while not rospy.is_shutdown():

    give_distances(LaserScan)
    dist=distances()
    needed_number=dist.measurer(distance_read)
    velocity=Twist()

    distance_front=distance_read[needed_number//2]
    distance_right=distance_read[needed_number-needed_number]
    distance_left=distance_read[needed_number-0]
    velocity_linear=velocity.linear.x
    velocity_angular=velocity.angular.z

    def initialization():
        velocity_linear=0
        velocity.linear.y=0
        velocity.linear.z=0
        velocity_angular=0
        velocity.angular.x=0
        velocity.angular.y=0
        return 0

    initialization()

    while distance_front>1:
        velocity_linear=0.5
        velocity_angular=0
    while distance_front<1:
        velocity_linear=0.5
        velocity_angular=0.7
    while distance_right<1:
        velocity_linear=0
        velocity_angular=0.7
    while distance_left<1:
        velocity_linear=0
        velocity_angular=-0.7

    rate.sleep()

The error message I get, at the moment, is the following:

Traceback (most recent call last):
  File "/home/user/catkin_ws/src/topics_quiz/src/topics_quiz.py", line 34, in <module>
    needed_number=dist.measurer(distance_read)
  File "/home/user/catkin_ws/src/topics_quiz/src/topics_quiz.py", line 21, in measurer
    self.length=len(self.final_array)
TypeError: object of type 'NoneType' has no len()

Any help would be appreciated!

Hi @Eliasmys ,

Welcome to this Community!

Quick question(s): When you decided to use classes, why do you have most of the functions outside the class? Also, why make a class for just two functions that barely do anything?

After reading through your code, I strongly believe that you need a primer on Python programming.
Please take the Python for Robotics course offered by this website.

Anyways, your solution(s) here:

  1. give_distances(LaserScan):
    This line will not work. LaserScan is the laser scanner message and it usually contains an empty dataframe - all of them will be zeros. When you pass this to give_distances() function, you are basically passing a data with zeros, naturally, it would return distance_read as zeros.
    EDIT: LaserScan is just the class for laser scan messages, LaserScan() initializes a message.
  2. dist = distances():
    You have initialized the distances class with basically no data in it.
  3. needed_number = dist.mesaurer(distance_read):
    This line will not work, because you did not store the result in step 1. Therefore, distance_read contains nothing (not even zeros). Therefore, when distances.measurur() function executes, self.distances is an empty list. Therefore when you do self.length = len(self.final_array) you are calculating the length of an empty list. Empty list by definition in python, is a NoneType, hence you get 'NoneType' has no len() error.

I hope you understood my explanation!

Regards,
Girish

@girishkumar.kannan Thank you, for your answer! And it’s nice to be a part of this community!

Initially, I didn’t use classes and didn’t have any reason to do that. As for why I made a class, I searched through the internet for a solution to my problems and some answers suggested the use of classes. I woudn’t do that by my own.

The truth is that I’m fairly new to Python programming and I need more hours and interaction to get used to it. I hope you understand that. I took the “Python for Robotics” Course and I may go through it again.

Here, below, is my original code. Is this any better:

#! /usr/bin/env python

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

rospy.init_node('topics_quiz_node')

sub = rospy.Subscriber('/kobuki/laser/scan',LaserScan)

pub = rospy.Publisher('/cmd_vel',Twist,queue_size=1)

rate = rospy.Rate(10)

while not rospy.is_shutdown():

    distance_read=LaserScan().ranges
    velocity=Twist()

    distance_front=distance_read[len(distance_read)//2]
    distance_right=distance_read[len(distance_read)-len(distance_read)]
    distance_left=distance_read[len(distance_read)-0]
    velocity_linear=velocity.linear.x
    velocity_angular=velocity.angular.z

    def initialization():
        velocity_linear=0
        velocity.linear.y=0
        velocity.linear.z=0
        velocity_angular=0
        velocity.angular.x=0
        velocity.angular.y=0
        return 0

    initialization()

    while distance_front>1:
        velocity_linear=0.5
        velocity_angular=0
    while distance_front<1:
        velocity_linear=0.5
        velocity_angular=0.7
    while distance_right<1:
        velocity_linear=0
        velocity_angular=0.7
    while distance_left<1:
        velocity_linear=0
        velocity_angular=-0.7

    rate.sleep()

?

Now, I get this error:

Traceback (most recent call last):
  File "/home/user/catkin_ws/src/topics_quiz/src/topics_quiz.py", line 20, in <module>
    distance_front=distance_read[len(distance_read)//2]
IndexError: list index out of range

.

Or this:

#! /usr/bin/env python

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

rospy.init_node('topics_quiz_node')

sub = rospy.Subscriber('/kobuki/laser/scan',LaserScan)

pub = rospy.Publisher('/cmd_vel',Twist,queue_size=1)

distance_read=LaserScan().ranges
velocity=Twist()

distance_front=distance_read[len(distance_read)//2]
distance_right=distance_read[len(distance_read)-len(distance_read)]
distance_left=distance_read[len(distance_read)-0]
velocity_linear=velocity.linear.x
velocity_angular=velocity.angular.z

def initialization():
    velocity_linear=0
    velocity.linear.y=0
    velocity.linear.z=0
    velocity_angular=0
    velocity.angular.x=0
    velocity.angular.y=0
    return 0

initialization()

while distance_front>1:
    velocity_linear=0.5
    velocity_angular=0
while distance_front<1:
    velocity_linear=0.5
    velocity_angular=0.7
while distance_right<1:
    velocity_linear=0
    velocity_angular=0.7
while distance_left<1:
    velocity_linear=0
    velocity_angular=-0.7

rospy.spin()

.

Hi @Eliasmys ,

Honestly, this code is better than the earlier one that you posted, but still bad.

My observations:

  1. You are missing the subscriber callback. Therefore you will never receive laser scan messages.
  2. LaserScan() just initializes a laser scan empty message. If you try to read data from initialized message, you will only get zeros and empty strings (strings with no characters).
  3. You do all initialization(s) before your loop.
  4. Do not define functions within loops - these functions are called inner functions - they are used for abstractions. You do not want to do that unless you really know what you are doing!
  5. For robot movement, instead of using 4 while loops, you can use one while loop with 4 if(else) conditions. If you use 4 consecutive while loops, once the loop finishes, it will exit. So your robot might move just 4 times or probably get stuck in one of the loops.

Regards,
Girish

@girishkumar.kannan

Is this any better? I’m really sorry to bother you, but I’m really stuck. I suppose there are some things, technical and functional, that I do not yet know. Could you provide any detailed feedback? I won’t push you, but I’d really appreciate it if you did. I won’t bother anymore after this. I will work in order to get better at programming etc. Thanks, in advance.

#! /usr/bin/env python

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

rospy.init_node('topics_quiz_node')

def give_distances(msg):
    global distance_read=LaserScan().ranges
    return distance_read

sub = rospy.Subscriber('/kobuki/laser/scan',LaserScan,give_distances)

pub = rospy.Publisher('/cmd_vel',Twist,queue_size=1)

give_distances()
velocity=Twist()

distance_front=distance_read[len(distance_read)//2]
distance_right=distance_read[len(distance_read)-len(distance_read)]
distance_left=distance_read[len(distance_read)-0]
velocity_linear=velocity.linear.x
velocity_angular=velocity.angular.z

def initialization():
    velocity_linear=0
    velocity.linear.y=0
    velocity.linear.z=0
    velocity_angular=0
    velocity.angular.x=0
    velocity.angular.y=0
    return 0

initialization()

while distance_read!=[]:
    if distance_front>1:
        velocity_linear=0.5
        velocity_angular=0
        give_distances()
    elif distance_front<1:
        velocity_linear=0.5
        velocity_angular=0.7
        give_distances()
    if distance_right<1:
        velocity_linear=0
        velocity_angular=0.7
        give_distances()
    if distance_left<1:
        velocity_linear=0
        velocity_angular=-0.7
        give_distances()
    give_distances()

rospy.spin()

Now, I get this:

Traceback (most recent call last):
  File "/home/user/catkin_ws/src/topics_quiz/src/topics_quiz.py", line 18, in <module>
    give_distances()
TypeError: give_distances() missing 1 required positional argument: 'msg'

.

Hi @Eliasmys ,

Here is one way to solve it. I am only giving this to you to get you motivated and learn from this.

#! /usr/bin/env python
# the above line is called a "shebang"
# if you forget to include that line, your code will not run!

# imports for the program
import rospy
from geometry_msgs.msg import Twist
from sensor_msgs.msg import LaserScan

# define scan callback before initializing scan subscriber
# this is required only if you do not use classes
# if you use classes then you can have the function within your class
def scan_callback(msg):
    global range_left, range_front, range_right
    range_left = msg.ranges[719]
    range_front = msg.ranges[360]
    range_right = msg.ranges[0]
    return None

# initialize rospy and relevant rates, publishers and subscribers
rospy.init_node("topics_quiz_node")
rate = rospy.Rate(2)
pub = rospy.Publisher("/cmd_vel", Twist, queue_size=1)
sub = rospy.Subscriber("/kobuki/laser/scan", LaserScan, scan_callback)

# initialize your process variables (and constants)
linear_speed = 0.15
angular_speed = 0.30
range_left = 0.0
range_front = 0.0
range_right = 0.0

# program your main function
# try-except block is used for exception handling
while not rospy.is_shutdown():
    try:
        # initialize a twist command to hold the values
        twist_cmd = Twist()

        # change the data of twist command appropriately 
        # with respect to the movement of the robot
        if (range_front > 1.0):
            twist_cmd.linear.x = linear_speed
            twist_cmd.angular.z = 0.0
            print("No Obstacle : Moving Straight")
        else:
            twist_cmd.linear.x = linear_speed
            twist_cmd.angular.z = angular_speed
            print("Obstacle Ahead : Turning Left")

        if (range_right < 1.0):
            twist_cmd.linear.x = linear_speed
            twist_cmd.angular.z = angular_speed
            print("Obstacle on Right : Turning Left")
        else:
            pass

        if (range_left < 1.0):
            twist_cmd.linear.x = linear_speed
            twist_cmd.angular.z = -angular_speed   # notice the (-) sign!
            print("Obstacle on Left : Turning Right")
        else:
            pass

        # publish the twist command to the cmd_vel topic
        pub.publish(twist_cmd)
        # sleep for specified rate for the robot to move
        rate.sleep()
    except:
        pass

# End of Code

Go through code and let me know if you understood it.

Don’t feel bad to ask any question! Just make sure to search the forum to see if a similar question is asked already (by someone else).

No problem at all! I am happy to help! I would appreciate if you get a bit strong in basics.

Cheers and Regards,
Girish

@girishkumar.kannan

Thank you!

I pretty much understood the code. Some points that still seem strange to me are:

  1. The need for a callback method.
  2. The pub.publish command.

Fyi I searched for another similar topic and, at least I, couldn’t find anything.

Thanks, again, for your response! I will take care of learning and developing as much as I can!

Hi @Eliasmys ,

These are the bare basics to get the subscriber and the publisher working using rospy.

If you do not know why subscribers need a callback function and why publishers use a publish function, then you must definitely take the course and learn ROS from beginning!

These are the concepts used in ROS and if you can’t understand these or don’t not know about these then that is bad.

Regards,
Girish

Ok, @girishkumar.kannan, I will take care.