follower2.py 5.9 KB

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