Date post: | 26-Jan-2015 |
Category: |
Technology |
Upload: | steven-beeckman |
View: | 114 times |
Download: | 3 times |
node.js & AR.Drone#njugbe meetup 3 - 13 March 2013
@stevenbeeckman
AR.Drone?
wifi controlled quadricopter with 2
camera’s!
node.js packages
ar-dronehttps://npmjs.org/package/ar-drone
ardronehttps://npmjs.org/package/ardrone
ardrone-webhttps://npmjs.org/package/ardrone-web
best maintained
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
MacBook
node fly.js node rtdashboard.jshttp
AR.Drone’s wifi networkbrowser - http://localhost:3000/
socket.io
fly.jsvar arDrone = require('ar-drone'); //RTFM for flying commands etcvar http = require('http');var client = arDrone.createClient();var options = {host: '127.0.0.1’, port: 3000, path: '/api/raw', method: 'POST'};var counter = 0; // naïve sampling counterclient.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 dashboardclient.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.jsvar 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.jsfunction 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.jsfunction 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.jsexports.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 namespacevar throttleHorizontal_data = new Array(); // array for the horizontal throttle datavar 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 itselfthrottleHorizontal_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 ithttps://github.com/stevenbeeckman/ardrone-controller
https://github.com/stevenbeeckman/ardrone-dashboard
Demo time
Way ahead
Access the videostreamsFront & bottom camera
Use OpenCV for computer vision
Node.js on the AR.Dronehttps://gist.github.com/maxogden/4152815
autonomous AR.Drone!interface with Arduino
Questions?My name is @stevenbeeckman.
Thanks for listening.