Saturday, February 22, 2014

Multiple AR.Drones from a single computer using the ardrone_autonomy ROS package

I'm currently working with a small graduate student team on centrally controlling a group of Parrot AR.Drone quadcopters or "quads" using the Robot Operating System (ROS), a software middleware for connecting robotic system components. There is a ROS driver called ardrone_autonomy for the AR.Drone, which wraps the AR.Drone SDK and exposes its controls and feedback to ROS' publish-subscribe model. This works well for controlling a single quad from a single computer. However, we ran into issues when trying to control multiple quads from a single computer, seemingly because the underlying SDK was designed for single-computer to single-quad use.

A lot of other groups have posted interesting solutions for controlling multiple quads. The AR.Drones use 802.11 wireless networks for control, feedback, and video; some solutions we examined focus on reconfiguring all quads to use a common network and then use multiple computers to control them. Others go a step further and allow control of multiple quads from a single computer. However, no solution that we've encountered allows both control and feedback/video of multiple quads from a single computer (at least, not using stock AR.Drone firmware).

One of the students, Brenton Campbell, and I set out to fly, crash, and break multiple quads at once hack some code and develop a solution for full bi-directional communications. This post documents and provides all code necessary for multi-quad control, feedback (navdata), and video from a single computer. It uses a modified version of the ardrone_autonomy ROS package and its included version of the AR.Drone SDK (v2.0.1, internally termed ARDroneLib), combined with some network hackery. Further, our solution requires no manual or permanent changes to the quad; it is entirely coordinated from the computer and can "automatically" reconfigure and utilize out-of-the-box quads. It has only been tested on v1.0 quads, but as far as I've read, it should apply equally to v2.0 quads.

I should note that this solution draws on insights gleaned from the above-referenced posts, and also:

The solution described herein might void warranties, violate license agreements, and (though exceedingly unlikely) render your AR.Drone unusable. My description assumes a fair amount of familiarity with Linux, comfort using the command line, and a bit of C and Python programming skill. Use this information and code at your own risk!

The solution can be broken into three steps, each of which I discuss below:
  1. Reconfiguring network settings on individual AR.Drones
  2. Remapping UDP ports to unique ground-side port numbers
  3. Modifying the AR.Drone SDK and ardrone_autonomy package to customize UDP ports
Update 3/26/2014 - Following a discussion thread on the ardrone_autonomy GitHub project, another contributor, Kenneth Bogert, discovered an alternative to steps 2 and 3 above. It turns out that when the computer sends UDP probe datagrams to a quad's video and navdata ports, the quad captures the source ports and uses them as its destination ports. By modifying the SDK to use ephemeral client-side ports, both video and navdata work and no port remapping is necessary. Kenneth posted a pull request with his modifications here.

Update 11/20/2014 - An updated version of Kenneth's pull request was merged into ardrone_autonomy on the 9th.

Monday, December 16, 2013

Weekend Diversion - an HTTP interface to mplayer using Python and Flask

Author's note: I'm currently collaborating with a friend over at AutisTech.org on finding or creating a video playback solution with very simple remote control for his daughter. This post is based on some early exploration I did toward this. Ultimately, we'd like to have a simplified mobile device interface to XBMC or something similar. If you are interested in contributing your expertise, please check out the projects page over at his website!

Update 1/25/2014: More details on the video player concept have been posted here.

Once again I was looking to do some coding for fun over the weekend. A friend and I had recently discussed ways to control media playback on a Linux-based system from a remote device. Looking for an excuse to practice writing Python code (I come from a Perl background) and to learn Flask (a lightweight in-application webserver for Python), I decided to roll my own simple HTTP interface to control mplayer, a popular, command-line media player for Linux.

Read on for the details of my approach and some mildly-questionable Python code. (If you're looking for more refined implementations of this idea with varying feature sets, check out here and here.)

Wednesday, November 20, 2013

Forcing a higher MSS to improve TCP performance on links with asymmetric MTUs

An interesting networking problem was recently posed to me. As context, suppose two computers, call them “A” and “B”, are networked together such that the physical link from A to B is separate from the physical link from B to A. The A-B link has a link-layer Maximum Transmission Unit (MTU) of, say, 1500 bytes (normal for Ethernet). However, the B-A link has a much smaller MTU, say 500 bytes:

-----              -----
|   |-- 1500 MTU ->|   |
| A |              | B |
|   |<-- 500 MTU --|   |
-----              -----

Here’s the problem: when transferring large volumes of data from A to B over a TCP connection, the transfer rate is much slower than what is expected given a 100Mbps link speed from A to B. The question is why, and what can be done about it? Read on for the discussion, and a proposed and tested solution.

Sunday, November 17, 2013

Weekend Diversion - Conway's Game of Life using ncurses

Seeking a bit of for-fun programming for the weekend and an excuse to refresh myself on the basics of ncurses, I decided to write my own small implementation of Conway's Game of Life. If you've never had a chance to become acquainted with this "game," I highly recommend taking the time to read about it or to play with one of the many implementations (including mine below if you'd like). It is a great study in how a small number of simple rules can lead to some amazing emergent phenomena when applied at a larger scale.

In a nutshell, the "game" (I use quotes because it is not a game in the traditional, competitive sense, although some people have created variants that allow two players to "compete") is staged in a 2-D world that one can think of as a grid of "cells." The life of each cell depends upon its neighbors; cells like some company, but not too much! For each "step" in the game, each cell's life is evaluated based on the number of living neighbors it has (above and below, left and right, and the four diagonals). The life of the cell following the step is determined by three rules:
  1. If the number of living neighbors is exactly 2, the cell remains alive or dead (whichever it was previously).
  2. If the number of living neighbors is exactly 3, the cell is "born" (or remains alive).
  3. Otherwise, the cell "dies" (or remains dead).
Pretty simple, right? So simple that an entire implementation with an ncurses interface takes less than 200 lines of code (as sloccount reports; minus whitespace and comments). So, let's get to it!

Saturday, September 14, 2013

Modifying software for fun and music, part 2

Author's Note: Whew! I've been on a long hiatus from posting. This post in particular is a long-overdue follow-up to an experiment where I documented my foray into deciphering and modifying a particular piece of open source software as I went along. Unlike that post, the following is compiled from notes made during the process. Enjoy!

Last time we left off with having added the ability to specify multiple songs from the command line. However, the last song in the list was getting truncated several seconds early. Also, m4a (AAC) files were not playing at all. Finally, after further testing it came to light that mono MP3 files were not playing correctly. Read on for my notes on treating each of these issues, and finally the output of diff on the original code and my changes, if you're so inclined to use them!

Wednesday, May 16, 2012

Using ld to manually build an executable

Author's note: further work on raop_play has been sporadic due to other happenings, though I have some new results addressing song truncation, playing other file types, and playback of mono files. I will (hopefully) get this posted shortly!

About three days ago, I finally bit the bullet and decided to give Kubuntu a try. While I have been using Debian happily for the past few years, I am discovering an increasing desire for simplicity and to have things work out of the box, rather than spend large amounts of time configuring everything from scratch. One of my side-goals was to try out the Pulse audio system to see if I could successfully integrate my Linux sound with my Airport Express. As it turns out, this does not work as smoothly as I'd anticipated. While getting it up and running was mostly straightforward, there are some playback issues that appear to be unresolved within the community. Perhaps a project for another day, but with a freshly-installed OS I wanted to play some tunes NOW!

So instead I go back to my previous work on raop_play and simply recompile with the new libraries on this installation... but I run into a problem. For some reason, the OpenSSL library wasn't being found by the linker, giving me the following error:

...
gcc -o raop_play raop_play.o raop_client.o rtsp_client.o aexcl_lib.o base64.o aes.o m4a_stream.o audio_stream.o wav_stream.o mp3_stream.o flac_stream.o ogg_stream.o aac_stream.o pls_stream.o pcm_stream.o -lssl -lsamplerate -lid3tag
raop_client.o: In function `rsa_encrypt':
raop_client.c:(.text+0x131): undefined reference to `RSA_new'
raop_client.c:(.text+0x175): undefined reference to `BN_bin2bn'
raop_client.c:(.text+0x1b3): undefined reference to `BN_bin2bn'
...

... and so on for another dozen lines. Actually, it was an even-worse problem at first, until I discovered that gcc is sensitive to the ordering of files and library references, and fixed the Makefile to move all "-l" options to the end of the command, as shown above.

I racked my brain, wondering if I had an incompatible version of the OpenSSL library, or if the system's library path was broken, but every workaround I tried yielded the same result.

Wednesday, March 21, 2012

"It's all just bit-flipping and timing"

This was stated to me by a coworker at my first job. It was at a formative moment in my life: getting ready to start college, working at a then-startup doing data entry and computer repair, and learning to program in C++ in my spare time. My coworker said this while I was shoulder-surfing his efforts to program an embedded computer to display text on a small serial-driven LCD screen. He was effectively instructing the computer to set particular bits on the display (bit-flipping) at specific times (timing). It's all just bit-flipping and timing...

Four years later I was taking CSSE 380, Organization of Programming Languages, learning Scheme and writing continuation-passing interpreters with garbage collection and static type checking. Which is to say, bending my mind on a nightly basis. (I recall discussing with my roommates - also in the class - starting a band called Dr. Scheme and writing heavy metal songs that always ended in "cond, lambda, define!" But I digress...)

Looking back on it now, that course wasn't really about learning Scheme, nor was it about coding garbage collection or type checking. It was about arriving at the fundamental realization that under the hood of the language compilers and interpreters we use on a daily basis lives an amazing paradox. Each one is a transformation on some language, turning it into yet another language (e.g., assembler) or into a sequence of operations performed immediately within that very transformation, producing a final result. And what is amazing is that these transformations are often built using the very language they transform! One can craft a Scheme interpreter in Scheme, using Scheme lists to represent Scheme code. Likewise (with a bit of parsing), one can write a C compiler in C. It was recognizing the duality of a program represented as code (text) and textual strings that can be manipulated in a programmatic way that opened my eyes to a part of what really goes on inside a computer.

Suddenly, the magic of a programming language, hand-crafted by wizards in underground dwellings, was transmuted into a very real, if not easy, undertaking that even us wee burgeoning Computer Scientists could approach. All of the abstractions that we took for granted when hitting the Compile button boiled down into the "bit-flipping and timing" of compilation and interpretation. I would call this a transformative moment in my Computer Science education, where my understanding of the discipline underwent a fundamental shift. And over time, I have come to recognize other moments in a CS education that bear this same mark.