Skip to content

Commit fadb293

Browse files
committed
Merge pull request #15 from mrpollo/sample_demos
Sample demos
2 parents 6ab20f7 + 7b675c2 commit fadb293

18 files changed

+508
-3
lines changed

droneapi/module/api.py

+9
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,15 @@ def wait_valid(self):
6363
while (self.__wp.wp_op is not None) and not self.__module.api.exit:
6464
time.sleep(0.200)
6565

66+
def takeoff(self, alt=None):
67+
if alt is not None:
68+
altitude = float(alt)
69+
self.__module.master.mav.command_long_send(self.__module.target_system,
70+
self.__module.target_component,
71+
mavutil.mavlink.MAV_CMD_NAV_TAKEOFF,
72+
0, 0, 0, 0, 0, 0, 0,
73+
altitude)
74+
6675
def goto(self, l):
6776
if l.is_relative:
6877
frame = mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT
+197
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import os, os.path
2+
import simplejson
3+
4+
from pymavlink import mavutil
5+
import droneapi.lib
6+
from droneapi.lib import VehicleMode, Location
7+
8+
import cherrypy
9+
from cherrypy.process import wspbus, plugins
10+
from jinja2 import Environment, FileSystemLoader
11+
12+
cherrypy_conf = {
13+
'/': {
14+
'tools.sessions.on': True,
15+
'tools.staticdir.root': local_path
16+
},
17+
'/static': {
18+
'tools.staticdir.on': True,
19+
'tools.staticdir.dir': './html/assets'
20+
}
21+
}
22+
23+
class Drone(object):
24+
def __init__(self, home_coords, server_enabled=True):
25+
self.api = local_connect()
26+
self.gps_lock = False
27+
self.altitude = 30.0
28+
self.vehicle = self.api.get_vehicles()[0]
29+
self.commands = self.vehicle.commands
30+
self.current_coords = []
31+
self.home_coords = home_coords
32+
self.webserver_enabled = server_enabled
33+
self._log("DroneDelivery Start")
34+
35+
# Register observers
36+
self.vehicle.add_attribute_observer('armed', self.armed_callback)
37+
self.vehicle.add_attribute_observer('location', self.location_callback)
38+
#self.vehicle.add_attribute_observer('mode', self.mode_callback)
39+
self.vehicle.add_attribute_observer('gps_0', self.gps_callback)
40+
41+
self._log("Waiting for GPS Lock")
42+
43+
def takeoff(self, alt):
44+
self._log("Taking off")
45+
self.commands.takeoff(30.0)
46+
self.vehicle.flush()
47+
48+
def arm(self, toggle=True):
49+
if toggle:
50+
self._log("Arming")
51+
else:
52+
self._log("Disarming")
53+
self.vehicle.armed = True
54+
self.vehicle.flush()
55+
56+
def run(self):
57+
self._log('Running initial boot sequence')
58+
self.arm()
59+
self.takeoff()
60+
self.change_mode('GUIDED')
61+
62+
if self.webserver_enabled is True:
63+
self._run_server()
64+
65+
def _run_server():
66+
# Start web server if enabled
67+
cherrypy.tree.mount(DroneDelivery(self), '/', config=cherrypy_conf)
68+
69+
cherrypy.config.update({
70+
'server.socket_port': 8080,
71+
'server.socket_host': '0.0.0.0',
72+
'log.screen': None
73+
})
74+
75+
cherrypy.engine.start()
76+
77+
def change_mode(self, mode):
78+
self._log("Mode: {0}".format(mode))
79+
80+
self.vehicle.mode = VehicleMode(mode)
81+
self.vehicle.flush()
82+
83+
def goto(self, location, relative=None):
84+
self._log("Goto: {0}, {1}".format(location, self.altitude))
85+
86+
self.commands.goto(
87+
Location(
88+
float(location[0]), float(location[1]),
89+
float(self.altitude),
90+
is_relative=relative
91+
)
92+
)
93+
self.vehicle.flush()
94+
95+
def get_location(self):
96+
return [self.current_location.lat, self.current_location.lon]
97+
98+
def location_callback(self, location):
99+
location = self.vehicle.location
100+
101+
if location.alt is not None:
102+
self.altitude = location.alt
103+
104+
self.current_location = location
105+
106+
def armed_callback(self, armed):
107+
self._log("DroneDelivery Armed Callback")
108+
self.vehicle.remove_attribute_observer('armed', self.armed_callback)
109+
110+
def mode_callback(self, mode):
111+
self._log("Mode: {0}".format(self.vehicle.mode))
112+
113+
def gps_callback(self, gps):
114+
self._log("GPS: {0}".format(self.vehicle.gps_0))
115+
if self.gps_lock is False:
116+
self.gps_lock = True
117+
self.vehicle.remove_attribute_observer('gps_0', self.gps_callback)
118+
self.run()
119+
120+
def _log(self, message):
121+
print "[DEBUG]: {0}".format(message)
122+
123+
class Templates:
124+
def __init__(self, home_coords):
125+
self.home_coords = home_coords
126+
self.options = self.get_options()
127+
self.environment = Environment(loader=FileSystemLoader( local_path + '/html'))
128+
129+
def get_options(self):
130+
return {
131+
'width': 670,
132+
'height': 470,
133+
'zoom': 13,
134+
'format': 'png',
135+
'access_token': 'pk.eyJ1IjoibXJwb2xsbyIsImEiOiJtUG0tRk9BIn0.AqAiefUV9fFYRo-w0jFR1Q',
136+
'mapid': 'mrpollo.kfbnjbl0',
137+
'home_coords': self.home_coords,
138+
'menu': [
139+
{'name': 'Home', 'location': '/'},
140+
{'name': 'Track', 'location': '/track'},
141+
{'name': 'Command', 'location': '/command'}
142+
],
143+
'current_url': '/',
144+
'json': ''
145+
}
146+
147+
def index(self):
148+
self.options = self.get_options()
149+
self.options['current_url'] = '/'
150+
return self.get_template('index')
151+
152+
def track(self, current_coords):
153+
self.options = self.get_options()
154+
self.options['current_url'] = '/track'
155+
self.options['current_coords'] = current_coords
156+
self.options['json'] = simplejson.dumps(self.options)
157+
return self.get_template('track')
158+
159+
def command(self, current_coords):
160+
self.options = self.get_options()
161+
self.options['current_url'] = '/command'
162+
self.options['current_coords'] = current_coords
163+
return self.get_template('command')
164+
165+
def get_template(self, file_name):
166+
template = self.environment.get_template( file_name + '.html')
167+
return template.render(options=self.options)
168+
169+
class DroneDelivery(object):
170+
def __init__(self, drone):
171+
self.drone = drone
172+
self.templates = Templates(self.drone.home_coords)
173+
174+
@cherrypy.expose
175+
def index(self):
176+
return self.templates.index()
177+
178+
@cherrypy.expose
179+
def command(self):
180+
return self.templates.command(self.drone.get_location())
181+
182+
@cherrypy.expose
183+
@cherrypy.tools.json_out()
184+
def vehicle(self):
185+
return dict(position=self.drone.get_location())
186+
187+
@cherrypy.expose
188+
def track(self, lat=None, lon=None):
189+
# Process POST request from Command
190+
# Sending MAVLink packet with goto instructions
191+
if(lat is not None and lon is not None):
192+
self.drone.goto([lat, lon], True)
193+
194+
return self.templates.track(self.drone.get_location())
195+
196+
Drone([32.5738, -117.0068])
197+
cherrypy.engine.block()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
body {
2+
padding-top: 20px;
3+
padding-bottom: 20px;
4+
}
5+
6+
.form-inline {
7+
margin-top: 20px;
8+
}
9+
10+
#map, #staticMap {
11+
width: 670px;
12+
height: 470px;
13+
}
14+
15+
.header,
16+
.marketing,
17+
.footer {
18+
padding-right: 15px;
19+
padding-left: 15px;
20+
}
21+
22+
.header {
23+
border-bottom: 1px solid #e5e5e5;
24+
}
25+
26+
.header h3 {
27+
padding-bottom: 19px;
28+
margin-top: 0;
29+
margin-bottom: 0;
30+
line-height: 40px;
31+
}
32+
33+
.footer {
34+
padding-top: 19px;
35+
color: #777;
36+
border-top: 1px solid #e5e5e5;
37+
}
38+
39+
@media (min-width: 768px) {
40+
.container {
41+
max-width: 730px;
42+
}
43+
}
44+
.container-narrow > hr {
45+
margin: 30px 0;
46+
}
47+
48+
.jumbotron {
49+
text-align: center;
50+
border-bottom: 1px solid #e5e5e5;
51+
}
52+
53+
.jumbotron .btn {
54+
padding: 14px 24px;
55+
font-size: 21px;
56+
}
57+
58+
.marketing {
59+
margin: 40px 0;
60+
}
61+
.marketing p + h4 {
62+
margin-top: 28px;
63+
}
64+
65+
@media screen and (min-width: 768px) {
66+
.header,
67+
.marketing,
68+
.footer {
69+
padding-right: 0;
70+
padding-left: 0;
71+
}
72+
.header {
73+
margin-bottom: 30px;
74+
}
75+
.jumbotron {
76+
border-bottom: 0;
77+
}
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
2+
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
3+
<script src='https://api.tiles.mapbox.com/mapbox.js/v2.1.4/mapbox.js'></script>
+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<!DOCTYPE HTML>
2+
<html>
3+
<head>
4+
{% include 'head.html' %}
5+
</head>
6+
<body>
7+
<div class='container'>
8+
{% import 'header.html' as header %}
9+
<div class='header'>
10+
<nav>
11+
{{ header.navigation(options.menu, options.current_url, 'nav nav-pills pull-right') }}
12+
</nav>
13+
<h3 class='text-muted'>DroneAPI Demos {{options.current_url}} </h3>
14+
</div>
15+
<div class='row marketing'>
16+
<div class='col-lg-12'>
17+
<div id="map"></div>
18+
</div>
19+
<div class='col-lg-12'>
20+
<form class="form-inline" action="/track" method="POST">
21+
<div class='form-group'>
22+
<input class='form-control' type="text" name="lat" id="lat" placeholder="Latitude" value="" />
23+
</div>
24+
<div class='form-group'>
25+
<input class='form-control' type="text" name="lon" id="lon" placeholder="Longitude" value="" />
26+
</div>
27+
<button type="submit" class="btn btn-default">Go</button>
28+
</form>
29+
</div>
30+
</div>
31+
<footer class='footer'>
32+
<p>&copy; 3D Robotics Inc.</p>
33+
</div>
34+
</div>
35+
{% include 'bottom-scripts.html' %}
36+
<script type="text/javascript" charset="utf-8">
37+
L.mapbox.accessToken = '{{options.access_token}}';
38+
var currentCoords = [{{ options.current_coords[0] }}, {{ options.current_coords[1] }}];
39+
var homeCoords = [{{ options.home_coords[0] }}, {{ options.home_coords[1] }}];
40+
var homeMarker = L.marker(homeCoords, {
41+
icon: L.mapbox.marker.icon({
42+
'marker-size': 'small',
43+
'marker-symbol': 'building',
44+
'marker-color': '#fa0'
45+
})
46+
});
47+
var vehicleMarker = function(location){
48+
return L.marker(location, {
49+
'marker-size': 'large',
50+
'marker-symbol': 'heliport',
51+
'marker-color': '#fa0'
52+
})
53+
}
54+
var map = L.mapbox.map('map', '{{options.mapid}}');
55+
var userMarker = L.marker([0, 0], {
56+
icon: L.mapbox.marker.icon({
57+
'marker-color': '#f86767'
58+
}),
59+
draggable: true
60+
});
61+
62+
homeMarker.addTo(map);
63+
vehicleMarker(currentCoords).addTo(map);
64+
userMarker.addTo(map);
65+
map.setView(homeCoords, {{ options.zoom }});
66+
map.on('click', function(event) {
67+
userMarker.setLatLng(event.latlng)
68+
$('#lat').val(event.latlng.lat);
69+
$('#lon').val(event.latlng.lng);
70+
});
71+
</script>
72+
</body>
73+
</html>

example/drone_delivery/html/head.html

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<meta charset="utf-8">
2+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
3+
<meta name="viewport" content="width=device-width, initial-scale=1">
4+
<title>Drone Delivery App</title>
5+
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet" />
6+
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css" rel="stylesheet" />
7+
<link href='https://api.tiles.mapbox.com/mapbox.js/v2.1.4/mapbox.css' rel='stylesheet' />
8+
<link href="/static/styles.css" rel="stylesheet"/>
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{% macro list_item(name, location='#', active=False) -%}
2+
<li class='{{ 'active' if active else '' }}'>
3+
<a href="{{location}}">{{name}}</a>
4+
</li>
5+
{%- endmacro %}
6+
7+
{% macro navigation(list, current_url, class='') -%}
8+
<ul class='{{class}}'>
9+
{%- for item in list %}
10+
{{ list_item(item.name, item.location, (item.location == current_url)) }}
11+
{%- endfor %}
12+
</ul>
13+
{%- endmacro %}

0 commit comments

Comments
 (0)