├── .gitignore ├── CMakeLists.txt ├── Makefile ├── launch ├── joystick_controller.launchtemplate ├── keyboard_controller.launch ├── keyboard_controller_outdoor.launch └── keyboard_controller_with_tags.launch ├── mainpage.dox ├── manifest.xml ├── package.xml └── src ├── drone_controller.py ├── drone_status.py ├── drone_video_display.py ├── joystick_controller.py └── keyboard_controller.py /.gitignore: -------------------------------------------------------------------------------- 1 | */bin/* 2 | */build/* 3 | *.pyc 4 | build -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(ardrone_tutorials) 3 | 4 | find_package(catkin REQUIRED) 5 | catkin_package() 6 | 7 | #catkin_python_setup() 8 | 9 | 10 | install(PROGRAMS src/drone_controller.py 11 | src/drone_status.py 12 | src/drone_video_display.py 13 | src/joystick_controller.py 14 | src/keyboard_controller.py 15 | DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 16 | ) 17 | 18 | install(DIRECTORY launch/ 19 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch 20 | ) -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include $(shell rospack find mk)/cmake.mk -------------------------------------------------------------------------------- /launch/joystick_controller.launchtemplate: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /launch/keyboard_controller.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /launch/keyboard_controller_outdoor.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /launch/keyboard_controller_with_tags.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /mainpage.dox: -------------------------------------------------------------------------------- 1 | /** 2 | \mainpage 3 | \htmlinclude manifest.html 4 | 5 | \b ardrone_tutorials 6 | 7 | 10 | 11 | --> 12 | 13 | 14 | */ 15 | -------------------------------------------------------------------------------- /manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Controllers developed for the "Up and flying with the AR.Drone and ROS" tutorial series 4 | 5 | Mike Hamer 6 | BSD 7 | 8 | http://github.com/mikehamer/ardrone_tutorials 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | ardrone_tutorials 3 | 1.0.0 4 | 5 | Controllers developed for the "Up and flying with the AR.Drone and ROS" tutorial series 6 | 7 | Mike Hamer 8 | 9 | catkin 10 | 11 | ardrone_autonomy 12 | joy 13 | 14 | BSD 15 | http://github.com/mikehamer/ardrone_tutorials 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/drone_controller.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # A basic drone controller class for the tutorial "Up and flying with the AR.Drone and ROS | Getting Started" 4 | # https://github.com/mikehamer/ardrone_tutorials_getting_started 5 | 6 | # This class implements basic control functionality which we will be using in future tutorials. 7 | # It can command takeoff/landing/emergency as well as drone movement 8 | # It also tracks the drone state based on navdata feedback 9 | 10 | # Import the ROS libraries, and load the manifest file which through will give us access to the project dependencies 11 | import roslib; roslib.load_manifest('ardrone_tutorials') 12 | import rospy 13 | 14 | # Import the messages we're interested in sending and receiving 15 | from geometry_msgs.msg import Twist # for sending commands to the drone 16 | from std_msgs.msg import Empty # for land/takeoff/emergency 17 | from ardrone_autonomy.msg import Navdata # for receiving navdata feedback 18 | 19 | # An enumeration of Drone Statuses 20 | from drone_status import DroneStatus 21 | 22 | 23 | # Some Constants 24 | COMMAND_PERIOD = 100 #ms 25 | 26 | 27 | class BasicDroneController(object): 28 | def __init__(self): 29 | # Holds the current drone status 30 | self.status = -1 31 | 32 | # Subscribe to the /ardrone/navdata topic, of message type navdata, and call self.ReceiveNavdata when a message is received 33 | self.subNavdata = rospy.Subscriber('/ardrone/navdata',Navdata,self.ReceiveNavdata) 34 | 35 | # Allow the controller to publish to the /ardrone/takeoff, land and reset topics 36 | self.pubLand = rospy.Publisher('/ardrone/land',Empty) 37 | self.pubTakeoff = rospy.Publisher('/ardrone/takeoff',Empty) 38 | self.pubReset = rospy.Publisher('/ardrone/reset',Empty) 39 | 40 | # Allow the controller to publish to the /cmd_vel topic and thus control the drone 41 | self.pubCommand = rospy.Publisher('/cmd_vel',Twist) 42 | 43 | # Setup regular publishing of control packets 44 | self.command = Twist() 45 | self.commandTimer = rospy.Timer(rospy.Duration(COMMAND_PERIOD/1000.0),self.SendCommand) 46 | 47 | # Land the drone if we are shutting down 48 | rospy.on_shutdown(self.SendLand) 49 | 50 | def ReceiveNavdata(self,navdata): 51 | # Although there is a lot of data in this packet, we're only interested in the state at the moment 52 | self.status = navdata.state 53 | 54 | def SendTakeoff(self): 55 | # Send a takeoff message to the ardrone driver 56 | # Note we only send a takeoff message if the drone is landed - an unexpected takeoff is not good! 57 | if(self.status == DroneStatus.Landed): 58 | self.pubTakeoff.publish(Empty()) 59 | 60 | def SendLand(self): 61 | # Send a landing message to the ardrone driver 62 | # Note we send this in all states, landing can do no harm 63 | self.pubLand.publish(Empty()) 64 | 65 | def SendEmergency(self): 66 | # Send an emergency (or reset) message to the ardrone driver 67 | self.pubReset.publish(Empty()) 68 | 69 | def SetCommand(self,roll=0,pitch=0,yaw_velocity=0,z_velocity=0): 70 | # Called by the main program to set the current command 71 | self.command.linear.x = pitch 72 | self.command.linear.y = roll 73 | self.command.linear.z = z_velocity 74 | self.command.angular.z = yaw_velocity 75 | 76 | def SendCommand(self,event): 77 | # The previously set command is then sent out periodically if the drone is flying 78 | if self.status == DroneStatus.Flying or self.status == DroneStatus.GotoHover or self.status == DroneStatus.Hovering: 79 | self.pubCommand.publish(self.command) 80 | 81 | -------------------------------------------------------------------------------- /src/drone_status.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # An enumeration of drone statuses for the tutorial "Up and flying with the AR.Drone and ROS | Getting Started" 4 | # https://github.com/mikehamer/ardrone_tutorials_getting_started 5 | 6 | class DroneStatus(object): 7 | Emergency = 0 8 | Inited = 1 9 | Landed = 2 10 | Flying = 3 11 | Hovering = 4 12 | Test = 5 13 | TakingOff = 6 14 | GotoHover = 7 15 | Landing = 8 16 | Looping = 9 -------------------------------------------------------------------------------- /src/drone_video_display.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # A basic video display window for the tutorial "Up and flying with the AR.Drone and ROS | Getting Started" 4 | # https://github.com/mikehamer/ardrone_tutorials_getting_started 5 | 6 | # This display window listens to the drone's video feeds and updates the display at regular intervals 7 | # It also tracks the drone's status and any connection problems, displaying them in the window's status bar 8 | # By default it includes no control functionality. The class can be extended to implement key or mouse listeners if required 9 | 10 | # Import the ROS libraries, and load the manifest file which through will give us access to the project dependencies 11 | import roslib; roslib.load_manifest('ardrone_tutorials') 12 | import rospy 13 | 14 | # Import the two types of messages we're interested in 15 | from sensor_msgs.msg import Image # for receiving the video feed 16 | from ardrone_autonomy.msg import Navdata # for receiving navdata feedback 17 | 18 | # We need to use resource locking to handle synchronization between GUI thread and ROS topic callbacks 19 | from threading import Lock 20 | 21 | # An enumeration of Drone Statuses 22 | from drone_status import DroneStatus 23 | 24 | # The GUI libraries 25 | from PySide import QtCore, QtGui 26 | 27 | 28 | # Some Constants 29 | CONNECTION_CHECK_PERIOD = 250 #ms 30 | GUI_UPDATE_PERIOD = 20 #ms 31 | DETECT_RADIUS = 4 # the radius of the circle drawn when a tag is detected 32 | 33 | 34 | class DroneVideoDisplay(QtGui.QMainWindow): 35 | StatusMessages = { 36 | DroneStatus.Emergency : 'Emergency', 37 | DroneStatus.Inited : 'Initialized', 38 | DroneStatus.Landed : 'Landed', 39 | DroneStatus.Flying : 'Flying', 40 | DroneStatus.Hovering : 'Hovering', 41 | DroneStatus.Test : 'Test (?)', 42 | DroneStatus.TakingOff : 'Taking Off', 43 | DroneStatus.GotoHover : 'Going to Hover Mode', 44 | DroneStatus.Landing : 'Landing', 45 | DroneStatus.Looping : 'Looping (?)' 46 | } 47 | DisconnectedMessage = 'Disconnected' 48 | UnknownMessage = 'Unknown Status' 49 | 50 | def __init__(self): 51 | # Construct the parent class 52 | super(DroneVideoDisplay, self).__init__() 53 | 54 | # Setup our very basic GUI - a label which fills the whole window and holds our image 55 | self.setWindowTitle('AR.Drone Video Feed') 56 | self.imageBox = QtGui.QLabel(self) 57 | self.setCentralWidget(self.imageBox) 58 | 59 | # Subscribe to the /ardrone/navdata topic, of message type navdata, and call self.ReceiveNavdata when a message is received 60 | self.subNavdata = rospy.Subscriber('/ardrone/navdata',Navdata,self.ReceiveNavdata) 61 | 62 | # Subscribe to the drone's video feed, calling self.ReceiveImage when a new frame is received 63 | self.subVideo = rospy.Subscriber('/ardrone/image_raw',Image,self.ReceiveImage) 64 | 65 | # Holds the image frame received from the drone and later processed by the GUI 66 | self.image = None 67 | self.imageLock = Lock() 68 | 69 | self.tags = [] 70 | self.tagLock = Lock() 71 | 72 | # Holds the status message to be displayed on the next GUI update 73 | self.statusMessage = '' 74 | 75 | # Tracks whether we have received data since the last connection check 76 | # This works because data comes in at 50Hz but we're checking for a connection at 4Hz 77 | self.communicationSinceTimer = False 78 | self.connected = False 79 | 80 | # A timer to check whether we're still connected 81 | self.connectionTimer = QtCore.QTimer(self) 82 | self.connectionTimer.timeout.connect(self.ConnectionCallback) 83 | self.connectionTimer.start(CONNECTION_CHECK_PERIOD) 84 | 85 | # A timer to redraw the GUI 86 | self.redrawTimer = QtCore.QTimer(self) 87 | self.redrawTimer.timeout.connect(self.RedrawCallback) 88 | self.redrawTimer.start(GUI_UPDATE_PERIOD) 89 | 90 | # Called every CONNECTION_CHECK_PERIOD ms, if we haven't received anything since the last callback, will assume we are having network troubles and display a message in the status bar 91 | def ConnectionCallback(self): 92 | self.connected = self.communicationSinceTimer 93 | self.communicationSinceTimer = False 94 | 95 | def RedrawCallback(self): 96 | if self.image is not None: 97 | # We have some issues with locking between the display thread and the ros messaging thread due to the size of the image, so we need to lock the resources 98 | self.imageLock.acquire() 99 | try: 100 | # Convert the ROS image into a QImage which we can display 101 | image = QtGui.QPixmap.fromImage(QtGui.QImage(self.image.data, self.image.width, self.image.height, QtGui.QImage.Format_RGB888)) 102 | if len(self.tags) > 0: 103 | self.tagLock.acquire() 104 | try: 105 | painter = QtGui.QPainter() 106 | painter.begin(image) 107 | painter.setPen(QtGui.QColor(0,255,0)) 108 | painter.setBrush(QtGui.QColor(0,255,0)) 109 | for (x,y,d) in self.tags: 110 | r = QtCore.QRectF((x*image.width())/1000-DETECT_RADIUS,(y*image.height())/1000-DETECT_RADIUS,DETECT_RADIUS*2,DETECT_RADIUS*2) 111 | painter.drawEllipse(r) 112 | painter.drawText((x*image.width())/1000+DETECT_RADIUS,(y*image.height())/1000-DETECT_RADIUS,str(d/100)[0:4]+'m') 113 | painter.end() 114 | finally: 115 | self.tagLock.release() 116 | finally: 117 | self.imageLock.release() 118 | 119 | # We could do more processing (eg OpenCV) here if we wanted to, but for now lets just display the window. 120 | self.resize(image.width(),image.height()) 121 | self.imageBox.setPixmap(image) 122 | 123 | # Update the status bar to show the current drone status & battery level 124 | self.statusBar().showMessage(self.statusMessage if self.connected else self.DisconnectedMessage) 125 | 126 | def ReceiveImage(self,data): 127 | # Indicate that new data has been received (thus we are connected) 128 | self.communicationSinceTimer = True 129 | 130 | # We have some issues with locking between the GUI update thread and the ROS messaging thread due to the size of the image, so we need to lock the resources 131 | self.imageLock.acquire() 132 | try: 133 | self.image = data # Save the ros image for processing by the display thread 134 | finally: 135 | self.imageLock.release() 136 | 137 | def ReceiveNavdata(self,navdata): 138 | # Indicate that new data has been received (thus we are connected) 139 | self.communicationSinceTimer = True 140 | 141 | # Update the message to be displayed 142 | msg = self.StatusMessages[navdata.state] if navdata.state in self.StatusMessages else self.UnknownMessage 143 | self.statusMessage = '{} (Battery: {}%)'.format(msg,int(navdata.batteryPercent)) 144 | 145 | self.tagLock.acquire() 146 | try: 147 | if navdata.tags_count > 0: 148 | self.tags = [(navdata.tags_xc[i],navdata.tags_yc[i],navdata.tags_distance[i]) for i in range(0,navdata.tags_count)] 149 | else: 150 | self.tags = [] 151 | finally: 152 | self.tagLock.release() 153 | 154 | if __name__=='__main__': 155 | import sys 156 | rospy.init_node('ardrone_video_display') 157 | app = QtGui.QApplication(sys.argv) 158 | display = DroneVideoDisplay() 159 | display.show() 160 | status = app.exec_() 161 | rospy.signal_shutdown('Great Flying!') 162 | sys.exit(status) 163 | -------------------------------------------------------------------------------- /src/joystick_controller.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # The Joystick Controller Node for the tutorial "Up and flying with the AR.Drone and ROS | Joystick Control" 4 | # https://github.com/mikehamer/ardrone_tutorials 5 | 6 | # This controller implements the base DroneVideoDisplay class, the DroneController class and subscribes to joystick messages 7 | 8 | # Import the ROS libraries, and load the manifest file which through will give us access to the project dependencies 9 | import roslib; roslib.load_manifest('ardrone_tutorials') 10 | import rospy 11 | 12 | # Load the DroneController class, which handles interactions with the drone, and the DroneVideoDisplay class, which handles video display 13 | from drone_controller import BasicDroneController 14 | from drone_video_display import DroneVideoDisplay 15 | 16 | # Import the joystick message 17 | from sensor_msgs.msg import Joy 18 | 19 | # Finally the GUI libraries 20 | from PySide import QtCore, QtGui 21 | 22 | # define the default mapping between joystick buttons and their corresponding actions 23 | ButtonEmergency = 0 24 | ButtonLand = 1 25 | ButtonTakeoff = 2 26 | 27 | # define the default mapping between joystick axes and their corresponding directions 28 | AxisRoll = 0 29 | AxisPitch = 1 30 | AxisYaw = 3 31 | AxisZ = 4 32 | 33 | # define the default scaling to apply to the axis inputs. useful where an axis is inverted 34 | ScaleRoll = 1.0 35 | ScalePitch = 1.0 36 | ScaleYaw = 1.0 37 | ScaleZ = 1.0 38 | 39 | # handles the reception of joystick packets 40 | def ReceiveJoystickMessage(data): 41 | if data.buttons[ButtonEmergency]==1: 42 | rospy.loginfo("Emergency Button Pressed") 43 | controller.SendEmergency() 44 | elif data.buttons[ButtonLand]==1: 45 | rospy.loginfo("Land Button Pressed") 46 | controller.SendLand() 47 | elif data.buttons[ButtonTakeoff]==1: 48 | rospy.loginfo("Takeoff Button Pressed") 49 | controller.SendTakeoff() 50 | else: 51 | controller.SetCommand(data.axes[AxisRoll]/ScaleRoll,data.axes[AxisPitch]/ScalePitch,data.axes[AxisYaw]/ScaleYaw,data.axes[AxisZ]/ScaleZ) 52 | 53 | 54 | # Setup the application 55 | if __name__=='__main__': 56 | import sys 57 | # Firstly we setup a ros node, so that we can communicate with the other packages 58 | rospy.init_node('ardrone_joystick_controller') 59 | 60 | # Next load in the parameters from the launch-file 61 | ButtonEmergency = int ( rospy.get_param("~ButtonEmergency",ButtonEmergency) ) 62 | ButtonLand = int ( rospy.get_param("~ButtonLand",ButtonLand) ) 63 | ButtonTakeoff = int ( rospy.get_param("~ButtonTakeoff",ButtonTakeoff) ) 64 | AxisRoll = int ( rospy.get_param("~AxisRoll",AxisRoll) ) 65 | AxisPitch = int ( rospy.get_param("~AxisPitch",AxisPitch) ) 66 | AxisYaw = int ( rospy.get_param("~AxisYaw",AxisYaw) ) 67 | AxisZ = int ( rospy.get_param("~AxisZ",AxisZ) ) 68 | ScaleRoll = float ( rospy.get_param("~ScaleRoll",ScaleRoll) ) 69 | ScalePitch = float ( rospy.get_param("~ScalePitch",ScalePitch) ) 70 | ScaleYaw = float ( rospy.get_param("~ScaleYaw",ScaleYaw) ) 71 | ScaleZ = float ( rospy.get_param("~ScaleZ",ScaleZ) ) 72 | 73 | # Now we construct our Qt Application and associated controllers and windows 74 | app = QtGui.QApplication(sys.argv) 75 | display = DroneVideoDisplay() 76 | controller = BasicDroneController() 77 | 78 | # subscribe to the /joy topic and handle messages of type Joy with the function ReceiveJoystickMessage 79 | subJoystick = rospy.Subscriber('/joy', Joy, ReceiveJoystickMessage) 80 | 81 | # executes the QT application 82 | display.show() 83 | status = app.exec_() 84 | 85 | # and only progresses to here once the application has been shutdown 86 | rospy.signal_shutdown('Great Flying!') 87 | sys.exit(status) -------------------------------------------------------------------------------- /src/keyboard_controller.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # The Keyboard Controller Node for the tutorial "Up and flying with the AR.Drone and ROS | Getting Started" 4 | # https://github.com/mikehamer/ardrone_tutorials 5 | 6 | # This controller extends the base DroneVideoDisplay class, adding a keypress handler to enable keyboard control of the drone 7 | 8 | # Import the ROS libraries, and load the manifest file which through will give us access to the project dependencies 9 | import roslib; roslib.load_manifest('ardrone_tutorials') 10 | import rospy 11 | 12 | # Load the DroneController class, which handles interactions with the drone, and the DroneVideoDisplay class, which handles video display 13 | from drone_controller import BasicDroneController 14 | from drone_video_display import DroneVideoDisplay 15 | 16 | # Finally the GUI libraries 17 | from PySide import QtCore, QtGui 18 | 19 | 20 | # Here we define the keyboard map for our controller (note that python has no enums, so we use a class) 21 | class KeyMapping(object): 22 | PitchForward = QtCore.Qt.Key.Key_E 23 | PitchBackward = QtCore.Qt.Key.Key_D 24 | RollLeft = QtCore.Qt.Key.Key_S 25 | RollRight = QtCore.Qt.Key.Key_F 26 | YawLeft = QtCore.Qt.Key.Key_W 27 | YawRight = QtCore.Qt.Key.Key_R 28 | IncreaseAltitude = QtCore.Qt.Key.Key_Q 29 | DecreaseAltitude = QtCore.Qt.Key.Key_A 30 | Takeoff = QtCore.Qt.Key.Key_Y 31 | Land = QtCore.Qt.Key.Key_H 32 | Emergency = QtCore.Qt.Key.Key_Space 33 | 34 | 35 | # Our controller definition, note that we extend the DroneVideoDisplay class 36 | class KeyboardController(DroneVideoDisplay): 37 | def __init__(self): 38 | super(KeyboardController,self).__init__() 39 | 40 | self.pitch = 0 41 | self.roll = 0 42 | self.yaw_velocity = 0 43 | self.z_velocity = 0 44 | 45 | # We add a keyboard handler to the DroneVideoDisplay to react to keypresses 46 | def keyPressEvent(self, event): 47 | key = event.key() 48 | 49 | # If we have constructed the drone controller and the key is not generated from an auto-repeating key 50 | if controller is not None and not event.isAutoRepeat(): 51 | # Handle the important cases first! 52 | if key == KeyMapping.Emergency: 53 | controller.SendEmergency() 54 | elif key == KeyMapping.Takeoff: 55 | controller.SendTakeoff() 56 | elif key == KeyMapping.Land: 57 | controller.SendLand() 58 | else: 59 | # Now we handle moving, notice that this section is the opposite (+=) of the keyrelease section 60 | if key == KeyMapping.YawLeft: 61 | self.yaw_velocity += 1 62 | elif key == KeyMapping.YawRight: 63 | self.yaw_velocity += -1 64 | 65 | elif key == KeyMapping.PitchForward: 66 | self.pitch += 1 67 | elif key == KeyMapping.PitchBackward: 68 | self.pitch += -1 69 | 70 | elif key == KeyMapping.RollLeft: 71 | self.roll += 1 72 | elif key == KeyMapping.RollRight: 73 | self.roll += -1 74 | 75 | elif key == KeyMapping.IncreaseAltitude: 76 | self.z_velocity += 1 77 | elif key == KeyMapping.DecreaseAltitude: 78 | self.z_velocity += -1 79 | 80 | # finally we set the command to be sent. The controller handles sending this at regular intervals 81 | controller.SetCommand(self.roll, self.pitch, self.yaw_velocity, self.z_velocity) 82 | 83 | 84 | def keyReleaseEvent(self,event): 85 | key = event.key() 86 | 87 | # If we have constructed the drone controller and the key is not generated from an auto-repeating key 88 | if controller is not None and not event.isAutoRepeat(): 89 | # Note that we don't handle the release of emergency/takeoff/landing keys here, there is no need. 90 | # Now we handle moving, notice that this section is the opposite (-=) of the keypress section 91 | if key == KeyMapping.YawLeft: 92 | self.yaw_velocity -= 1 93 | elif key == KeyMapping.YawRight: 94 | self.yaw_velocity -= -1 95 | 96 | elif key == KeyMapping.PitchForward: 97 | self.pitch -= 1 98 | elif key == KeyMapping.PitchBackward: 99 | self.pitch -= -1 100 | 101 | elif key == KeyMapping.RollLeft: 102 | self.roll -= 1 103 | elif key == KeyMapping.RollRight: 104 | self.roll -= -1 105 | 106 | elif key == KeyMapping.IncreaseAltitude: 107 | self.z_velocity -= 1 108 | elif key == KeyMapping.DecreaseAltitude: 109 | self.z_velocity -= -1 110 | 111 | # finally we set the command to be sent. The controller handles sending this at regular intervals 112 | controller.SetCommand(self.roll, self.pitch, self.yaw_velocity, self.z_velocity) 113 | 114 | 115 | 116 | # Setup the application 117 | if __name__=='__main__': 118 | import sys 119 | # Firstly we setup a ros node, so that we can communicate with the other packages 120 | rospy.init_node('ardrone_keyboard_controller') 121 | 122 | # Now we construct our Qt Application and associated controllers and windows 123 | app = QtGui.QApplication(sys.argv) 124 | controller = BasicDroneController() 125 | display = KeyboardController() 126 | 127 | display.show() 128 | 129 | # executes the QT application 130 | status = app.exec_() 131 | 132 | # and only progresses to here once the application has been shutdown 133 | rospy.signal_shutdown('Great Flying!') 134 | sys.exit(status) --------------------------------------------------------------------------------