I have been completing the ROS2 in 5 days (Python) course. I am working on Part II of the Rosject, which involves making a Service Server and Client for the Find Wall behaviour.
I have implemented client functionality into the node and it successfully calls the Find Wall Server at startup. However, the Server never ends. I’m not sure if client.future.done() is needed here, but after completing it’s behaviour, the program never goes back to the wall-following behaviour. If i remove the Service related code, then it executes the wall-following behaviour correctly. Here are the two scripts
wall_following.py:
import rclpy
# import the ROS2 python libraries
from rclpy.node import Node
# import the LaserScan module from sensor_msgs interface
from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import Twist
from std_srvs.srv import Empty
# import Quality of Service library, to set the correct profile and reliability to read sensor data.
from rclpy.qos import ReliabilityPolicy, QoSProfile
class WallFollower(Node):
def __init__(self):
super().__init__('wall_follower')
self.dist_right = 0.0
self.dist_fwd = 0.0
self.laser_subscriber = self.create_subscription(
LaserScan, '/scan',
self.wall_dist,
QoSProfile(depth=10,
reliability=ReliabilityPolicy.RELIABLE))
self.move_pub_ = self.create_publisher(
Twist, 'cmd_vel', 10)
# Find Wall Behaviour
self.client = self.create_client(Empty, 'find_wall')
while not self.client.wait_for_service(timeout_sec=1.0):
self.get_logger().info('service not available, waiting again...')
self.req = Empty.Request()
timer_period = 0.5
self.timer = self.create_timer(timer_period, self.move)
def send_request(self):
self.client.future = self.client.call(self.req)
def wall_dist(self, msg):
# print the log info in the terminal
self.dist_right = msg.ranges[520]
self.dist_left = msg.ranges[180]
self.dist_fwd = msg.ranges[0]
self.all_dist = msg
# self.get_logger().info('Dist fwd: "%s"' % str(self.dist_fwd))
# self.get_logger().info('Dist to wall: "%s"' % str(self.dist_right))
# self.get_logger().info('Dist to left: "%s"' % str(self.dist_left))
def move(self):
cmd = Twist()
self.get_logger().info('Dist fwd YA MADFE IT HERE: "%s"' % str(self.dist_fwd))
if self.dist_fwd < 0.5:
cmd.angular.z = 0.35
cmd.linear.x = 0.01
elif self.dist_right > 0.3:
cmd.angular.z = -0.2
cmd.linear.x = 0.03
elif self.dist_right < 0.3 and self.dist_right > 0.2:
cmd.angular.z = 0.0
cmd.linear.x = 0.05
elif self.dist_right < 0.2:
cmd.angular.z = 0.2
cmd.linear.x = 0.03
self.move_pub_.publish(cmd)
def main(args=None):
# initialize the ROS communication
rclpy.init(args=args)
# declare the node constructor
wall_follower = WallFollower()
# pause the program execution, waits for a request to kill the node (ctrl+c)
wall_follower.send_request()
rclpy.spin(wall_follower)
# Explicity destroy the node
wall_follower.destroy_node()
# shutdown the ROS communication
rclpy.shutdown()
if __name__ == '__main__':
main()
And wall_finder.py:
import rclpy
# import the ROS2 python libraries
from rclpy.node import Node
# import the LaserScan module from sensor_msgs interface
from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import Twist
from std_srvs.srv import Empty
# import Quality of Service library, to set the correct profile and reliability to read sensor data.
from rclpy.qos import ReliabilityPolicy, QoSProfile
from rclpy.callback_groups import ReentrantCallbackGroup
from rclpy.executors import MultiThreadedExecutor
RIGHT = 520
LEFT = 180
FWD = 0
class WallFinder(Node):
def __init__(self):
super().__init__('wall_finder')
self.reentrant_group_1 = ReentrantCallbackGroup()
self.srv = self.create_service(Empty, 'find_wall', self.find_wall_callback, callback_group=self.reentrant_group_1)
self.dist_right = 0.0
self.dist_fwd = 0.0
self.laser_subscriber = self.create_subscription(
LaserScan, '/scan',
self.wall_dist,
QoSProfile(depth=10,
reliability=ReliabilityPolicy.RELIABLE), callback_group=self.reentrant_group_1)
self.move_pub_ = self.create_publisher(
Twist, 'cmd_vel', 10)
self.move_order = "TURN 1"
def wall_dist(self, msg):
# print the log info in the terminal
self.dist_left = msg.ranges[LEFT]
self.dist_right = msg.ranges[RIGHT]
self.dist_fwd = msg.ranges[FWD]
self.all_dist = msg
self.get_logger().info('Dist fwd: "%s"' % str(self.dist_fwd))
self.get_logger().info('Dist to wall: "%s"' % str(self.dist_right))
# self.get_logger().info('Dist to left: "%s"' % str(self.dist_left))
def find_wall_callback(self, request, response):
cmd = Twist()
# response.success = True
while self.move_order != "FINISHED":
self.get_logger().info('MOVEMENT: "%s"' % str(self.move_order))
if self.move_order == "TURN 1":
index = self.all_dist.ranges.index(min(self.all_dist.ranges))
if index < 360:
cmd.angular.z = 0.2
elif index >= 360:
# turn right
cmd.angular.z = -0.2
if index == FWD:
cmd.angular.z = 0.0
self.move_order = "MOVE 1"
elif self.move_order == "MOVE 1":
if min(self.all_dist.ranges) > 0.305:
cmd.linear.x = 0.05
elif min(self.all_dist.ranges) <= 0.295:
cmd.linear.x = -0.05
else:
cmd.linear.x = 0.0
self.move_order = "TURN 2"
elif self.move_order == "TURN 2":
index = self.all_dist.ranges.index(min(self.all_dist.ranges))
if index >= LEFT and index <= (LEFT+360):
cmd.angular.z = -0.2
else:
cmd.angular.z = 0.2
if index == RIGHT:
cmd.angular.z = 0.0
self.move_order = "FINISHED"
# response.success = False
# response.message = "Finished"
self.move_pub_.publish(cmd)
return response
def main(args=None):
# initialize the ROS communication
rclpy.init(args=args)
# declare the node constructor
wall_finder = WallFinder()
# pause the program execution, waits for a request to kill the node (ctrl+c)
executor = MultiThreadedExecutor(num_threads=2)
executor.add_node(wall_finder)
try:
executor.spin()
finally:
# shutdown the ROS communication
wall_finder.destroy_node()
executor.shutdown()
if __name__ == '__main__':
main()
Thank you for your time, let me know if you have any questions or need any more information!
Cheers