The last time Hackerfall tried to access this page, it returned a not found error. A cached version of the page is below, or click here to continue anyway

SR:ping stick

ping stick

the ping stick demonstrates some ideas implicit in embodied cognition, the philosophical idea that, in essence, what we call 'mind' is deeply and irretrievably embedded in, a part of, the body. it's a really interesting subject and a useful way to think about thinking and the body and interface (computer or otherwise) . the wikipedia page on embodied cognition is a decent introduction.

the ping stick couples your sense of touch to your mind's "visual" comprehension of space. put simply, you can feel the physical environment around you with your fingertip, and walk around a room or other space with your eyes closed. with essentially zero training, just holding the thing in one hand and gently waving it in front of you as you walk, you feel/sense/see walls, doorways, furniture, etc around you in a way that immediately makes sense. practice brings much subtlety to its use.

the ping torch is directly inspired by Adam Spiers' haptic torch (University of Reading, UK, 2004), encountered in Professor Simon Penny's reader for his Embodied Cognition class, University of California, Irvine, 2009. mine appears to be simpler, and much smaller (the inexorable stampede of technology).

usage is very simple; you hold it in one hand and point it like a flashlight in the direction you are walking, more or less gently swinging it back and forth in front of you, your finger resting on a small round button on the bottom. the sole purpose of the ping stick is to vibrate the small touch-button with an intensity proportional to distance to an object such as a wall or a chair, with things closer vibrating more intensely.

the vibrating button is the sole output; no pixels, LEDs or sounds. there is a power switch (which i would like to eliminate) and a sonar sensor in front, and no adjustments of any kind. software within the device translates sonar range (distance) data from the sonar device, into vibration intensity in the tiny motor. technical details are below.

the result is a fairly immediate mapping of what would seem to be utterly unrelated cognitive events; arbitrary sensation in a fingertip, and visualization of space. this is apparently due to the spectacular redundancy and innate ability of the body/mind to analogize sensory data, analogous (to me) as a form of useful synaesthesia. (it is very much like the instantaneous bodily orientation one gets by suddenly touching a wall after wandering around in a pitch-dark room; one swipe of a hand across the wall tells you much about the space that persists for some time after you walk away from the wall).

here's a brief (8 second) movie of it swept past a wall edge. this awkward video was taken with a remote microphone held onto the vibrating button so that it can be heard in the video as a mild buzzing. Note the edge-emphasis as it sweeps past the stepladder leaning on the building.

technical details

the ping torch consists of a Sparkfun Arduino Mini Pro, an EZ-1 sonar sensor (see note below), a vibrating motor, power supply and support glue, and housed in a thin wall aluminum tube. the software is almost trivially simple: sonar range is taken continuously (as the power button is held down), at the EZ1's maximum rate of about 20 measurements/second, and that range (in inches) is translated to an arbitrary "intensity" value. some heuristics are applied: the mapping is logarithmic, not linear; things closer are more "interesting" (collision avoidance, etc). objects beyond 80 inches are intentionally ignored; "feeling" things beyond that became confusing (sounds quite arbitrary here in type, but experienced quite obvious). to somewhat help overcome response time limitations inherent in the sensor, edge emphasis is done when there is an abrupt change in distance, eg. sweeping the sensor past a doorway, or an object projecting from the wall. emphasis is done by differentiating the range and adding this into the steadystate intensity. edges feel more edgy.

here's the code.

/*  Ping Torch    Tom Jennings     27 nov 2012   stripped out IR code (forked a copy with IR left in) as IR reflectivity                is too unreliable. Put reaction code into ISR so that it's run as soon as                the sonar PW edge falls, for best-case response time. Tweaked emphasis.  15 nov 2012   since IR reflectivity is spotty, added a "hold" feature ("retriggerable                one shot") to hold output for a time so that brief reflections                get extended. juggled logic. sonar to IR crossover not that great                around USEIR, but seems OK. decreased timers all around.  13 nov 2012   changed EZ-1 over to interrupt-driven (from pulseIn()). Tweaked                distance threshholds, added timer so that it updates intensity                (and looks at ir, when appropriate) every 10mS.  08 nov 2012   tweaked up and working.  07 nov 2012   switched from unusable Pro Micro to the non-USB version; couldn't                get the driver to work. added code for Sharp sensor.  04 nov 2012   Moved to Sparkfun Arduino Pro Micro, added Sharp IR sensor.  18 Oct 12     Removed integration; i assumed (wrongly) it was needed and all it                did was of course slow response, and fast response is more                important than noise.                 Restored edge detection.  17 Oct 2012   Disabled edge amplification.  16 Oct 2012   New.      inspired by the Haptic Torch project of the Interactive Systems Group  of the   University of Reading,, by Adam Spiers    This one uses a vibratory motor as effector, arranged to be under the tip of  your pointing finger. The power switch is under the thumb. There are no other controls.  The entire thing is housed in a section of tubing with the sensor looking out the front.    Uses a MaxSonar EZ-1 ultrasonic range finder. The EZ-1 takes a measurement every   ~49mS by default. Vibration intensity is inversely proportional to distance; close  objects vibrate most intensely. Objects beyond MAXDIST inches generate no  response.    Additionally, abrupt changes in distance (eg. the edges of objects) are emphasized by  momentarily increasing intensity when seeing an edge.     */ // need log() from this. #include  // Tunable parameters. const int DEBUGTIME = 333;    // how often we print distance gunk const int EDGEEMPH = 4;       // exaggerates the effect of distance step changes (1==none) const int EDGETHRESH = 4;     // how abrupt a change we notice const int MINDIST = 30;       // anything below this is "close" (maximum intensity) const int MAXDIST = 80;       // anything beyond this (in inches) is un-seen // These are ad hoc characteristics of this vibrating motor; below Min it's undetectable, // and not too much difference between Max and actual maximum (AWFS) which we'll reserve  // for edge detection emphasis. const int BUZZMIN = 50; const int BUZZMAX = 180; const int AWFS = 256;         // analogWrite() full scale value (0..255) // Arduino hardware pin assignments. const int EZPW = 3;           // EZ-1 PW output (interrupt 1) const int EZPWR = 9;          // lol, ran out of physical pins for 5V; EZ-1 draws 3mA! const int BUZZER = 6;         // vibrating motor driver pin struct _diff {   int *a;                      // history (of differences)   unsigned z;                  // size   unsigned i;   long int sum;   unsigned recent;             // most-recent } D; // EZ-1 sonar pinger stuff. volatile unsigned long ezT;    // uS timer volatile int distance;         // most recent measurement volatile int intensity;        // most recent vibration intensity void setup (void) {   analogWrite (BUZZER, 0);     // shut up!   pinMode (EZPWR, OUTPUT);     // (ran out of holes on the board)   digitalWrite (EZPWR, 1);     // EZ-1 draws only 3mA!   Serial.begin (9600);   newDiff (&D, 2);             // set up the differentiator   attachInterrupt (1, EZ1PW, CHANGE); } /* The EZ-1 "free runs", taking a measurement every 50mS or so, and generates a pulse whose width is proportional to distance; 147uS/inch. The ISR (EZ1PW())  handles that. */ void loop (void) {    static unsigned long T;       // loop response timer   static unsigned long dbT;     // debug output timer // Just print crap out to the F232 adapter, 3 times/sec.   if (millis() > dbT && intensity) {    dbT= millis() + DEBUGTIME;    Serial.print (distance, DEC);     Serial.print ("\", buzz ");    Serial.print (intensity, DEC);    Serial.println("");   } } /* The EZ-1 generates a positive-going pulse whose width is 147uS/inch. This ISR is invoked on each edge of the pulse. Start the timer on the rising edge,  stop it and calculate integer distance (inches) on the falling edge. */ void EZ1PW () { int edge;                     // exaggerates response at the edge of objects   if (digitalRead (EZPW)) {                              // rising edge     ezT= micros();                                       // start the timer        } else {     intensity= 0;     distance= (micros() - ezT + (147/2)) / 147;          // falling edge      // this better take less than 47 mS!     if (distance  // Map linear distance onto a logarithmic vibration-intensity scale; things  // closer are more intense. //                invert range            logarithmic scaling (0..1)          vibration range       intensity= BUZZMAX - (log10 ((float)distance * (10.0/(float)MAXDIST)) * (BUZZMAX - BUZZMIN)); // Add emphasis at abrupt edges.       edge= diff (&D, distance);                          // this is non-zero at edges,       if (abs (edge) > EDGETHRESH) intensity *= EDGEEMPH; // emphasize edges,       intensity= min (BUZZMAX, max (intensity, BUZZMIN)); // bound it,     }     analogWrite (BUZZER, intensity);                      // update buzzer intensity   } }   /***************************************************************************  *   * Compute the sum of differences in a stream of input values.  *   * d= diff (v);  *   ***************************************************************************/ void newDiff (struct _diff *d, int z) {   d-> z= z;   d-> sum= 0L;   d-> i= d-> recent= 0;   d-> a= (int *) malloc (z * sizeof(int));   for (int i= 0; i a[i]= 0; } int diff (struct _diff *d, int v) {   int n= v - d-> recent;           // new diff is current - previous   d-> recent= v;                   // current becomes next previous   d-> sum -= d-> a[d-> i];         // subtract oldest (diff) from sum   d-> a[d-> i]= n;                 // replace oldest difference with newest (shift register)   ++d-> i %= d-> z;                // advance index   return(d-> sum += n);            // add newest diff to sum, return it. }

Continue reading on