Вы находитесь на странице: 1из 26

node.js & AR.

Drone
#njugbe meetup 3 - 13 March 2013
@stevenbeeckman
AR.Drone?

2
h s!
i t
w ra
wifi controlled quadricopter e
am
node.js packages
bes
ar-drone tm
aint
https://npmjs.org/package/ar-drone aine
d
ardrone
https://npmjs.org/package/ardrone
ardrone-web
https://npmjs.org/package/ardrone-web
hello world
var arDrone = require('ar-drone');
var client = arDrone.createClient();

client.animateLeds('blinkGreenRed', 5, 10);

client.on('navdata', console.log);
Real-time Dashboard?
In the browser?
Real-time Dashboard
Express.js for the server
Socket.io for push to the browser (web sockets!)
Client-side:
jQuery Knob
Rickshaw.js
Schematics

AR.Drones wifi network


browser - http://localhost:3000/

socket.io

http
node fly.js node rtdashboard.js

MacBook
fly.js
var arDrone = require('ar-drone'); //RTFM for flying commands etc
var http = require('http');
var client = arDrone.createClient();
var options = {host: '127.0.0.1, port: 3000, path: '/api/raw', method: 'POST'};
var counter = 0; // nave sampling counter
client.config('general:navdata_demo', 'FALSE'); // get all the data from the sensors

client.takeoff();
client
.after(3000, function() {
this.down(0.1);
})
.after(1000, function(){
this.stop();
this.land();
});
fly.js - logging to dashboard
client.on('navdata', function(data){ // on receiving navdata, send data to dashboard
counter = counter + 1;
if(counter > 50){ // only send every 50th data header
counter = 0;
var raw_data_header = new Object();

if(data.rawMeasures && data.demo && data.pwm){ // data not always contains demo & pwm
raw_data_header = {
header: {
time: data.time
, sequenceNumber: data.sequenceNumber
, flying: data.droneState.flying
, batteryMilliVolt: data.rawMeasures.batteryMilliVolt
, altitude: data.demo.altitude
, velocity: {x: data.demo.xVelocity
, y: data.demo.yVelocity
, z: data.demo.zVelocity}
, throttle: {forward: data.pwm.gazFeedForward
, height: data.pwm.gazAltitude}
}
};
}else{
fly.js - logging to dashboard
raw_data_header = {
header: {
time: data.time
, sequenceNumber: data.sequenceNumber
, flying: data.droneState.flying
, batteryMilliVolt: 0
, altitude: 0
, velocity: {x: 0
, y: 0
, z: 0}
, throttle: {forward: 0
, height: 0}
}
};
}
fly.js - logging to dashboard
var data_to_be_sent = JSON.stringify(raw_data_header);
var headers = {
'Content-Type': 'application/json'
, 'Content-Length': data_to_be_sent.length
};
options.headers = headers;

var req = http.request(options, function(res){


});

req.on('error', function(e){
// per http://nodejs.org/api/http.html#http_http_request_options_callback
console.log("Problem with request: " + e.message);
})

req.write(data_to_be_sent);
req.end();
}
});
rtdashboard.js
var app = express();
setupApp();

function setupApp(){
...
db = mongoose.createConnection(app.set('db-uri'));
db.on('error', console.error.bind(console, 'Connection error:'));

db.once('open', function(){
console.log("Connected to database");
app.use(app.router);
setupRoutes();
startServer();
});
}
rtdashboard.js
function setupRoutes(){
app.get('/', routes.index); // contains the dashboard

app.post('/api/raw', addDb, addAltitude, addSpeed, addHeading,


addThrottleVertical, addThrottleHorizontal, addBattery, routes.raw);
// receives some navdata from fly.js

app.get('*', function(req, res){


console.log("Page not found: " + req.originalUrl);
res.render('404');
});
}

function addDb(req, res, next){


req.db = db; // contains the database
next();
}

function addAltitude(req, res, next){


req.altitude = altitude; // contains a socket.io namespace object, see startServer()
next();
}
rtdashboard.js
function startServer(){
server = http.createServer(app);
io = io.listen(server);

server.listen(app.get('port'), function(){
console.log("Express server started.");
});

// each sensor gets its own socket.io namespace


altitude = io.of('/altitude');
speed = io.of('/speed');
heading = io.of('/heading');
throttle_vertical = io.of('/throttle_vertical');
throttle_horizontal = io.of('/throttle_horizontal');
battery = io.of('/battery');
}
routes/index.js
exports.index = function(req, res){
res.render('index', { });
};

exports.raw = function(req, res){


if(req.body.header){
var header = new Object();
header.rawData = req.body.header;

// send data to dashboard on socket.io


req.altitude.emit('altitude', {value: header.rawData.altitude});
req.throttle_vertical.emit('throttle', {value: header.rawData.throttle.height});
req.throttle_horizontal.emit('throttle', {value: header.rawData.throttle.forward});
req.battery.emit('battery', {value: header.rawData.batteryMilliVolt})

res.json({message: "Success"});
}else{
res.json({message: "No header received."});
}
}
views/index.ejs
<!DOCTYPE HTML>
<html>
<head>
<title>Real-Time Dashboard</title>
<link rel='stylesheet' href='/bootstrap/css/bootstrap.min.css' />
<link rel="stylesheet" href="/css/rickshaw.min.css"/>
<script src="/js/jquery-1.9.1.min.js"></script>
<script src="/js/jquery.knob.js"></script>
<script src="/js/d3.v2.js"></script>
<script src="/js/rickshaw.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
... <!-- see second next slides -->
</script>
</head>
<body>
... <!-- see next slide -->
</body>
</html>
views/index.ejs
<body>
...
<div class="span4">
<h3>Horizontal Throttle</h3>
<div class="span2">
<input type="text" id="throttleHorizontal" data-fgColor="#66cc66" data-min="-400"
data-max="400" data-cursor=true data-angleOffset=-180 data-width="70" value="0"
data-readOnly=true> <!-- jQuery Knob -->
</div>
<div style="float: left;" id="throttleHorizontal_chart"> <!-- rickshaw graph -->
</div>
</div>
...
</body>
views/index.ejs
<script>
$(document).ready(function(){
$("#throttle").knob();
$("#throttleHorizontal").knob();
});

var throttleHorizontal = io.connect('http://localhost/throttle_horizontal'); // connect to socket.io namespace


var throttleHorizontal_data = new Array(); // array for the horizontal throttle data
var throttleHorizontal_graph;

// see next slide


...
</script>
views/index.ejs
<script>
...
throttleHorizontal.on('throttle', function(data){
$("#throttleHorizontal").val(data.value);
$("#throttleHorizontal").trigger("change"); // trigger the knob to redraw itself
throttleHorizontal_data.push({x: (new Date()).getTime(), y: parseInt(data.value)});

if(!throttleHorizontal_graph){
//console.log("Altitude graph doesn't yet exist, drawing it for the first and only time.");
throttleHorizontal_graph = new Rickshaw.Graph( {
element: document.querySelector("#throttleHorizontal_chart"),
width: 80,
height: 60,
renderer: "line",
interpolation: "step-after",
series: [{
color: '#66cc66',
data: throttleHorizontal_data
}]
});
throttleHorizontal_graph.render();
}else{
views/index.ejs
<script>
...
throttleHorizontal.on('throttle', function(data){
...
}else{
//Throttle graph already exists so just update the data and rerender.
throttleHorizontal_graph.series[0].data = throttleHorizontal_data;
throttleHorizontal_graph.render();
}
});
...
</script>
Fork it
https://github.com/stevenbeeckman/ardrone-controller
https://github.com/stevenbeeckman/ardrone-dashboard
Demo time
Way ahead
Access the videostreams
Front & bottom camera

Use OpenCV for computer vision


Node.js on the AR.Drone
https://gist.github.com/maxogden/4152815

autonomous AR.Drone!
interface with Arduino
Questions?
My name is @stevenbeeckman.
Thanks for listening.

Вам также может понравиться