ROS basics in 5 days - ROSject, 'name not defined' error

Hello, I’ve written the service server and client to find and follow wall, and it functions correctly most of the times, although, only sometimes, when I try launch the launch file that launches both the nodes for server and client, I get this error: -
(ray is assigned to the laser ranges, as in the server code)

Following are the programs for the server, client and the launch file: -
1. Server

#! /usr/bin/env python

import rospy
from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import Twist
from wall_follower.srv import FindWall, FindWallResponse


# Defining variables

max_range = 3.5  # Max range of the laser.
min_range = 0.12  # Min range of the laser.

max_angle = 3.142  # Max angle of the laser scanner
min_angle = -3.142  # Min angle of the laser scanner

angle_increment = 0.008  # Angle increment

speed = [0.08, 0.12]  # Speed options: slow and fast.


def servCallback(req):

    fw_service_response = FindWallResponse()  # Defining response object.
    rospy.loginfo("/find_wall service called")
    min1 = ray[0]
    val = findWall(min1)
    result = mainBehaviour(val)
    fw_service_response.wallfound = result
    
    if fw_service_response.wallfound:
        rospy.loginfo("Behaviour achieved")
    else:
        rospy.loginfo("Behaviour failed!")

    return fw_service_response


def posCallback(msg):

    global f, b, l, r, ray, f_r, min1

    ray = msg.ranges
    
    f = msg.ranges[len(msg.ranges)//2]  # Front readings
    r = msg.ranges[len(msg.ranges)//4]  # Right readings
    l = msg.ranges[len(msg.ranges)*3//4]  # Left readings
    b = msg.ranges[len(msg.ranges) - 1]  # Back readings

    f_r = ray[len(ray)*3//8]  #Front-Right readings (ray number 270)

def moveForward(spd):
    vel.linear.x = spd
    vel.angular.z = 0


def moveBackwards(spd):
    vel.linear.x = -spd
    vel.angular.z = 0


def turnCW():
    vel.linear.x = 0.1
    vel.angular.z = -0.4


def turnCCW():
    vel.linear.x = 0.1
    vel.angular.z = 0.4


def rotateLeft():
    vel.linear.x = 0
    vel.angular.z = 0.4


def rotateRight():
    vel.linear.x = 0
    vel.angular.z = -0.4


def stopRobot():
    vel.linear.x = 0
    vel.angular.z = 0


# This function will find the wall, by finding the lowest ray value.
def findWall(min1):
    # find the minimum ray dist index
    for i in range(len(ray)-1):
        if ray[i] < min1:
            min1 = ray[i]
            # time.sleep(0.5)

    return min1

# This function will move the robot's front closer to the wall.
def alignFront(val):
    aligned = False  # Is the robot aligned? -> NO
    while not (val+0.05) >= f >= (val-0.05):  # while the front is not almost equal to the
                                              # minimum ray distance, rotate the robot to right.
        rotateRight()
        pub.publish(vel)
        rate.sleep()

        if (val+0.05) >= f >= (val-0.05):
            aligned = True  # Is the robot aligned? -> YES
            break

    stopRobot()
    pub.publish(vel)
    rate.sleep()
    
    return aligned

# This function willl align the robot's front right to face the wall.   
def alignFrontRight(val):
    aligned = False
    while not (val+0.05) >= f_r >= (val-0.05):
        print("f_r: ", f_r)
        rotateLeft()
        pub.publish(vel)
        rate.sleep()
        if (val+0.05) >= f_r >= (val-0.05):
            aligned = True
            break
    if (val+0.05) >= f_r >= (val-0.05):
        print("f_r in range")
    else:
        print("NOT in range")
    print("Out of loop", f_r)
    stopRobot() 
    pub.publish(vel)
    rate.sleep()

    return aligned


# This function executes the main expected behavior for the robot
# ie, once the wall is found, it will align its front move closer,
# then align its front-right(ray 270) to the wall, and return True if aligned correctly.
def mainBehaviour(val):

    align_obj = alignFront(val)
    while align_obj:
        if f > 0.3:
            moveForward(speed[0])
        elif f <= 0.3:
            stopRobot()
            break
        pub.publish(vel)
        rate.sleep()
    align_f_r_obj = alignFrontRight(val)

    return align_f_r_obj


if __name__ == '__main__':
    try:
        vel = Twist()
        rospy.init_node('wall_follow')
        pub = rospy.Publisher('/cmd_vel', Twist, queue_size=1)
        sub = rospy.Subscriber('/scan', LaserScan, posCallback)
        fin_wall_service = rospy.Service('/find_wall', FindWall, servCallback)
        rospy.loginfo("Service online.")
        rate = rospy.Rate(20)
        rospy.spin()

    except rospy.ROSInterruptException():
        rospy.loginfo("Node Terminated")

2. Client

#!/usr/bin/env python

import rospy
from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import Twist
from wall_follower.srv import FindWall, FindWallRequest

# Defining variables 

max_range = 3.5  # Max range of the laser.
min_range = 0.12  # Min range of the laser.

max_angle = 3.142  # Max angle of the laser scanner
min_angle = -3.142  # Min angle of the laser scanner

angle_increment = 0.008  # Angle increment

speed = [0.08, 0.12]  # Speeds


def servClient():
    rospy.wait_for_service('/find_wall')
    find_wall_client = rospy.ServiceProxy('/find_wall', FindWall)
    find_wall_request_obj = FindWallRequest()
    result = find_wall_client(find_wall_request_obj)

    print(result)

def posCallback(msg):
    global f, b, l, r, ray # , f_r, b_r, f_l, b_l

    ray = msg.ranges

    f = msg.ranges[len(msg.ranges)//2]  # Front readings
    r = msg.ranges[len(msg.ranges)//4]  # Right readings
    l = msg.ranges[len(msg.ranges)*3//4]  # Left readings
    b = msg.ranges[len(msg.ranges) - 1]  # Back readings

    
    wallFollow()


def moveForward(spd):
    vel.linear.x = spd
    vel.angular.z = 0

def moveBackwards(spd):
    vel.linear.x = -spd
    vel.angular.z = 0

def turnCW():
    vel.linear.x = 0.1
    vel.angular.z = -0.4

def turnCCW():
    vel.linear.x = 0.1
    vel.angular.z = 0.4

def rotateLeft():
    vel.linear.x = 0
    vel.angular.z = 0.4

def rotateRight():
    vel.linear.x = 0
    vel.angular.z = -0.4

def stopRobot():
    vel.linear.x = 0
    vel.angular.z = 0


# This function makes the robot follow the wall, keeping it to the right side.
def wallFollow():
    if max_range > f > 0.8:  # Follow the wall fast, if it's farther than 0.8m
        moveForward(speed[1])
        if 0.3 > r > 0.2:  # Keep following fast if the right is correctly aligned.
            moveForward(speed[1])
        elif max_range > r > 0.3:  # If wall at right is farther than 0.3,
            turnCW()               # the robot must move closer towards it.            
        elif 0.2 < r < max_range: # If wall at right is closer than 0.2,
            turnCCW()             # the robot must move away.
     
    elif f < 0.8:  # If the wall is closer than 0.8 meters, move slow!
        moveForward(speed[0])  
        turnCCW()  #  Turn CCW to avoid collision with front wall

        if f <= 0.3:  # If wall at front is less than 0.3,
                      # the robot must back up!
            moveBackwards(speed[0])  # Go reverse
    pub.publish(vel)
    rate.sleep()


if __name__ == '__main__':
    try:
        vel = Twist()
        rospy.init_node('wall_follow_client')
        servClient()
        pub = rospy.Publisher('/cmd_vel', Twist, queue_size= 1)
        sub = rospy.Subscriber('/scan', LaserScan, posCallback)
        rate = rospy.Rate(20)
        rospy.spin()

    except rospy.ROSInterruptException:
        rospy.loginfo("Node Terminated")

3. Launch file

<launch>

<!-- this file launches two node, one for the server and the second for the client -->

<node pkg = 'wall_follower'
      type = 'find_wall_service_server.py'
      name = 'find_wall_server_node'
      output = 'screen'>

</node>

<node pkg = 'wall_follower'
      type = 'wall_follow_client.py'
      name = 'wall_follower_client_node'
      output = 'screen'>

</node>

</launch>

Even just trying and launching the launch file again solves the problem too! Sometimes changing the orientation a little, using teleop_keyboard and then trying to launch the launch file also solves the problem.
Can anyone please give me an insight as to why the code runs most times and sometimes it doesn’t? Am I missing something?

Thanks for the time!

Hi @Yogesh_G ,

Your most simplest (definitely not the best) solution is to add the line global ray in your servCallback(req) method in your server code.
This should fix your ray is not defined error.

The above should eventually fix the client result also. Let me know if it does not.

Regards,
Girish

Hey @girishkumar.kannan , I just tried your suggestion, and it did not work. I got the same error. In the code,
PS: I’ve declared it global in posCallback(msg) originally.
And now declared it global in servCallback(req)

Hi @Yogesh_G ,

Let me guess, this time you are getting the same error but the error is in some line within findWall() function right?

I guess you need to implement classing in your code. If you want to retain this code, you might have to place ray = [] under # Defining variables

Try adding the same global ray line within findWall(min1) function. Let me know if it works now.

Regards,
Girish

@Yogesh_G ,

Why do you have robot control code lines in your client program?
Your client just calls the server. Your client does not do any robot operations other than calling the service.

Regards,
Girish

It is for the wall following behaviour that is defined in the client program. The server is just to find the wall and the client first calls the sever, then once it has found the wall it follows the wall. This is in accordance with the rosject notebook Section 2, 2.2. Please let me know if this looks like a misinterpretation on my side, however I believe this was the supposed instruction.

Thanks, regards,
Yogesh.

Hey @navjotsinghsodhi52 , that is a very neat code! I eventually understood that using OOP should be the preferred way, and I thank you for the great suggestion; Although I’m a beginner and havent yet learn how to use classes. I’m on my way to learn OOP using python. Thanks for sharing the code!

Regards,
Yogesh.

1 Like

@Yogesh_G ,

Glad you understood that OOP is required for better coding, however, I would recommend you to write your own OOP based code and not take any hints from @navjotsinghsodhi52 code. One person’s code is their skill and should not be copied unless it is a shared code.

So regarding this comment, let me give you an outline of program files that you will require for your course project.

  1. wall follower code - just has wall follower logic and nothing else
  2. service server code - just has the code to perform the service
  3. service client code - just has the code to make a call to the service server and nothing else
  4. action server code - just has the code to perform the action
  5. action client code - just has the code the make a call to the action server and nothing else
  6. main program code - integrates wall follower logic along with service client and action client

I hope this helps you understand how the project is decomposed into sub-programs.

Reagrds,
Girish

Hey @girishkumar.kannan , I tried this. It gave me index out of range error this time.

@Yogesh_G ,

You seem to be having very basic issues. You need to fix them yourself.
Please take the Python 3 for Robotics course (FREE) to get to know Python programming better.

Regards,
Girish

1 Like

@girishkumar.kannan I have finished that course(Python3 for ROS), although I understand what you mean.
@girishkumar.kannan and @navjotsinghsodhi52 I’ll try both your advice and come back to this with OOP.

Thanks for your time! Regards,
Yogesh.

2 Likes