ardrone_follow.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # Copyright (c) 2012, Falkor Systems, Inc.  All rights reserved.
00003 
00004 # Redistribution and use in source and binary forms, with or without
00005 # modification, are permitted provided that the following conditions are
00006 # met:
00007 
00008 # Redistributions of source code must retain the above copyright notice,
00009 # this list of conditions and the following disclaimer.  Redistributions
00010 # in binary form must reproduce the above copyright notice, this list of
00011 # conditions and the following disclaimer in the documentation and/or
00012 # other materials provided with the distribution.  THIS SOFTWARE IS
00013 # PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
00014 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00015 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00016 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
00017 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00018 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00019 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00020 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00021 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00022 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00023 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00024 
00025 import roslib
00026 roslib.load_manifest('falkor_ardrone')
00027 
00028 import sys
00029 import rospy
00030 import math
00031 import pid
00032 import cv2
00033 import numpy as np
00034 from cv_bridge import CvBridge, CvBridgeError
00035 
00036 from geometry_msgs.msg import Point
00037 from geometry_msgs.msg import Twist
00038 from std_msgs.msg import Empty
00039 from sensor_msgs.msg import Joy, Image
00040 from ardrone_autonomy.msg import Navdata
00041 from ardrone_autonomy.srv import LedAnim
00042 from ardrone_autonomy.srv import RecordEnable
00043 import std_srvs.srv
00044 
00045 class ArdroneFollow:
00046     def __init__( self ):
00047         print "waiting for driver to startup"
00048         rospy.wait_for_service( "ardrone/setledanimation" )
00049         rospy.wait_for_service( "ardrone/setrecord" )
00050         print "driver started"
00051         self.led_service = rospy.ServiceProxy( "ardrone/setledanimation", LedAnim )
00052         self.record_service = rospy.ServiceProxy( "ardrone/setrecord", RecordEnable )
00053         self.recording = False
00054 
00055         self.tracker_sub = rospy.Subscriber( "ardrone_tracker/found_point",
00056                                              Point, self.found_point_cb )
00057         self.goal_vel_pub = rospy.Publisher( "goal_vel", Twist )
00058         self.found_time = None
00059 
00060         self.tracker_img_sub = rospy.Subscriber( "ardrone_tracker/image",
00061                                                  Image, self.image_cb )
00062         self.tracker_image = None
00063 
00064         self.timer = rospy.Timer( rospy.Duration( 0.10 ), self.timer_cb, False )
00065 
00066         self.land_pub = rospy.Publisher( "ardrone/land", Empty )
00067         self.takeoff_pub = rospy.Publisher( "ardrone/takeoff", Empty )
00068         self.reset_pub = rospy.Publisher( "ardrone/reset", Empty )
00069 
00070         self.angularZlimit = 3.141592 / 2
00071         self.linearXlimit = 1.0
00072         self.linearZlimit = 2.0
00073 
00074         # Increasing the P term for yaw
00075         self.xPid = pid.Pid( 0.080, 0.0, 0.0, self.angularZlimit )
00076         self.yPid = pid.Pid( 0.050, 0.0, 0.0, self.linearZlimit )
00077         self.zPid = pid.Pid( 0.050, 0.0, 0.0, self.linearXlimit )
00078 
00079         # alpha for the ema filter on the found point
00080         self.alpha = 0.5
00081 
00082         self.xPid.setPointMin = 45
00083         self.xPid.setPointMax = 55
00084 
00085         self.yPid.setPointMin = 40
00086         self.yPid.setPointMax = 60
00087 
00088         self.zPid.setPointMin = 32
00089         self.zPid.setPointMax = 40
00090 
00091         self.lastAnim = -1
00092 
00093         self.found_point = Point( 0, 0, -1 )
00094         self.old_cmd = self.current_cmd = Twist()
00095 
00096         self.joy_sub = rospy.Subscriber( "joy", Joy, self.callback_joy )
00097         self.manual_cmd = False
00098         self.auto_cmd = False
00099 
00100         self.bridge = CvBridge()
00101 
00102         self.navdata_sub = rospy.Subscriber( "/ardrone/navdata", Navdata, self.navdata_cb )
00103         self.navdata = None
00104         self.states = { 0: 'Unknown',
00105                         1: 'Init',
00106                         2: 'Landed',
00107                         3: 'Flying',
00108                         4: 'Hovering',
00109                         5: 'Test',
00110                         6: 'Taking Off',
00111                         7: 'Goto Fix Point',
00112                         8: 'Landing',
00113                         9: 'Looping' }
00114 
00115 
00116 
00117         cv2.namedWindow( 'AR.Drone Follow', cv2.cv.CV_WINDOW_NORMAL )
00118 
00119     def toggle_record( self ):
00120         self.recording = not self.recording
00121         
00122         self.record_service( enable = self.recording )
00123 
00124     def navdata_cb( self, data ):
00125         self.navdata = data
00126 
00127     def image_cb( self, data ):
00128         try:
00129             cv_image = self.bridge.imgmsg_to_cv( data, "passthrough" )
00130         except CvBridgeError, e:
00131             print e
00132         
00133         self.tracker_image = np.asarray( cv_image )
00134 
00135     def increase_z_setpt( self ):
00136         self.zPid.setPointMin *= 1.01
00137         self.zPid.setPointMax *= 1.01
00138 
00139     def decrease_z_setpt( self ):
00140         self.zPid.setPointMin /= 1.01
00141         self.zPid.setPointMax /= 1.01
00142 
00143     def callback_joy( self, data ):
00144         empty_msg = Empty()
00145 
00146         if data.buttons[12] == 1 and self.last_buttons[12] == 0:
00147             self.takeoff()
00148 
00149         if data.buttons[14] == 1 and self.last_buttons[14] == 0:
00150             self.land()
00151 
00152         if data.buttons[16] == 1 and self.last_buttons[16] == 0:
00153             self.toggle_record()
00154 
00155         if data.buttons[13] == 1 and self.last_buttons[13] == 0:
00156             self.reset()
00157 
00158         if data.buttons[15] == 1 and self.last_buttons[15] == 0:
00159             self.auto_cmd = not self.auto_cmd
00160             self.hover()
00161 
00162         if data.buttons[4] == 1:
00163             self.increase_z_setpt()
00164 
00165         if data.buttons[6] == 1:
00166             self.decrease_z_setpt()
00167 
00168         self.last_buttons = data.buttons
00169 
00170         # Do cmd_vel
00171         self.current_cmd = Twist()
00172 
00173         self.current_cmd.angular.x = self.current_cmd.angular.y = 0
00174         self.current_cmd.angular.z = data.axes[2] * self.angularZlimit
00175 
00176         self.current_cmd.linear.z = data.axes[3] * self.linearZlimit
00177         self.current_cmd.linear.x = data.axes[1] * self.linearXlimit
00178         self.current_cmd.linear.y = data.axes[0] * self.linearXlimit
00179 
00180         if ( self.current_cmd.linear.x == 0 and
00181              self.current_cmd.linear.y == 0 and
00182              self.current_cmd.linear.z == 0 and
00183              self.current_cmd.angular.z == 0 ):
00184             self.manual_cmd = False
00185         else:
00186             self.setLedAnim( 9 )
00187             self.manual_cmd = True
00188 
00189         self.goal_vel_pub.publish( self.current_cmd )
00190 
00191     def setLedAnim( self, animType, freq = 10 ):
00192         if self.lastAnim == type:
00193             return
00194 
00195         msg = LedAnim();
00196         msg.type = animType;
00197         msg.freq = freq;
00198         msg.duration = 3600;
00199         
00200         self.led_service( type = animType, freq = freq, duration = 255 )
00201         self.lastAnim = type
00202 
00203     def takeoff( self ):
00204         self.takeoff_pub.publish( Empty() )
00205         self.setLedAnim( 9 )
00206 
00207     def land( self ):
00208         self.land_pub.publish( Empty() )
00209 
00210     def reset( self ):
00211         self.reset_pub.publish( Empty() )
00212 
00213     def found_point_cb( self, data ):
00214         if data.z != -1.0:
00215             if self.found_point.z == -1.0:
00216                 self.found_point = data
00217             else:
00218                 # update the ema found_point, if we didn't just re-acquire
00219                 self.found_point.x = self.found_point.x * self.alpha + data.x * ( 1.0 - self.alpha )
00220                 self.found_point.y = self.found_point.y * self.alpha + data.y * ( 1.0 - self.alpha )
00221                 self.found_point.z = self.found_point.z * self.alpha + data.z * ( 1.0 - self.alpha )
00222         else:
00223             self.found_point = data
00224 
00225         self.found_time = rospy.Time.now()
00226 
00227     def hover( self ):
00228         hoverCmd = Twist()
00229         self.goal_vel_pub.publish( hoverCmd )
00230 
00231     def hover_cmd_cb( self, data ):
00232         self.hover()
00233 
00234     def put_text( self, vis, text, pos ):
00235         cv2.putText( vis, text,
00236                      pos, cv2.FONT_HERSHEY_PLAIN, 1.0,
00237                      (255, 255, 255) )
00238         cv2.putText( vis, text,
00239                      pos, cv2.FONT_HERSHEY_PLAIN, 1.0,
00240                      ( 0, 0, 0), thickness = 2 )
00241 
00242     def draw_picture( self ):
00243         if self.tracker_image == None:
00244             return
00245 
00246         vis = self.tracker_image.copy()
00247         (ySize, xSize, depth) = vis.shape
00248 
00249         cx_min = int(self.xPid.setPointMin * xSize / 100)
00250         cx_max = int(self.xPid.setPointMax * xSize / 100)
00251         cx = int( ( cx_min + cx_max ) / 2 )
00252         
00253         cy_min = int(self.yPid.setPointMin * ySize / 100)
00254         cy_max = int(self.yPid.setPointMax * ySize / 100)
00255         cy = int( ( cy_min + cy_max ) / 2 )
00256 
00257         cv2.rectangle( vis, ( cx_min, cy_min ), ( cx_max, cy_max ),
00258                        ( 0, 255, 255 ) )
00259 
00260         areasqrt = np.sqrt( 640 * 480 )
00261         side_min_half = int( self.zPid.setPointMin * areasqrt / 100 / 2 )
00262         side_max_half = int( self.zPid.setPointMax * areasqrt / 100 / 2 )
00263 
00264         cv2.rectangle( vis, ( cx - side_min_half, cy - side_min_half ),
00265                        ( cx + side_min_half, cy + side_min_half ),
00266                        ( 255, 255, 0 ) )
00267 
00268         cv2.rectangle( vis, ( cx - side_max_half, cy - side_max_half ),
00269                        ( cx + side_max_half, cy + side_max_half ),
00270                        ( 255, 255, 0 ) )
00271 
00272         if self.current_cmd.linear.x > 0:
00273             line_color = ( 0, 255, 0 )
00274         else:
00275             line_color = ( 0, 0, 255 )
00276 
00277         cv2.line( vis, ( 320, 180 ), ( int( xSize/2 - self.current_cmd.angular.z * 320 ),
00278                                        int( ySize/2 - self.current_cmd.linear.z * 180  ) ),
00279                   line_color,
00280                   min( max( int( abs( self.current_cmd.linear.x ) * 255 ), 0 ), 255 ) )
00281 
00282         if self.navdata != None:
00283             self.put_text( vis, 'State: %s' % self.states[ self.navdata.state ],
00284                            ( 150, 240 ) )
00285             self.put_text( vis, 'Battery Percent: %4.1f' % self.navdata.batteryPercent,
00286                            ( 150, 260 ) )
00287 
00288         if self.manual_cmd:
00289             self.put_text( vis, 'MANUAL CONTROL', ( 150, 280 ) )
00290 
00291         if self.auto_cmd:
00292             self.put_text( vis, 'TRACKING', ( 150, 300 ) ) 
00293 
00294         if self.recording:
00295             self.put_text( vis, 'RECORDING', ( 150, 320 ) )
00296 
00297         cv2.imshow( 'AR.Drone Follow', vis )
00298         cv2.waitKey( 1 )
00299 
00300     def timer_cb( self, event ):
00301         self.draw_picture()
00302 
00303         # If we haven't received a found point in a second, let found_point be
00304         # (0,0,-1)
00305         if ( self.found_time == None or
00306              ( rospy.Time.now() - self.found_time ).to_sec() > 1 ):
00307             self.found_point = Point( 0, 0, -1.0 )
00308             self.found_time = rospy.Time.now()
00309 
00310         if event.last_real == None:
00311             dt = 0
00312         else:
00313             dt = ( event.current_real - event.last_real ).to_sec()
00314 
00315         if self.found_point.z == -1.0:
00316             self.current_cmd = Twist()
00317             self.setLedAnim( 0, 2 )
00318         else:
00319             self.current_cmd.angular.z = self.xPid.get_output( self.found_point.x,
00320                                                                dt )
00321             self.current_cmd.linear.z = self.yPid.get_output( self.found_point.y,
00322                                                               dt )
00323             self.current_cmd.linear.x = self.zPid.get_output( self.found_point.z,
00324                                                               dt )
00325 
00326             self.setLedAnim( 8, 2 )
00327 
00328         if self.auto_cmd == False or self.manual_cmd == True:
00329             self.setLedAnim( 9 )
00330             return
00331 
00332         self.goal_vel_pub.publish( self.current_cmd )
00333 
00334 
00335 def main():
00336     rospy.init_node( 'ardrone_follow' )
00337     af = ArdroneFollow()
00338 
00339     try:
00340         rospy.spin()
00341     except KeyboardInterrupt:
00342         print "Keyboard interrupted"
00343         af.usb_service()
00344 
00345 if __name__ == '__main__':
00346     main()


falkor_ardrone
Author(s): Sameer Parekh
autogenerated on Tue Jan 7 2014 11:12:13