123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- #!/usr/bin/env python
- """
- object_tracker.py - Version 1.1 2013-12-20
-
- Rotate the robot left or right to follow a target published on the /roi topic.
-
- Created for the Pi Robot Project: http://www.pirobot.org
- Copyright (c) 2012 Patrick Goebel. All rights reserved.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details at:
-
- http://www.gnu.org/licenses/gpl.html
- """
- import rospy
- from sensor_msgs.msg import RegionOfInterest, CameraInfo
- from geometry_msgs.msg import Twist
- import thread
- class ObjectTracker():
- def __init__(self):
- rospy.init_node("object_tracker")
-
- # Set the shutdown function (stop the robot)
- rospy.on_shutdown(self.shutdown)
-
- # How often should we update the robot's motion?
- self.rate = rospy.get_param("~rate", 10)
- r = rospy.Rate(self.rate)
-
- # The maximum rotation speed in radians per second
- self.max_rotation_speed = rospy.get_param("~max_rotation_speed", 2.0)
-
- # The minimum rotation speed in radians per second
- self.min_rotation_speed = rospy.get_param("~min_rotation_speed", 0.5)
-
- # Sensitivity to target displacements. Setting this too high
- # can lead to oscillations of the robot.
- self.gain = rospy.get_param("~gain", 2.0)
-
- # The x threshold (% of image width) indicates how far off-center
- # the ROI needs to be in the x-direction before we react
- self.x_threshold = rospy.get_param("~x_threshold", 0.1)
- # Publisher to control the robot's movement
- self.cmd_vel_pub = rospy.Publisher('cmd_vel', Twist, queue_size=5)
-
- # Intialize the movement command
- self.move_cmd = Twist()
-
- # Get a lock for updating the self.move_cmd values
- self.lock = thread.allocate_lock()
-
- # We will get the image width and height from the camera_info topic
- self.image_width = 0
- self.image_height = 0
-
- # Set flag to indicate when the ROI stops updating
- self.target_visible = False
-
- # Wait for the camera_info topic to become available
- rospy.loginfo("Waiting for camera_info topic...")
- rospy.wait_for_message('camera_info', CameraInfo)
-
- # Subscribe the camera_info topic to get the image width and height
- rospy.Subscriber('camera_info', CameraInfo, self.get_camera_info, queue_size=1)
- # Wait until we actually have the camera data
- while self.image_width == 0 or self.image_height == 0:
- rospy.sleep(1)
-
- # Subscribe to the ROI topic and set the callback to update the robot's motion
- rospy.Subscriber('roi', RegionOfInterest, self.set_cmd_vel, queue_size=1)
-
- # Wait until we have an ROI to follow
- rospy.loginfo("Waiting for messages on /roi...")
- rospy.wait_for_message('roi', RegionOfInterest)
-
- rospy.loginfo("ROI messages detected. Starting tracker...")
-
- # Begin the tracking loop
- while not rospy.is_shutdown():
- # Acquire a lock while we're setting the robot speeds
- self.lock.acquire()
-
- try:
- # If the target is not visible, stop the robot
- if not self.target_visible:
- self.move_cmd = Twist()
- else:
- # Reset the flag to False by default
- self.target_visible = False
-
- # Send the Twist command to the robot
- self.cmd_vel_pub.publish(self.move_cmd)
-
- finally:
- # Release the lock
- self.lock.release()
-
- # Sleep for 1/self.rate seconds
- r.sleep()
- def set_cmd_vel(self, msg):
- # Acquire a lock while we're setting the robot speeds
- self.lock.acquire()
-
- try:
- # If the ROI has a width or height of 0, we have lost the target
- if msg.width == 0 or msg.height == 0:
- self.target_visible = False
- return
-
- # If the ROI stops updating this next statement will not happen
- self.target_visible = True
-
- # Compute the displacement of the ROI from the center of the image
- target_offset_x = msg.x_offset + msg.width / 2 - self.image_width / 2
-
- try:
- percent_offset_x = float(target_offset_x) / (float(self.image_width) / 2.0)
- except:
- percent_offset_x = 0
-
- # Rotate the robot only if the displacement of the target exceeds the threshold
- if abs(percent_offset_x) > self.x_threshold:
- # Set the rotation speed proportional to the displacement of the target
- try:
- speed = self.gain * percent_offset_x
- if speed < 0:
- direction = -1
- else:
- direction = 1
- self.move_cmd.angular.z = -direction * max(self.min_rotation_speed,
- min(self.max_rotation_speed, abs(speed)))
- except:
- self.move_cmd = Twist()
- else:
- # Otherwise stop the robot
- self.move_cmd = Twist()
- finally:
- # Release the lock
- self.lock.release()
- def get_camera_info(self, msg):
- self.image_width = msg.width
- self.image_height = msg.height
- def shutdown(self):
- rospy.loginfo("Stopping the robot...")
- self.cmd_vel_pub.publish(Twist())
- rospy.sleep(1)
- if __name__ == '__main__':
- try:
- ObjectTracker()
- rospy.spin()
- except rospy.ROSInterruptException:
- rospy.loginfo("Object tracking node terminated.")
|