Last active
February 14, 2021 02:42
-
-
Save microlinux/82409619242be5f7210b7fea7bb4251e to your computer and use it in GitHub Desktop.
Python interface for the Ryze Tello drone
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""3/21/2018 DEVELOPMENT HAS MOVED TO https://github.com/microlinux/tello""" | |
"""License. | |
Copyright 2018 Todd Mueller <[email protected]> | |
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 3 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. | |
You should have received a copy of the GNU General Public License | |
along with this program. If not, see <http://www.gnu.org/licenses/>. | |
""" | |
import socket | |
import threading | |
import time | |
import traceback | |
class Tello: | |
"""Wrapper around the Tello API. | |
WARNING: THIS CODE HAS NOT BEEN TESTED AGAINST A REAL TELLO | |
""" | |
def __init__(self, local_ip, local_port, imperial=True, command_timeout=.3, tello_ip='192.168.10.1', tello_port=8889): | |
"""Binds to the local IP/port and puts the Tello into command mode. | |
Args: | |
local_ip (str): Local IP address to bind. | |
local_port (int): Local port to bind. | |
imperial (bool): If True, speed is MPH and distance is feet. | |
If False, speed is KPH and distance is meters. | |
command_timeout (int|float): Number of seconds to wait for a response to a command. | |
tello_ip (str): Tello IP. | |
tello_port (int): Tello port. | |
Raises: | |
RuntimeError: If the Tello rejects the attempt to enter command mode. | |
""" | |
self.abort_flag = False | |
self.command_timeout = command_timeout | |
self.imperial = imperial | |
self.response = None | |
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
self.tello_address = (tello_ip, tello_port) | |
self.socket.bind((local_ip, local_port)) | |
self.receive_thread = threading.Thread(target=self._receive_thread) | |
self.receive_thread.daemon=True | |
self.receive_thread.start() | |
if self.send_command('command') != 'OK': | |
raise RuntimeError('Tello rejected attempt to enter command mode') | |
def __del__(self): | |
"""Closes the local socket.""" | |
self.socket.close() | |
def _receive_thread(self): | |
"""Listens for responses from the Tello. | |
Runs as a thread, sets self.response to whatever the Tello last returned. | |
""" | |
while True: | |
try: | |
self.response, ip = self.socket.recvfrom(256) | |
except Exception: | |
break | |
def flip(self, direction): | |
"""Flips. | |
Args: | |
direction (str): Direction to flip, 'l', 'r', 'f', 'b', 'lb', 'lf', 'rb' or 'rf'. | |
Returns: | |
str: Response from Tello, 'OK' or 'FALSE'. | |
""" | |
return self.send_command('flip %s' % direction) | |
def get_battery(self): | |
"""Returns percent battery life remaining. | |
Returns: | |
int: Percent battery life remaining. | |
""" | |
battery = self.send_command('battery?') | |
try: | |
battery = int(battery) | |
except: | |
pass | |
return battery | |
def get_flight_time(self): | |
"""Returns the number of seconds elapsed during flight. | |
Returns: | |
int: Seconds elapsed during flight. | |
""" | |
flight_time = self.send_command('time?') | |
try: | |
flight_time = int(flight_time) | |
except: | |
pass | |
return flight_time | |
def get_speed(self): | |
"""Returns the current speed. | |
Returns: | |
int: Current speed in KPH or MPH. | |
""" | |
speed = self.send_command('speed?') | |
try: | |
speed = float(speed) | |
if self.imperial is True: | |
speed = round((speed / 44.704), 1) | |
else: | |
speed = round((speed / 27.7778), 1) | |
except: | |
pass | |
return speed | |
def land(self): | |
"""Initiates landing. | |
Returns: | |
str: Response from Tello, 'OK' or 'FALSE'. | |
""" | |
return self.send_command('land') | |
def move(self, direction, distance): | |
"""Moves in a direction for a distance. | |
This method expects meters or feet. The Tello API expects distances | |
from 20 to 500 centimeters. | |
Metric: .02 to 5 meters | |
Imperial: .7 to 16.4 feet | |
Args: | |
direction (str): Direction to move, 'forward', 'back', 'right' or 'left'. | |
distance (int|float): Distance to move. | |
Returns: | |
str: Response from Tello, 'OK' or 'FALSE'. | |
""" | |
distance = float(distance) | |
if self.imperial is True: | |
distance = int(round(distance * 30.48)) | |
else: | |
distance = int(round(distance * 100)) | |
return self.send_command('%s %s' % (direction, distance)) | |
def move_backward(self, distance): | |
"""Moves backward for a distance. | |
See comments for Tello.move(). | |
Args: | |
distance (int): Distance to move. | |
Returns: | |
str: Response from Tello, 'OK' or 'FALSE'. | |
""" | |
return self.move('back', distance) | |
def move_down(self, distance): | |
"""Moves down for a distance. | |
See comments for Tello.move(). | |
Args: | |
distance (int): Distance to move. | |
Returns: | |
str: Response from Tello, 'OK' or 'FALSE'. | |
""" | |
return self.move('down', distance) | |
def move_forward(self, distance): | |
"""Moves forward for a distance. | |
See comments for Tello.move(). | |
Args: | |
distance (int): Distance to move. | |
Returns: | |
str: Response from Tello, 'OK' or 'FALSE'. | |
""" | |
return self.move('forward', distance) | |
def move_left(self, distance): | |
"""Moves left for a distance. | |
See comments for Tello.move(). | |
Args: | |
distance (int): Distance to move. | |
Returns: | |
str: Response from Tello, 'OK' or 'FALSE'. | |
""" | |
return self.move('left', distance) | |
def move_right(self, distance): | |
"""Moves right for a distance. | |
See comments for Tello.move(). | |
Args: | |
distance (int): Distance to move. | |
""" | |
return self.move('right', distance) | |
def move_up(self, distance): | |
"""Moves up for a distance. | |
See comments for Tello.move(). | |
Args: | |
distance (int): Distance to move. | |
Returns: | |
str: Response from Tello, 'OK' or 'FALSE'. | |
""" | |
return self.move('up', distance) | |
def send_command(self, command): | |
"""Sends a command to the Tello and waits for a response. | |
If self.command_timeout is exceeded before a response is received, | |
a RuntimeError exception is raised. | |
Args: | |
command (str): Command to send. | |
Returns: | |
str: Response from Tello. | |
Raises: | |
RuntimeError: If no response is received within self.timeout seconds. | |
""" | |
self.abort_flag = False | |
timer = threading.Timer(self.command_timeout, self.set_abort_flag) | |
self.socket.sendto(command.encode('utf-8'), self.tello_address) | |
timer.start() | |
while self.response is None: | |
if self.abort_flag is True: | |
raise RuntimeError('No response to command') | |
timer.cancel() | |
response = self.response.decode('utf-8') | |
self.response = None | |
return response | |
def set_abort_flag(self): | |
"""Sets self.abort_flag to True. | |
Used by the timer in Tello.send_command() to indicate to that a response | |
timeout has occurred. | |
""" | |
self.abort_flag = True | |
def set_speed(self, speed): | |
"""Sets speed. | |
This method expects KPH or MPH. The Tello API expects speeds from | |
1 to 100 centimeters/second. | |
Metric: .1 to 3.6 KPH | |
Imperial: .1 to 2.2 MPH | |
Args: | |
speed (int|float): Speed. | |
Returns: | |
str: Response from Tello, 'OK' or 'FALSE'. | |
""" | |
speed = float(speed) | |
if self.imperial is True: | |
speed = int(round(speed * 44.704)) | |
else: | |
speed = int(round(speed * 27.7778)) | |
return self.send_command('speed %s' % speed) | |
def takeoff(self): | |
"""Initiates take-off. | |
Returns: | |
str: Response from Tello, 'OK' or 'FALSE'. | |
""" | |
return self.send_command('takeoff') | |
def rotate_cw(self, degrees): | |
"""Rotates clockwise. | |
Args: | |
degrees (int): Degrees to rotate, 1 to 360. | |
Returns: | |
str: Response from Tello, 'OK' or 'FALSE'. | |
""" | |
return self.send_command('cw %s' % degrees) | |
def rotate_ccw(self, degrees): | |
"""Rotates counter-clockwise. | |
Args: | |
degrees (int): Degrees to rotate, 1 to 360. | |
Returns: | |
str: Response from Tello, 'OK' or 'FALSE'. | |
""" | |
return self.send_command('ccw %s' % degrees) | |
if __name__ == '__main__': | |
try: | |
print 'WARNING: THIS PROGRAM HAS NOT BEEN TESTED AGAINST A REAL TELLO' | |
print 'In theory, your Tello is about to ...' | |
print '1.) Take off' | |
print '2.) Set speed to 2 MPH' | |
print '3.) Move up 2 feet' | |
print '4.) Move foward 4 feet' | |
print '5.) Move left 4 feet' | |
print '6.) Rotate clockwise 90 degrees' | |
print '7.) Rotate counter-clockwise 270 degrees' | |
print '8.) Move forward 4 feet' | |
print '9.) Land' | |
print 'The Tello will begin flying after entering the IP address of this computer' | |
local_ip = raw_input('IP address of this computer: ') | |
tello = Tello(local_ip, 32768) | |
print 'Intiating take-off' | |
print 'Tello response: %s' % tello.takeoff() | |
print 'Waiting 10 seconds before continuing' | |
time.sleep(10) | |
print 'Setting speed to 2 MPH' | |
print 'Tello response: %s' % tello.set_speed(2) | |
print 'Waiting 2 seconds before continuing' | |
time.sleep(2) | |
print 'Moving up 2 feet' | |
print 'Tello response: %s' % tello.move_up(2) | |
print 'Waiting 5 seconds before continuing' | |
time.sleep(5) | |
print 'Moving forward 4 feet' | |
print 'Tello response: %s' % tello.move_forward(4) | |
print 'Waiting 10 seconds before continuing' | |
time.sleep(10) | |
print 'Moving left 4 feet' | |
print 'Tello response: %s' % tello.move_left(4) | |
print 'Waiting 10 seconds before continuing' | |
time.sleep(10) | |
print 'Rotating clockwise 90 degrees' | |
print 'Tello response: %s' % tello.rotate_cw(90) | |
print 'Waiting 5 seconds before continuing' | |
time.sleep(5) | |
print 'Rotating counter-clockwise 270 degrees' | |
print 'Tello response: %s' % tello.rotate_ccw(270) | |
print 'Waiting 10 seconds before continuing' | |
time.sleep(10) | |
print 'Moving forward 4 feet' | |
print 'Tello response: %s' % tello.move_forward(4) | |
print 'Waiting 10 seconds before continuing' | |
time.sleep(10) | |
print 'Intiating landing' | |
print 'Tello response: %s' % tello.land() | |
print 'Program done.' | |
except Exception as e: | |
print traceback.format_exc() | |
exit(1) | |
exit(0) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment