follower.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #!/usr/bin/env python
  2. """
  3. follower.py - Version 1.1 2013-12-20
  4. Follow a "person" by tracking the nearest object in x-y-z space.
  5. Based on the follower application by Tony Pratkanis at:
  6. http://ros.org/wiki/turtlebot_follower
  7. Created for the Pi Robot Project: http://www.pirobot.org
  8. Copyright (c) 2012 Patrick Goebel. All rights reserved.
  9. This program is free software; you can redistribute it and/or modify
  10. it under the terms of the GNU General Public License as published by
  11. the Free Software Foundation; either version 2 of the License, or
  12. (at your option) any later version.
  13. This program is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. GNU General Public License for more details at:
  17. http://www.gnu.org/licenses/gpl.html
  18. """
  19. import rospy
  20. from roslib import message
  21. from sensor_msgs import point_cloud2
  22. from sensor_msgs.msg import PointCloud2
  23. from geometry_msgs.msg import Twist
  24. from math import copysign
  25. class Follower():
  26. def __init__(self):
  27. rospy.init_node("follower")
  28. # Set the shutdown function (stop the robot)
  29. rospy.on_shutdown(self.shutdown)
  30. # The dimensions (in meters) of the box in which we will search
  31. # for the person (blob). These are given in camera coordinates
  32. # where x is left/right,y is up/down and z is depth (forward/backward)
  33. self.min_x = rospy.get_param("~min_x", -0.2)
  34. self.max_x = rospy.get_param("~max_x", 0.2)
  35. self.min_y = rospy.get_param("~min_y", -0.3)
  36. self.max_y = rospy.get_param("~max_y", 0.5)
  37. self.max_z = rospy.get_param("~max_z", 1.2)
  38. # The goal distance (in meters) to keep between the robot and the person
  39. self.goal_z = rospy.get_param("~goal_z", 0.6)
  40. # How far away from the goal distance (in meters) before the robot reacts
  41. self.z_threshold = rospy.get_param("~z_threshold", 0.05)
  42. # How far away from being centered (x displacement) on the person
  43. # before the robot reacts
  44. self.x_threshold = rospy.get_param("~x_threshold", 0.05)
  45. # How much do we weight the goal distance (z) when making a movement
  46. self.z_scale = rospy.get_param("~z_scale", 1.0)
  47. # How much do we weight left/right displacement of the person when making a movement
  48. self.x_scale = rospy.get_param("~x_scale", 2.5)
  49. # The maximum rotation speed in radians per second
  50. self.max_angular_speed = rospy.get_param("~max_angular_speed", 2.0)
  51. # The minimum rotation speed in radians per second
  52. self.min_angular_speed = rospy.get_param("~min_angular_speed", 0.0)
  53. # The max linear speed in meters per second
  54. self.max_linear_speed = rospy.get_param("~max_linear_speed", 0.3)
  55. # The minimum linear speed in meters per second
  56. self.min_linear_speed = rospy.get_param("~min_linear_speed", 0.1)
  57. # Slow down factor when stopping
  58. self.slow_down_factor = rospy.get_param("~slow_down_factor", 0.8)
  59. # Initialize the movement command
  60. self.move_cmd = Twist()
  61. # Publisher to control the robot's movement
  62. self.cmd_vel_pub = rospy.Publisher('cmd_vel', Twist, queue_size=5)
  63. # Subscribe to the point cloud
  64. self.depth_subscriber = rospy.Subscriber('point_cloud', PointCloud2, self.set_cmd_vel, queue_size=1)
  65. rospy.loginfo("Subscribing to point cloud...")
  66. # Wait for the pointcloud topic to become available
  67. rospy.wait_for_message('point_cloud', PointCloud2)
  68. rospy.loginfo("Ready to follow!")
  69. def set_cmd_vel(self, msg):
  70. # Initialize the centroid coordinates point count
  71. x = y = z = n = 0
  72. # Read in the x, y, z coordinates of all points in the cloud
  73. for point in point_cloud2.read_points(msg, skip_nans=True):
  74. pt_x = point[0]
  75. pt_y = point[1]
  76. pt_z = point[2]
  77. # Keep only those points within our designated boundaries and sum them up
  78. if -pt_y > self.min_y and -pt_y < self.max_y and pt_x < self.max_x and pt_x > self.min_x and pt_z < self.max_z:
  79. x += pt_x
  80. y += pt_y
  81. z += pt_z
  82. n += 1
  83. # If we have points, compute the centroid coordinates
  84. if n:
  85. x /= n
  86. y /= n
  87. z /= n
  88. # Check our movement thresholds
  89. if (abs(z - self.goal_z) > self.z_threshold):
  90. # Compute the angular component of the movement
  91. linear_speed = (z - self.goal_z) * self.z_scale
  92. # Make sure we meet our min/max specifications
  93. self.move_cmd.linear.x = copysign(max(self.min_linear_speed,
  94. min(self.max_linear_speed, abs(linear_speed))), linear_speed)
  95. else:
  96. self.move_cmd.linear.x *= self.slow_down_factor
  97. if (abs(x) > self.x_threshold):
  98. # Compute the linear component of the movement
  99. angular_speed = -x * self.x_scale
  100. # Make sure we meet our min/max specifications
  101. self.move_cmd.angular.z = copysign(max(self.min_angular_speed,
  102. min(self.max_angular_speed, abs(angular_speed))), angular_speed)
  103. else:
  104. # Stop the rotation smoothly
  105. self.move_cmd.angular.z *= self.slow_down_factor
  106. else:
  107. # Stop the robot smoothly
  108. self.move_cmd.linear.x *= self.slow_down_factor
  109. self.move_cmd.angular.z *= self.slow_down_factor
  110. # Publish the movement command
  111. self.cmd_vel_pub.publish(self.move_cmd)
  112. def shutdown(self):
  113. rospy.loginfo("Stopping the robot...")
  114. # Unregister the subscriber to stop cmd_vel publishing
  115. self.depth_subscriber.unregister()
  116. rospy.sleep(1)
  117. # Send an emtpy Twist message to stop the robot
  118. self.cmd_vel_pub.publish(Twist())
  119. rospy.sleep(1)
  120. if __name__ == '__main__':
  121. try:
  122. Follower()
  123. rospy.spin()
  124. except rospy.ROSInterruptException:
  125. rospy.loginfo("Follower node terminated.")