Working with service and subscriber for ROS Basics (python) rosject

Hello, I am having trouble with getting the subscriber and the service to communicate as I am very new to python (really only experienced with MATLAB unfortunately). My main issue is finding a way to get the ‘msg’ from the subscriber and ‘request’ from the service in the same function, or find a way to get two separate functions to work together nicely. This is my attempt, but whenever I call the service the turtlebot spins into oblivion never to stop. Not sure what I am doing wrong here! Appreciate all the help.

Blockquote

#! /usr/bin/env python
import rospy
from rosject_1.srv import FindWall, FindWallResponse

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


minDist = 0
laser_ranges = []
found_wall = False


def findClosestWall(msg):

    minDist = msg.range_min
    laser_ranges = msg.ranges

    while laser_ranges[360] > minDist + 0.1:
        move.angular.z = 0.2
        pub.publish(move)
        rate.sleep()

        laser_ranges = msg.ranges
    move.angular.z = 0.0
    pub.publish(move)

    if msg.ranges[360] > 0.3:

        while msg.ranges[360] > 0.3:
            move.angular.z = 0.0
            move.linear.x = 0.1
            pub.publish(move)
            rate.sleep()

    move.linear.x = 0.0
    pub.publish(move)
    global found_wall
    found_wall = True
    return 0


def callback(request):

    rospy.loginfo("The Service /findWall has been called")
    global found_wall
    response = FindWallResponse()
    if found_wall:
        response.wallfound = True
        return response


rospy.init_node('findWall_server')
sub = rospy.Subscriber('/scan', LaserScan, findClosestWall)
pub = rospy.Publisher('/cmd_vel', Twist, queue_size=1)
move = Twist()
rate = rospy.Rate(5)
findwall_service = rospy.Service('/findWall', FindWall, callback)
rospy.spin()

You can take our free Python course to learn more (if you have not taken it already).

You are already doing well in this. You can make request available just as you made msg available using a global variable. You just need some little adjustments.

In your subscriber callback,

laser_ranges = msg.ranges

should be

global laser_ranges
laser_ranges = msg.ranges

since global_ranges is a global variable and you want to modify it (I think).

However, in your service callback,

global found_wall
response = FindWallResponse()
if found_wall:
    response.wallfound = True
    return response

should be

response = FindWallResponse()
if found_wall:
    response.wallfound = True
    return response

because you don’t need to use the global keyword to access a global variable within a function - you only need it when you want to modify it.

PS: even if you use global unnecessarily when you just need to access a global variable, it still works. But you must use it if you want to modify the variable (even if you are also accessing it within the same function).

As far as I can see here, if the robot is not stopping, it’s because your logic to stop it in the subscriber is not working. I recommend that you examine laser_ranges to ensure you are getting the expected values.

On the other hand, I suppose that when you call the service, it is not returning a result because:

  • found_wall will not be true until the robot is stopped towards the end of the subscriber logic.

Hi again! thanks for the tips, but it seems like I am having some of the same issues. When I run the server node the robot starts searching for the wall (rotating) before the service is called. I believe its because I have the subscriber call outside of the callback function, so when the node starts it starts as well. Is it possible to put the subscriber inside of the callback function?

I added the subscriber to the server callback function and it does appear to start searching when the service is called. However, it has been throwing the following error whenever I ‘rosservice call /findWall’:

ERROR: service [/findWall] responded with an error: b’service cannot process request: service handler returned None’

I believe this is because I need to import an Empty request type? I will try this and update this topic.

One last thing, it appears that the msg.ranges is not updating, i.e. staying a static value, making the code run in an infinite while loop. I am not sure how to fix this. Thank!

Blockquote

#! /usr/bin/env python
import rospy
from rosject_1.srv import FindWall, FindWallResponse

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


minDist = 0

found_wall = False


def findClosestWall(msg):

    global found_wall
    global minDist
    if minDist == 0:
        minDist = msg.range_min

    while msg.ranges[360] > minDist + 0.1:
        move.angular.z = 0.2
        pub.publish(move)
        print('minDist: ', minDist)
        print('laser_ranges: ', msg.ranges[360])
        # rate.sleep()

    move.angular.z = 0.0
    pub.publish(move)
    print('wall found!')

    if msg.ranges[360] > 0.3:

        while msg.ranges[360] > 0.3:
            move.angular.z = 0.0
            move.linear.x = 0.1
            pub.publish(move)
            # rate.sleep()

    move.linear.x = 0.0
    pub.publish(move)
    found_wall = True
    return 0


def callback(request):

    rospy.loginfo("The Service /findWall has been called")
    sub = rospy.Subscriber('/scan', LaserScan, findClosestWall)
    response = FindWallResponse()
    if found_wall:
        response.wallfound = True
        return str(response)


rospy.init_node('findWall_server')
rospy.loginfo("The Service is ready to be called")
pub = rospy.Publisher('/cmd_vel', Twist, queue_size=1)
move = Twist()
rate = rospy.Rate(5)
findwall_service = rospy.Service('/findWall', FindWall, callback)
rospy.spin()

Hello again, sorry about all these updates but I have figured out the msg.ranges updating issue! here is my new code:

Blockquote

#! /usr/bin/env python
import rospy
from rosject_1.srv import FindWall, FindWallResponse
from std_srvs.srv import Empty

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


minDist = 0

found_wall = False


def findClosestWall(msg):

    global found_wall
    global minDist
    if minDist == 0:
        minDist = min(msg.ranges)

    if msg.ranges[360] > minDist + 0.1:
        move.angular.z = 0.2
        pub.publish(move)
        print('minDist: ', minDist)
        print('laser_ranges: ', msg.ranges[360])
        # rate.sleep()

    if msg.ranges[360] < minDist + 0.1:
        move.angular.z = 0.0
        pub.publish(move)
        print('wall found!')

    if msg.ranges[360] > 0.3 and msg.ranges[360] < minDist + 0.1:
        print('Approaching wall')
        move.angular.z = 0.0
        move.linear.x = 0.1
        pub.publish(move)
        # rate.sleep()

    if msg.ranges[360] < 0.3 and msg.ranges[360] < minDist + 0.1:
        move.linear.x = 0.0
        pub.publish(move)
        found_wall = True
        print('At the wall')
        return 0


def callback(request):

    rospy.loginfo("The Service /findWall has been called")
    sub = rospy.Subscriber('/scan', LaserScan, findClosestWall)
    response = FindWallResponse()
    if found_wall:
        response.wallfound = True
        return str(response)


rospy.init_node('findWall_server')
rospy.loginfo("The Service is ready to be called")
pub = rospy.Publisher('/cmd_vel', Twist, queue_size=1)
move = Twist()
rate = rospy.Rate(5)
findwall_service = rospy.Service('/findWall', FindWall, callback)
rospy.spin()

I am still having that error from the previous reply though, but it seems like the service is still called and the robot finds the wall, moves toward it and stops at 0.3m from it which is great. Now my only problem is that the service doesn’t give a response and the service continues to print out ‘found wall’ and ‘At the wall’ repeatedly. I am not sure how to exit the subscriber callback and update the ‘wallfound’ object in the service callback because that seems to be the issue alongside the error from my earlier reply.

If someone could take a look at my previous reply I would greatly appreciate it. I am still having this issue. Thanks!

NVM! I have finally solved it! for all those who are having trouble and reading this thread:

  1. write laser data and min distance in the subscriber callback as global variables (no logic here)
  2. write all the logic in the server call back
  3. server and subscriber init need to be in global scope

I’d post my code but that’d ruin the fun of figuring it out
Thanks!

1 Like

This topic was automatically closed after 20 hours. New replies are no longer allowed.