Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated to DJITellopy, added an emergancy stop, and more #16

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# tello_sim

**tello_sim** is a simple Python simulator (sim) that can be used by students to test their [tello](https://www.ryzerobotics.com/tello-edu) flight plans before deploying them to a real drone. It was inspired by the [easyTello](https://github.com/Virodroid/easyTello) library and uses it for the drone interface.
**tello_sim** is a simple Python simulator (sim) that can be used by students to test their [tello](https://www.ryzerobotics.com/tello-edu) flight plans before deploying them to a real drone. In this fork, it uses [DJITellopy](https://github.com/damiafuentes/DJITelloPy)

This is a fork of a previous project written by [Fireline-Science](https://github.com/Fireline-Science/tello_sim), so all credit goes to him for the heavy lifting. I simply am just updating it to help teach my classes.

One suggested use for the sim is to develop an in-class obstacle course for students to fly their drone through. For example, you could have designated launch and landing positions that are separated by a series of obstacles. Obstacles could include tunnels to fly through or corners to navigate around.

Expand Down Expand Up @@ -63,20 +65,20 @@ my_drone.takeoff()
![](/images/takeoff.png)

```python
my_drone.forward(40)
my_drone.move_forward(40)
```
![](/images/forward.png)

By default, the simulator plots a 25 cm error region in light blue around the flight path. For more advanced projects, you can override the default parameter by including the optional second error bar parameter in centimeters like ```forward(40, e=50)```. This is useful when you want to change the model (simulation) based on empirical (actual) testing.


```python
my_drone.cw(45)
my_drone.rotate_counter_clockwise(45)
```
![](/images/cw.png)

```python
my_drone.forward(50)
my_drone.move_forward(50)
```
![](/images/forward_2.png)

Expand Down Expand Up @@ -105,13 +107,13 @@ In a classroom, it can be useful to allow students to share their command script
]
},
{
"command": "cw",
"command": "rotate_counter_clockwise",
"arguments": [
90
]
},
{
"command": "forward",
"command": "move_forward",
"arguments": [
100
]
Expand Down Expand Up @@ -143,7 +145,7 @@ my_drone.reset()
```

## Deploying to a Real Drone
We are using the [easytello](https://github.com/Virodroid/easyTello) library to allow you to deploy your simulated flight to a real drone. Once you are connected to your drone via WiFi, you can deploy the commands you built up in an interactive session or loaded via a command file in Jupyter with the following command:
We are using the [DJITellopy](https://github.com/damiafuentes/DJITelloPy) library to allow you to deploy your simulated flight to a real drone. Once you are connected to your drone via WiFi, you can deploy the commands you built up in an interactive session or loaded via a command file in Jupyter with the following command:

```python
my_drone.deploy()
Expand Down Expand Up @@ -171,3 +173,10 @@ Note: if you are running multiple scripts to the drone, you may have to kill the
lsof -i:8889
kill XXXX
```

If you're on Windows the following commands should work
```

netstat -p tcp -ano | findstr ":21" (Outpuuted as Protocol Local Address Foreign Address State PID)
taskkill /F /ID XXXX
```
76 changes: 38 additions & 38 deletions commands.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,67 +8,67 @@
"arguments": []
},
{
"command": "forward",
"command": "move_forward",
"arguments": [
100
]
},
{
"command": "cw",
"arguments": [
90
]
"command": "rotate_clockwise",
"arguments": [
90
]
},
{
"command": "forward",
"arguments": [
100
]
"command": "move_forward",
"arguments": [
100
]
},
{
"command": "cw",
"arguments": [
90
]
"command": "rotate_clockwise",
"arguments": [
90
]
},
{
"command": "forward",
"arguments": [
100
]
"command": "move_forward",
"arguments": [
100
]
},
{
"command": "cw",
"arguments": [
90
]
"command": "rotate_clockwise",
"arguments": [
90
]
},
{
"command": "forward",
"arguments": [
100
]
"command": "move_forward",
"arguments": [
100
]
},
{
"command": "cw",
"arguments": [
45
]
"command": "rotate_clockwise",
"arguments": [
45
]
},
{
"command": "forward",
"arguments": [
50
]
"command": "move_forward",
"arguments": [
50
]
},
{
"command": "flip",
"arguments": [
"r"
]
"command": "flip",
"arguments": [
"r"
]
},
{
"command": "land",
"arguments": []
}
]
]
6 changes: 3 additions & 3 deletions example.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# flight commands are below
my_drone.takeoff()
my_drone.forward(130)
my_drone.ccw(90)
my_drone.forward(80)
my_drone.move_forward(130)
my_drone.rotate_clockwise(90)
my_drone.move_forward(80)
my_drone.land()
99 changes: 42 additions & 57 deletions example_flight_notebook.ipynb

Large diffs are not rendered by default.

Binary file modified images/cw.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/forward.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/jupyter_notebook.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions my_first_flight.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@
"arguments": []
},
{
"command": "forward",
"command": "move_forward",
"arguments": [
50
]
},
{
"command": "cw",
"command": "rotate_clockwise",
"arguments": [
90
]
},
{
"command": "forward",
"command": "move_forward",
"arguments": [
100
]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
install_requires=[
'pandas',
'matplotlib',
'easytello'
'djitellopy'
],
classifiers=[
'Programming Language :: Python :: 3',
Expand Down
Binary file modified teaching_materials/simulation_intro.pdf
Binary file not shown.
Binary file modified teaching_materials/simulation_intro.pptx
Binary file not shown.
Binary file modified teaching_materials/simulation_teaching.pdf
Binary file not shown.
Binary file modified teaching_materials/simulation_teaching.pptx
Binary file not shown.
67 changes: 52 additions & 15 deletions tello_sim/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import numpy as np
import pandas as pd

from easytello import Tello

from djitellopy import Tello
from djitellopy import TelloException

class Simulator():
def __init__(self):
Expand Down Expand Up @@ -60,13 +60,33 @@ def send_command(self, command: str, *args):
print('I am running your "{}" command.'.format(self.serialize_command(command_json)))

time.sleep(2)


def send_command_with_retries(self, command: str, timeout: int = 7, retries: int = 3, *args):
command_json = {
'command': command,
'arguments': args
}
self.command_log.append(command_json)
print('I am running your "{}" command.'.format(self.serialize_command(command_json)))

for i in range(retries):
try:
response = self.driver_instance.send_command_with_return(command, timeout)
if(response == 'ok'):
return True

except Exception as e:
print("Connection attempt #{} failed trying to send command: {}".format(i, command))
time.sleep(1)
print('Failed to send command after {} retries'.format(retries))

# Control Commands
def command(self):
print("Hi! My name is TelloSim and I am your training drone.")
print("I help you try out your flight plan before sending it to a real Tello.")
print("I am now ready to take off. 🚁")
self.send_command('command')
self.send_command_with_retries('command')

def check_takeoff(self):
if not self.takeoff_state:
Expand Down Expand Up @@ -141,6 +161,16 @@ def takeoff(self):
print("My estimated takeoff altitude is {} centimeters".format(self.altitude))
else:
print("My current altitude is {} centimeters, so I can't takeoff again!".format(self.altitude))


def emergency(self):
"""
Stop all motors immediately.
"""
self.send_command_with_retries("emergency")
self.takeoff_state = False
self.altitude = 0


def land(self, e=25):
"""
Expand All @@ -155,13 +185,13 @@ def land(self, e=25):
print("Get ready for landing!")
self.takeoff_state = False
self.altitude = 0
self.send_command('land')
self.send_command_with_retries('land')
print("Here are the graphs of your flight! I can't wait to try this for real.")
self.plot_horz_steps(e)
self.plot_altitude_steps(e)


def up(self, dist: int, e=25):
def move_up(self, dist: int, e=25):
"""
Command drone to fly up a given number of centimeters.

Expand All @@ -186,7 +216,7 @@ def up(self, dist: int, e=25):
self.send_command('up', dist)
self.plot_altitude_steps(e)

def down(self, dist: int, e=25):
def move_down(self, dist: int, e=25):
"""
Command drone to fly down a given number of centimeters.

Expand All @@ -211,7 +241,7 @@ def down(self, dist: int, e=25):
self.send_command('down', dist)
self.plot_altitude_steps(e)

def left(self, dist: int, e=25):
def move_left(self, dist: int, e=25):
"""
Command drone to fly left a given number of centimeters.

Expand All @@ -238,7 +268,7 @@ def left(self, dist: int, e=25):
self.send_command('left', dist)
self.plot_horz_steps(e)

def right(self, dist: int, e=25):
def move_right(self, dist: int, e=25):
"""
Command drone to fly right a given number of centimeters.

Expand All @@ -264,7 +294,7 @@ def right(self, dist: int, e=25):
self.send_command('right', dist)
self.plot_horz_steps(e)

def forward(self, dist: int, e=25):
def move_forward(self, dist: int, e=25):
"""
Command drone to fly forward a given number of centimeters.

Expand All @@ -290,7 +320,7 @@ def forward(self, dist: int, e=25):
self.send_command('forward', dist)
self.plot_horz_steps(e)

def back(self, dist: int, e=25):
def move_back(self, dist: int, e=25):
"""
Command drone to fly backward a given number of centimeters.

Expand All @@ -315,7 +345,7 @@ def back(self, dist: int, e=25):
self.send_command('back', dist)
self.plot_horz_steps(e)

def cw(self, degr: int):
def rotate_clockwise(self, degr: int):
"""
Rotate drone clockwise.

Expand All @@ -335,7 +365,7 @@ def cw(self, degr: int):
self.send_command('cw', degr)
print("My new bearing is {} degrees.".format(self.bearing))

def ccw(self, degr: int):
def rotate_counter_clockwise(self, degr: int):
"""
Rotate drone counter clockwise.

Expand All @@ -345,7 +375,7 @@ def ccw(self, degr: int):

Examples
----------
drone.ccw(90) # rotates drone 90 degrees counter clockwise
drone.rotate_counter_clockwise(90) # rotates drone 90 degrees counter clockwise

"""
self.check_takeoff()
Expand Down Expand Up @@ -381,6 +411,7 @@ def flip(self, direc: str, e=25):
self.send_command('flip', direc)
self.flip_coors.append(self.cur_loc)
self.plot_horz_steps(e)


# Deploys the command log from the simulation state to the actual drone
def deploy(self):
Expand All @@ -399,10 +430,16 @@ def deploy(self):
# Since the driver binds to a socket on instantiation, we can only
# keep a single driver instance open per session
self.driver_instance = Tello()

try:
self.driver_instance.connect()
except Exception as e:
print("Failed to connect to drone. Please check that you are connected to the drone's WiFi network. If you're still having issues, please ask for help!")

for command in self.command_log:
self.driver_instance.send_command(self.serialize_command(command))

self.driver_instance.send_command_without_return(self.serialize_command(command))


# Resets the simulation state back to the beginning: no commands + landed
def reset(self):
"""
Expand Down
Loading