Introduction:
In the United States, there are 3.3 million people over the age of 40 with vision impairments that cannot be mitigated with glasses, contacts, pharmaceuticals, or surgery. Of these 3.3 million, 937,000 are blind. The most common causes of blindness are age-related diseases such as macular degeneration, cataract, and glaucoma. Due to the aging population, the number of blind people is expected to increase to 1.6 million in 2020 (Congdon, et al., 2004).
In order to navigate independently, the vision impaired people require mobility tools. The standard is the long white cane, which is used to sweep the ground for obstacles and to indicate to others that the user is blind. The cane is lightweight and inexpensive, and requires minimal maintenance. However, these canes give a limited range of information. The optimal cane length is 8 inches shorter than the user, which means that a 5’6” person would have a 4’10” cane (National Federation of the Blind, 2011). Since the cane is held at an angle, the actual range is approximately 3.5 feet. This limited range restricts the speed at which the person can travel and forces the person to react quickly to obstacles. Furthermore, the cane cannot tell the user if there is a fast approaching obstacle, whether it is a person or a car. Therefore, walking down the street and crossing the road are much more perilous and nerve wracking for the blind.
Another common tool for the vision impaired is the guide dog. Dogs possess natural intelligence and can communicate to a certain extent with humans. However, some people are allergic to dogs or do not want to spend time and money caring for dogs. Furthermore, dogs cannot perceive color the same way humans do, so they cannot be trained to understand traffic lights. The human has to determine when to cross the road by listening to the flow of traffic, which leaves room for uncertainty and risk (Hersh & Johnson, 2008).
Enhanced canes using ultrasound technology include the GuideCane, a prototype with wheels designed by Borenstein and Ulrich. The GuideCane is a rolling unit with ten ultrasound sensors, a computer, and servo motors. The sensors receive inputs from the environment that the computer processes. If there is an obstacle in the user’s path, the servo motors steer the wheels of the cane, changing the user’s path. However, this device is relatively heavy at 4 kg, which limits its portability, especially when navigating stairs. Other enhanced canes use infrared laser technology to detect obstacles; however, this technology can sometimes miss glass or transparent plastic barriers such as glass buildings, doors or bus shelters (Hersh & Johnson, 2008).
Objectives:
The aim was to increase the range of the common white cane and to add a speed detection function. The device was named “Speed Stick” and we considered several criteria when designing it. First, the device needs to produce a real-time analysis of the environment in order to provide useful information when the person is moving. Second, the cane should be easy to understand and use with minimal training. More specifically, the outputs of the cane need to solicit the correct intuitive reactions from the user. For example, the user should not have to think extensively in order to figure out that the cane is trying to tell him to “get out of the way now.” Third, the cane needs to accommodate people of varying heights. Lastly, the cane should be light weight and balanced.
Hardware:
The cane utilizes one LV-MaxSonar-EZ4 ultrasonic transducer, one cellphone vibrating motor, two battery packs of four AA batteries, and one Arduino microcontroller. The vibrator housing was designed using SolidWorks. It was printed using Penn’s 3D printer in the MEAM department.
The inputs to the system come from the ultrasound transducer, which both sends and receives ultrasound waves. The Arduino then processes the input and sends signals to the cellphone vibrating motor. Finally, the motor provides vibrational outputs.
The transducer is secured to approximately the halfway point of the shaft of the cane so that it is located close enough to the ground to detect low lying obstacles. The transducer is not placed directly on the tip of the cane because the tip is used to tap objects and sustains the most damage from uneven terrain (Hersh & Johnson, 2008). Furthermore, the angle of the transducer relative to the ground is manually adjustable to accommodate people of varying heights who may hold the cane at different angles. The microcontroller and the battery packs are secured directly behind the transducer on the backside of the cane, which minimizes lengthy wires and tangling. Additionally, it is intuitive to the user which side the transducer is on because the cane will naturally rest in the proper position as a result of weight distribution. The cane and its components can be seen in the following image. One battery pack supplies the ultrasound transducer, while the other pack powers the rest of the components. The vibrator is secured to the top of the cane, near the handle, for optimal transfer of vibrational information. The motor is housed in a specially-designed plastic shell that protects the wiring from accidental damage.
Software:
The software design is one of the more important aspects of the Speed Stick. The coding enables the microcontroller to take as an input ultrasound pulse width and output that into vibration frequency. The approach to coding the input and output will now be delineated.
Input:
The ultrasound transducer is capable of providing several types of information to the microcontroller. The Speed Stick uses pulse width as its input. The pulse width, according to the transducer’s datasheet, is the “time duration of the time domain envelope that is 20dB above the rising and decaying cycles of a transducer response.” The pulse width thus varies depending upon the distance that an ultrasound signal has to travel to reach an object.
A time duration variable is first created that takes as its value the time domain length of the pulse width. This time value is then converted into distance using the "microsecondsToInches" function, seen in the following. The conversion factor is based off the fact that there are 73.75 microseconds per inch, assuming sound travels at a speed of 1130 feet per second. This result is divided by 2 to account for the fact that the sound travels to the object and back. The validity of this conversion was verified with a meter stick. This function was harvested from the Arduino references pages.
long microsecondsToInches(long microseconds)
{
return microseconds / 74 / 2;
}
{
return microseconds / 74 / 2;
}
Output:
With a distance value in hand, the next step is to tell the cellphone vibrator when to vibrate. There are two target functionalities that use the vibrator within the Speed Stick: to alert the user if there is an object moving toward them above a given velocity, and to inform the user of the distance between the Speed Stick and an object.
The velocity functionality works as a binary operator: either there is an object quickly approaching them or there is not. The output is a simple yet distinctive 2-second vibration. To determine when to give this emergency output, however, is more interesting.
The algorithm uses an array called distanceArraySpeed. The algorithm assigns 3 values into the array over time, sorts them in numerical order using a function called bSort1, and then takes the middle value. This middle value, after these operations, is the median. This approach acts as a sort of filter that removes outlying values reported by the transducer. Because this happens on the fly and a median is collected every 3 loop iterations no matter what, minimal delay is instituted. The velocity is then calculated by taking the difference between two consecutive median values, and subsequently dividing that by the delta time. The final step is to check the velocity against a velocity threshold, and to have the 2-second emergency vibration go off whenever the reported velocity exceeds it.
The second functionality, which tells the user how far objects are in front of them, is coded to behave in a very similar manner to the velocity functionality. In this way, an array called distanceArray is used to collect distance values over time. However, instead of taking only 3 distance values, it takes 5 values. The rationality behind using 5 values instead of 3 values for this functionality is twofold: 5 values afford a much more robust filtering of outlying values, and practice tests indicated that the extra time it took to store 2 more values was negligible for this feature. A median is thus calculated using the same sort function that is used in the velocity-warning algorithm.
The median is then sent through a band-pass filter that removes low and high aberrant values. The high end cutoff is set to 100 inches, but can be increased to augment the amount of distance information that the user gets. The hardware gives far less accurate information as the distance increases, however, so 100 inches was determined to be optimal for providing accurate information over a large area.
With an acceptable median value in hand, the next step is to tell the vibrator to vibrate at a frequency that correlates with the distance value. It was decided that the frequency of vibrations should be high when objects are close, and the frequency of vibrations should be low when the objects are far. A formula that utilizes a linear equation, shown in the following, was used to accomplish this.
digitalWrite(pin,HIGH);
delay(200.0+((median-5.0)/100.0)*800.0);
digitalWrite(pin,LOW);
delay(200.0+((median-5.0)/100.0)*800.0);
digitalWrite(pin,LOW);
In this way, when the median distance value is low (i.e. when objects are close), the delay is small and the frequency of vibration is thus high. The opposite is true for when the median distance value is high. Another equation was also attempted that was non-linear (see Appendix), but the linear function gave a better performance in the end.
It is important to note that the velocity detection functionality can easily be turned off by setting velocityFeature to zero. The distance detection functionality is always on.
Conclusion:
The Speed Stick is effective at providing the user with two types of information: whether there is an object moving towards the user above a velocity threshold, and informing the user the distance between the Speed Stick and an object. The velocity-warning feature is limited by the rapidity with which data points are collected as well as the error-prone hardware that is used in the prototype. The distance-detection feature is very robust in its current state, but would benefit from a more sophisticated ultrasound transducer. The prototype demonstrates that the blind can be hopeful that there will be strong navigation-support alternatives to current options in the future.
Works Cited:
Congdon, N., O'Colmain, B., Klaver, C. C., Klein, R., Munoz, B., Friedman, D. S., et al. (2004). Causes and prevalence of visual impairment among adults in the United States. Archives of Ophthalmology, 477-485.
Hersh, M. A., & Johnson, M. A. (2008). Assistive Technology for Visually Impaired and Blind People. London: Springer.
National Federation of the Blind. (2011). Free White Cane Program. Retrieved April 5, 2011, from National Federation of the Blind: http://www.nfb.org/nfb/free_cane_program.asp
Appendix:
/*
Nathan Carberry
Betty Huang
Project
April 27, 2011
*/
//variable definitions
int pin = 10;
int pinUS = 5;
float duration = 0;
int counter = 0;
int counterSpeed = 0;
const int arrayLength = 5;
const int arrayLengthSpeed = 3;
int distanceArray[arrayLength];
int distanceArraySpeed[arrayLengthSpeed];
int timeArray[2]={0,millis()};
float deltaX;
float deltaT;
float velocity;
float speedLimit = 2.8;
int velocityFeature = 0;
int printOn=0; //give printing feedback on (1) or off (0)
void setup()
{
pinMode(pin,OUTPUT);
Serial.begin(9600);
}
//Note: Some of the following code relating to ping was borrowed from the Arduino references.
void loop()
{
static int oldMedian=0; //constant values created with static
static int newMedian=0;
// establish variables for duration of the ping,
// and the distance result in inches and centimeters:
long duration, inches, cm;
int pingPin = pinUS;
// The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
// Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);
// The same pin is used to read the signal from the PING))): a HIGH
// pulse whose duration is the time (in microseconds) from the sending
// of the ping to the reception of its echo off of an object.
pinMode(pingPin, INPUT);
duration = pulseIn(pingPin, HIGH);
// convert the pulseIn time into a distance
inches = microsecondsToInches(duration);
cm = microsecondsToCentimeters(duration);
if(printOn==1) //for observation purposes
{
Serial.print(inches);
Serial.print("in, ");
Serial.print(cm);
Serial.print("cm");
Serial.println();
}
distanceArray[counter] = inches;
distanceArraySpeed[counterSpeed] = inches;
timeArray[counter] = millis();
counter = counter + 1;
counterSpeed = counterSpeed + 1;
int x = 1;
int Value;
int ValueIndex;
int True = 0;
int RangeHigh = 0;
int RangeLow = 0;
int RangeHighTwo = 0;
int RangeLowTwo = 0;
int median;
//************************************************************************
//alerts user if detected object moves at a certain speed towards the walking stick
//************************************************************************
if(counterSpeed==arrayLengthSpeed && velocityFeature==1)
{
bSort1(distanceArraySpeed, 0, arrayLengthSpeed);
median = distanceArraySpeed[arrayLengthSpeed/2]; //finds the median value in the distanceArraySpeed
timeArray[0]=timeArray[1];
timeArray[1]=millis();
oldMedian=newMedian;
newMedian=median;
deltaX=newMedian-oldMedian;
deltaT=timeArray[1]-timeArray[0];
velocity = deltaX*1.0/deltaT*1.0;
if(velocity>=speedLimit && velocity<30)
{
Serial.println("FAST!!");
digitalWrite(pin,HIGH);
delay(2000);
digitalWrite(pin,LOW);
}
else if(velocity<=-speedLimit && velocity>-30)
{
Serial.println("FAST!!");
digitalWrite(pin,HIGH);
delay(2000);
digitalWrite(pin,LOW);
}
counterSpeed = 0;
}
//************************************************************************
//creates vibration feedback that is correlated with distance
//************************************************************************
if(counter==arrayLength)
{
bSort1(distanceArray, 0, arrayLength);
median = distanceArray[arrayLength/2]; //finds the median in the distanceArray
if(printOn==1)
{
Serial.println(median); //for checking values if printing is on
}
if(median<5) //do nothing if median is less than 5 inches
{
}
else if(median>100) //do nothing if median is greater than 100 inches
{
}
else
{
digitalWrite(pin,HIGH);
delay(200.0+((median-5.0)/100.0)*800.0); //linear function that creates vibrations at a
digitalWrite(pin,LOW); //frequency inverted with distance
}
counter = 0;
//non-linear attempt at correlating distance and vibration frequency
/*
else if(median>50)
{
}
else
{
digitalWrite(pin,HIGH);
delay(200.0+sqrt(((median-5)/50.0)*900.0*900.0));
digitalWrite(pin,LOW);
}
*/
}
}
//sorting function to aid in finding the median value
void bSort1(int array [], int from, int upTo) {
// code size = 74 bytes
int swaps;
do {
swaps=0;
for(int i = from; i < upTo; i++)
if(array[i] > array[i+1]) {
int x = array[i+1];
array[i+1] = array[i];
array[i] = x;
++swaps;
}//for
} while (swaps);
}//bsort1
long microsecondsToInches(long microseconds)
{
// According to Parallax's datasheet for the PING))), there are
// 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
// second). This gives the distance travelled by the ping, outbound
// and return, so we divide by 2 to get the distance of the obstacle.
// See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
return microseconds / 74 / 2;
}
long microsecondsToCentimeters(long microseconds)
{
// The speed of sound is 340 m/s or 29 microseconds per centimeter.
// The ping travels out and back, so to find the distance of the
// object we take half of the distance travelled.
return microseconds / 29 / 2;
}
Nathan Carberry
Betty Huang
Project
April 27, 2011
*/
//variable definitions
int pin = 10;
int pinUS = 5;
float duration = 0;
int counter = 0;
int counterSpeed = 0;
const int arrayLength = 5;
const int arrayLengthSpeed = 3;
int distanceArray[arrayLength];
int distanceArraySpeed[arrayLengthSpeed];
int timeArray[2]={0,millis()};
float deltaX;
float deltaT;
float velocity;
float speedLimit = 2.8;
int velocityFeature = 0;
int printOn=0; //give printing feedback on (1) or off (0)
void setup()
{
pinMode(pin,OUTPUT);
Serial.begin(9600);
}
//Note: Some of the following code relating to ping was borrowed from the Arduino references.
void loop()
{
static int oldMedian=0; //constant values created with static
static int newMedian=0;
// establish variables for duration of the ping,
// and the distance result in inches and centimeters:
long duration, inches, cm;
int pingPin = pinUS;
// The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
// Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);
// The same pin is used to read the signal from the PING))): a HIGH
// pulse whose duration is the time (in microseconds) from the sending
// of the ping to the reception of its echo off of an object.
pinMode(pingPin, INPUT);
duration = pulseIn(pingPin, HIGH);
// convert the pulseIn time into a distance
inches = microsecondsToInches(duration);
cm = microsecondsToCentimeters(duration);
if(printOn==1) //for observation purposes
{
Serial.print(inches);
Serial.print("in, ");
Serial.print(cm);
Serial.print("cm");
Serial.println();
}
distanceArray[counter] = inches;
distanceArraySpeed[counterSpeed] = inches;
timeArray[counter] = millis();
counter = counter + 1;
counterSpeed = counterSpeed + 1;
int x = 1;
int Value;
int ValueIndex;
int True = 0;
int RangeHigh = 0;
int RangeLow = 0;
int RangeHighTwo = 0;
int RangeLowTwo = 0;
int median;
//************************************************************************
//alerts user if detected object moves at a certain speed towards the walking stick
//************************************************************************
if(counterSpeed==arrayLengthSpeed && velocityFeature==1)
{
bSort1(distanceArraySpeed, 0, arrayLengthSpeed);
median = distanceArraySpeed[arrayLengthSpeed/2]; //finds the median value in the distanceArraySpeed
timeArray[0]=timeArray[1];
timeArray[1]=millis();
oldMedian=newMedian;
newMedian=median;
deltaX=newMedian-oldMedian;
deltaT=timeArray[1]-timeArray[0];
velocity = deltaX*1.0/deltaT*1.0;
if(velocity>=speedLimit && velocity<30)
{
Serial.println("FAST!!");
digitalWrite(pin,HIGH);
delay(2000);
digitalWrite(pin,LOW);
}
else if(velocity<=-speedLimit && velocity>-30)
{
Serial.println("FAST!!");
digitalWrite(pin,HIGH);
delay(2000);
digitalWrite(pin,LOW);
}
counterSpeed = 0;
}
//************************************************************************
//creates vibration feedback that is correlated with distance
//************************************************************************
if(counter==arrayLength)
{
bSort1(distanceArray, 0, arrayLength);
median = distanceArray[arrayLength/2]; //finds the median in the distanceArray
if(printOn==1)
{
Serial.println(median); //for checking values if printing is on
}
if(median<5) //do nothing if median is less than 5 inches
{
}
else if(median>100) //do nothing if median is greater than 100 inches
{
}
else
{
digitalWrite(pin,HIGH);
delay(200.0+((median-5.0)/100.0)*800.0); //linear function that creates vibrations at a
digitalWrite(pin,LOW); //frequency inverted with distance
}
counter = 0;
//non-linear attempt at correlating distance and vibration frequency
/*
else if(median>50)
{
}
else
{
digitalWrite(pin,HIGH);
delay(200.0+sqrt(((median-5)/50.0)*900.0*900.0));
digitalWrite(pin,LOW);
}
*/
}
}
//sorting function to aid in finding the median value
void bSort1(int array [], int from, int upTo) {
// code size = 74 bytes
int swaps;
do {
swaps=0;
for(int i = from; i < upTo; i++)
if(array[i] > array[i+1]) {
int x = array[i+1];
array[i+1] = array[i];
array[i] = x;
++swaps;
}//for
} while (swaps);
}//bsort1
long microsecondsToInches(long microseconds)
{
// According to Parallax's datasheet for the PING))), there are
// 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
// second). This gives the distance travelled by the ping, outbound
// and return, so we divide by 2 to get the distance of the obstacle.
// See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
return microseconds / 74 / 2;
}
long microsecondsToCentimeters(long microseconds)
{
// The speed of sound is 340 m/s or 29 microseconds per centimeter.
// The ping travels out and back, so to find the distance of the
// object we take half of the distance travelled.
return microseconds / 29 / 2;
}