Ros_posible_python_mistake

guys I am making basic run and stop program for ROS simulation and I found a problem. I don't know why, when the call back function runs the self.l_scan = scan_data -> updates like it should. But when the is_obstacle member function runs. The self.l_scan is empty. I have probably some spell mistake, but I tried to look for it like for hour and I can not find the mistake. The topic /scan is from Turtlebot and it runs like it should. I tried via echo.

Code

#!/usr/bin/env python
import rospy
from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import Twist
import math

class Robot(object):
    def __init__(self):
        # Params
        self.l_scan = LaserScan()
        self.loop_rate = rospy.Rate(10)
        self.velocity = Twist()

        self.pub = rospy.Publisher("/cmd_vel", Twist, queue_size=10)
        self.sub = rospy.Subscriber("/scan", LaserScan, self.callback)

    def callback(self,scan_data):
        self.l_scan = scan_data
        print("readed")

    def is_obstacle(self,distance):
        print(self.l_scan)
        slice_of_array = self.l_scan.ranges[174: 184+1]
        if min(slice_of_array) < distance:
            return True
        else:
            return False

    def stop(self):
        print("stop")    
        self.velocity = Twist()
        self.pub.publish(self.velocity)

    ## move until find obstacle closer than 0.6, laser [-5,+5] degrees
    def move(self):
        self.velocity.linear.x = 1

        while not self.is_obstacle(0.6):
            print("moving")
            self.pub.publish(velocity)
            self.loop_rate.sleep()
        self.stop()

## rotate until find obstacle greater then 3
##def rotate():
##    global l_scan
##    laser_sub = rospy.Subscriber("/scan", LaserScan, scan_callback)
##    vel_pub = rospy.Publisher("/cmd_vel", Twist)

if __name__ == '__main__':
    try:
        rospy.init_node('Turtle_robot_control', anonymous=True)
        my_robot = Robot()
        my_robot.move()

    except rospy.ROSInterruptException:
        rospy.loginfo("node terminated.")

Output

header: 
  seq: 0
  stamp: 
    secs: 0
    nsecs:         0
  frame_id: ''
angle_min: 0.0
angle_max: 0.0
angle_increment: 0.0
time_increment: 0.0
scan_time: 0.0
range_min: 0.0
range_max: 0.0
ranges: []
intensities: []
readed
Traceback (most recent call last):
  File "/home/trahery/catkin_ws/src/ros_essentials_cpp/src/topic04_perception02_laser/robot_control.py", line 54, in <module>
    print(my_robot.is_obstacle(0.2))
  File "/home/trahery/catkin_ws/src/ros_essentials_cpp/src/topic04_perception02_laser/robot_control.py", line 24, in is_obstacle
    if min(slice_of_array) < distance:
ValueError: min() arg is an empty sequence

Your issue is because you're immediately calling move after finishing the constructor; where you setup the subscriber. This means there is relatively 0 chance that any data has been received. Instead, you should be validating that you have enough data before trying to slice the list and other operations on the data; because these operations assume you do have data. Something like this:

def is_obstacle(self,distance):
    if len(self.l_scan.ranges) < 185:
        rospy.logwarn(f'Not enough data for calculation. {self.l_scan.ranges} points read')
        return False
    slice_of_array = self.l_scan.ranges[174: 184+1]
    if min(slice_of_array) < distance:
        return True
    else:
        return False

Note that this causes issues with other parts of your code. Now it will immediate drop out of the loop and stop, but also log an error message. To fix this, and given your current code setup you will need to verify there has been data received to enter the loop:

while len(self.l_scan.ranges) == 0:
    self.loop_rate.sleep()
while not self.is_obstacle(0.6):
    #...

One final note, in feature sparse environments your code may also break if it doesn't detect enough laserscan points.