This project shows you how to use the built-in tilt sensors of the MacBook to control a Roomba. Since Perl hasn’t been used to program a Roomba yet, it seemed like a fun task to show that it could be done (and pretty easily too).
You only need two bits of software not already on your Mac:
Download them both, put them both in your PATH and try it out. But first you might want to read on to see how it works.
Apple MacBooks have integrated 3-axis accelerometer called the Sudden Motion Sensor (SMS, previously Apple Motion Sensor, AMS). Normally this is used to detect when the laptop is dropped so it can shut down the hard drive to prevent damage. But the SMS can be accessed programmatically and used to measure tilt.
Amit Singh has a wonderful book out called Mac OS X Internals where he goes into great detail about the SMS. He’s also created several programs that use the accelerometer data. The one used in this project is the command-line program AMSTracker.
When AMSTracker is run, it outputs three columns of numbers, one for each axis of accelerometer data. AMSTracker can also be run in a “scrolling” mode where it outputs new data periodically. This is the mode used in this project. The output then looks like:
laptop% AMSTracker -s -u 1 AMS hardware present and initialized -7 -1 0 -6 -1 1 -6 -1 2 -5 -1 1 -6 -1 2 -5 -1 2 -7 -1 0
It’s easy for Perl to parse such stuff. The regex to get those three values is “
Controlling the Roomba from Perl
On Mac OS X, like other Unixes, serial ports appear as ordinary files. And just like any other files, they can be read from and written to. Perl’s whole reason for being is reading and writing files so it’ll be a snap for it.
But wait, how do we set the serial parameters like baud rate and so on? Files don’t have these kinds of attributes. That’s where the program “
stty” comes in.
Stty sets the various options on a “terminal device” (aka serial port). Figuring out the magic stty incantation needed to let the serial port work with the Roomba is laborious so will just be presented here:
demo% export PORT=/dev/tty.RooTooth-COM0-1 demo% stty -f $PORT 57600 raw -parenb -parodd cs8 -hupcl -cstopb clocal
This command is issued by the “
roomba_init()” function in the Perl script before it opens the “file” that is the serial port.
Once the port/file is open, functions like “
roomba_drive()” and “
roomba_stop()“in the script just print to the open file descriptor. If you wanted to read the sensors, you would read from the file descriptor.
Converting Tilt Data to Roomba Commands
The tilt data is essentially two numbers that vary between -127 and +127. One number corresponds to back-n-forth tilt motion of the laptop while the other corresponds to left-right tilt. A simple linear function can convert between the tilt data space and the velocity/radius space the Roomba expects. This is the ‘proportional’ mode of operation, where the degree of tilt affects the speed or angle of the Roomba.
The code implements two special cases of proportional control to make it easier to use:
- if laptop is flat (in the deadzone), send “
- if only tilted left-right, send “
roomba_spinleft()” or “
Another mode called “fourway” or “d-pad” mode turns the tilt data into a four-state switch: forward, backward, spinleft, spinright, sort of like an old Nintendo D-pad controller. It does this by defining a “deadzone” in the center and only if you tilt in one direct enough to escape this zone does it count as a valid motion. In this mode, controlling the Roomba is easier but you can’t get as precise of positioning.
roomba-tilt.pl, the only required argument is the path to the serial port the Roomba is connected to. This serial can either be a RS-232 serial tether or a Bluetooth adapter as shown in the video. The full usage is:
laptop% ./roomba-tilt.pl Usage: roomba-tilt.pl -p <serial_port> [options] Options: -p <serial_port> Serial port of Roomba (e.g. "/dev/tty.RooTooth") -fourway Change to D-pad mode instead of normal proportional -fullmode Turn off Roomba safety mode (watch out!) -d|-deadzone <n> Set center deadzone (normally 15) -u|-update_rate <f> Set update rate in seconds (normally 0.25) -tracker <path> Set AMSTracker path (if not in PATH) -v|-verbose Output internal info -h|-help This screen
Running it in verbose mode is interesting because you can see the data sent from AMSTracker and the drive commands sent to the Roomba.
laptop% ./roomba-tilt.pl -p /dev/tty.RooTooth-COM0-2 -v roomba_init: /dev/tty.RooTooth-COM0-2 roomba-tilt.pl running , press ctrl-c to exit x:-2, y:-1, z:28 roomba_drive: -140,-1250 x:13, y:0, z:25 roomba_drive: -125,1140 x:39, y:0, z:22 roomba_drive: -110,880 x:59, y:0, z:21 roomba_drive: -105,680 x:23, y:0, z:40 roomba_drive: -200,1040 x:-13, y:-1, z:20 roomba_drive: -100,-1140 x:-17, y:-1, z:17 roomba_drive: -85,-1100 x:-20, y:-1, z:-5 roomba_drive: 110,65535
Issues and Improvements
As you play with AMSTracker or roomba-tilt.pl, you’ll see how the tilt data will flop from +/-127 to -/+127. In roomba-tilt.pl, this causes the Roomba to go from making hard left to a hard right (or vice versa). The code currently doesn’t attempt to smooth the data to eliminate these jumps. You could write a filter that does a little analysis on the data and if it does something unexpected (like the jump), it calms the data down before sending it to the Roomba.22 comments