0

I'm using the code below in an attempt to read laser data and determine which side of my robot is closest to a wall. The laser data is successfully being printed in the left and right callbacks but I try to assign both values to global variables and use those varibles in a third function. However, the varibales DO NOT print in my run() function:

#!/usr/bin/env python

import rospy
import math
from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import Twist
from nav_msgs.msg import Odometry
from tf import transformations
from sensor_msgs.msg import LaserScan

subL_value = ''
subR_value = 'test'

def callbackLeft(msg) :
    subL_value = min(msg.ranges[0:49])

def callbackRight(msg) :
    global subR_value
    subR_value = min(msg.ranges[0:49])

def run() :
    # rospy.Subscriber('/scan_left', LaserScan, callbackLeft)
    rospy.Subscriber('/scan_right', LaserScan, callbackRight)

    # print('\tLeft: '), 
    # print(subL_value)
    print('\tRight: '), 
    global subR_value 
    print(subR_value)

if __name__ == "__main__":
    rospy.init_node('wall_detector')
    run()
    rospy.spin()

In the code above, I am only using the subR_value, Initiating it to a value of test, resetting it in the callbackRight subscriber callback function and attemoting to read the new value in the run() function.

However, running the script, I have 2 problems:

  1. The printed value is test and hence is no overwritten as expected
  2. The script does not loop only outputting a single Right: test output

I found this post which is a similar problem and also this one but I seem to be satisfying the necessary global variable labelling.

Am I missing something ?

sisko
  • 9,604
  • 20
  • 67
  • 139
  • In `run` you are only registering a callback, theprint function is only called once there. Only the callback is executed periodically. You would have to add the print inside the callback. – 2b-t Jul 20 '21 at 19:46

1 Answers1

1

In your code you are only calling run once, it is not called periodically and therefore also does not print periodically.

The node is initialised inside your __main__, enters once into the run() routine, registers the callback for /scan_right, prints the print(...) statements and exits the routine. Returned to the main the program will be stopped from exiting due to rospy.spin(). As long as ROS is alive every single time a new message on the /scan_right topic is received the callbackRight will be called which updates the global variable (but does not print it).

If you wanted to print the variable every single time an update occurs you would have to call the print(...) inside the callback. If you wanted to print it periodically (with a fixed rate but not on every update) you would have to modify ros.spin() to something like

rate = rospy.Rate(10) # Fixed update frequency of 10hz
while not rospy.is_shutdown():
    # Call your print function here
    rate.sleep() 

Instead of using global variables I would move the code inside a class. Furthermore do not use print with ROS but instead use rospy.loginfo(), rospy.logwarn() and rospy.logerr(). They have several advantages as already discussed at the bottom of this post.

2b-t
  • 2,414
  • 1
  • 10
  • 18
  • Thanks for your input. I have refactored my code so it's contained in a class and now using rospy.is_shutdown(). However, I still need to pass data from the callbacks to my run function – sisko Jul 20 '21 at 22:36
  • @sisko Is the given code your final application or a simplified example? As said, either you can save the data to be transferred as a class member and **call the `run` function periodically at a fixed rate** or you can **call `run` from inside the callback** and directly pass the data as a parameter to it. Are you sure you do not want to refactor `run` as well if you want to call it periodically? The subscriber callback should only be registered once. Usually you subscribe in the constructor and unsubscribe in the destructor. – 2b-t Jul 20 '21 at 23:11
  • I Appreciate the very helpful and detailed explanation. – sisko Jul 21 '21 at 10:46