Earlier this year, the City of Philadelphia released its Vision Zero Action Plan. This ambitious plan aims to reduce traffic deaths in Philadelphia to zero by 2030, and is the major motivation behind our design.
The plan outlines four major goals:
We believe that the Lit-Lane design and concept can be a powerful tool in achieving all four goals.
The Problem: bike network is incomplete and misused
Even in bike lanes, bikers are often an afterthought. Cars, trucks, and buses regularly drive, stop and turn (and open their doors) into bike lanes, which deters people from biking and jeopardizes the safety of cyclists.
This issue is especially prevalent in Philadelphia, which has one of the highest rates of biking deaths per capita in the U.S, with higher rates than Boston, NYC, and LA. It is also important to note that biking accidents are very localized to specific streets- just 12% of Philadelphia Streets account for 50% of serious injuries or fatalities.
It’s time to take action:
If planners really want to promote biking and reduce deaths, we need to get serious about improving safety and making roads work for bikers.
The Solution: Enter Lit-Lane!
The aim of our design is to:
- Make bikers more visible and noticeable
- Reduce obstacles in bike lanes
- Make biking more beautiful and fun
- Create a psychological shift to reclaim the bike lane
- Be adaptive to different street typologies and preferences
Lit-Lane uses a combination of sensors (more on this later) to sense and react to the conditions on the bike lane. When Lit-Lane senses bikers, it surrounds them with beautiful white lights that lead in front of them and make them more visible to cars ahead. When Lit-Lane senses cars, it reacts with blinking lights which change from yellow to red, to notify the drivers that they must vacate the lane.
Lit-Lane is better than the traditional alternatives
- Painted buffers: low protection, low night protection
- Delineator posts: unattractive, obstructive, not durable
- Bumps/Ballards: unattractive, can cause damage, prohibitive
- Parked cars: space inefficient, potential to get doored
- Cast curbs or planters: space inefficient, very expensive
Instead, Lit-Lane:
- Achieves safety goals
- Creates no physical barriers
- Is more space efficient
- Actively discourages bad behavior
- Is beautiful, and creates an attraction that promotes biking and active transport
Lit-Lane I/O
Sensory Inputs-
- Weight sensors: Classify vehicle as bicycle or car
- Ultrasonic distance sensors: Detect the location of vehicles
Reaction-
- LED Strips: White light surrounds moving cyclists. Yellow light signals a warning when cars crosses lane. Red light signals when car hasn’t moved.
- Sound Buzzer: Warns cars to move out of bike lane
The diagram below shows how the sensory inputs are processed and used to illicit a response.
So, How Does It Work?
To demonstrate our concept, we created a prototype using an Arduino board and some off-the-shelf sensors. The prototype really brings the idea to life! We put together a short video to show the end results (disclaimer: video was not sponsored by diet coke…).
Note that the sensor bump in the middle of the lane is not to scale. In reality, the sensor will remain very small while the lane would be… well… life sized.
Here are the nuts and bolts of how we put together the prototype:
Making It A Reality
We wanted to imagine what Lit-Lane would look like in the real world. To do this, we picked a location we think would be ideal for this type of project: Broad Street.
We picked a section of Broad Street that is specifically identified in the Vision Zero Plan as being high risk for bikers.
This is a map of bike accident hot spots in Philadelphia:
In addition to being a high risk street section, we believe this part of Broad Street will be ideal because of the high-profile nature of the street design. The lights and sounds produced by Lit-Lane will likely be best received in streets that are already bright and loud. However, like we mentioned earlier in the post, it will be very easy to adapt the settings of Lit-Lane to fit different street typologies. Streets that are residential/quiet can have little to no noise response and lights that are programmed to be less disruptive to residents. Additionally, we believe main commercial streets are ideal because the spectacle and attraction that Lit-Lane creates has the potential of increasing tourism and economic activity. This will help garner the support of the business community downtown.
Before + After mockups help imagine what could be:
Dealing With Costs
Determining cost is currently tricky with Lit-Lane. While less so than some alternatives, our prototype is still somewhat expensive, due to the nature of the weight sensors needing to be in the ground. However, newer iterations of the project can be adapted to reduce this cost.
This being the first iteration of the design, we believe that large potential exists for reducing costs in future designs. Reworking the sensor configuration such that installation/maintenance will not require digging up the street will drive the costs down dramatically. Many technologies ranging from low-end (such as on-bike sensors) to high-end (such as machine vision) could be used to achieve this goal in the future.
Thank you!
Thank you so much for reading, and happy biking!
Guy Duer, Rachael Hartofelis, and Boao Xia
CPLN571, Prof. Allison Lassiter, University of Pennsylvania
December 2017
Appendix: Prototype Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
/*This code operates a smart bike lane prototype for CPLN571 course at UPenn. With this code, the bike lane can tell *where the location of a biker is and shine lights to protect the biker. The lane activates when the weight sensors sense the weight *of a bike. When the weight is too high (indicating a car/bus), the bike lane blinks yellow and after a few seconds blinks red and plays *an alarm. This is to notify the car to clear the bikelane. */ #include <Time.h> #include <TimeLib.h> #include <FastLED.h> #include <NewPing.h> #include <HX711.h> #include <NewTone.h> //inititate settings #define NUM_LEDS 80 const int ledLength = 10; #define MAX_DISTANCE 80 float bikeweight = 0.06; float carweight = 1; float blinkmax = 30; int blinkspeed = 350; //initiate pins #define LED_PIN 5 #define LED_PIN2 4 #define TRIGGER_PIN 7 #define ECHO_PIN 8 #define TRIGGER_PIN2 11 #define ECHO_PIN2 12 #define buzzerPin 13 #define DOUT 3 #define CLK 2 #define DOUT2 10 #define CLK2 9 //initiate global variables long distance1, distance2; long distance1a, distance1b, distance1c, distance2a, distance2b, distance2c, distance1d, distance2d; float weight1, weight1a, weight1b, weight1c; float weight2, weight2a, weight2b, weight2c; float blinkcount1, blinkcount2 = 0; long previousMillis = 0; int ledValue1, ledValue2; int sensorValue; int dimCount; int DimArray[10] = {240,210,150,100,0,0,100,150,210,240}; //initiate components CRGB leds[NUM_LEDS]; CRGB leds2[NUM_LEDS]; HX711 scale(DOUT, CLK); HX711 scale2(DOUT2, CLK2); float calibration_factor = -7050; //-7050 worked for my 440lb max scale setup NewPing sonar1(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); NewPing sonar2(TRIGGER_PIN2, ECHO_PIN2, MAX_DISTANCE); void setup() { //set up components FastLED.addLeds<WS2811, LED_PIN>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip ); FastLED.addLeds<WS2811, LED_PIN2>(leds2, NUM_LEDS).setCorrection( TypicalLEDStrip ); pinMode(TRIGGER_PIN, OUTPUT); pinMode(ECHO_PIN, INPUT); scale.set_scale(); scale.tare(); scale2.set_scale(); scale2.tare(); long zero_factor = scale.read_average(100); //Get a baseline reading long zero_factor2 = scale2.read_average(100); //Get a baseline reading } void loop() { //obtain and smooth distance from distance sensor 1 distance1d = distance1c; distance1c = distance1b; distance1b = distance1a; distance1a = sonar1.ping_cm(); distance1 = (distance1a + distance1b + distance1c)/3; //obtain and smooth distance from distance sensor 2 distance2d = distance2c; distance2c = distance2b; distance2b = distance2a; distance2a = sonar2.ping_cm(); distance2 = (distance2a + distance2b + distance2c)/3; //obtain and smooth weight from load sensor 1 scale.set_scale(calibration_factor); //Adjust to this calibration factor weight1a = weight1b; weight1b = weight1c; weight1c = scale.get_units(); weight1 = (weight1a + weight1b + weight1c)/3; //obtain and smooth weight from load sensor 2 scale2.set_scale(calibration_factor); //Adjust to this calibration factor weight2a = weight2b; weight2b = weight2c; weight2c = scale2.get_units(); weight2 = (weight2a + weight2b + weight2c)/3; //Map distance values to reflect correct location on the LED strips ledValue1 = map(distance1, 0, 55, 0, 35); ledValue1 = abs(ledValue1 - 35); ledValue2 = map(distance2, 0, 55, 42, 70); //Clear past LED configuration and reset fade index FastLED.clear(); dimCount = 0; //configure lights in first section of LED strip depending on weight and times blinked if(weight1 > bikeweight){ //condition for yellow lights if(weight1 >= carweight && blinkcount1 < blinkmax){ //start time to configure yellow lights blinking unsigned long currentMillis = millis(); //turn yellow lights on when millis is between blink interval if((currentMillis - previousMillis >= blinkspeed) && (currentMillis - previousMillis < (blinkspeed*2))){ for(int led = ledValue1-3; led < ledValue1+7; led++) { leds[led] = CRGB(100,255,0); leds[led].fadeToBlackBy(DimArray[dimCount]); leds2[led + 1] = CRGB(100,255,0); leds2[led + 1].fadeToBlackBy(DimArray[dimCount]); dimCount = dimCount + 1;} blinkcount1 = blinkcount1 + 1;} //turn lights off when millis is smaller then blink interval else if(currentMillis - previousMillis < blinkspeed){ for(int led = ledValue1-3; led < ledValue1+7; led++) { leds[led] = CRGB::Black; leds2[led + 1] = CRGB::Black;} } //When greater than blink interval, reset millis else{ previousMillis = currentMillis; }} //condition for red lights else if(weight1 >= carweight && blinkcount1 >= blinkmax){ //start time to configure red lights blinking unsigned long currentMillis = millis(); if((currentMillis - previousMillis >= blinkspeed) && (currentMillis - previousMillis < (blinkspeed*2))){ for(int led = ledValue1-3; led < ledValue1+7; led++) { //unlike the yellow lights, red lights include a sound response NewTone(buzzerPin, 523, 100); leds[led] = CRGB(0,255,0); leds[led].fadeToBlackBy(DimArray[dimCount]); leds2[led + 1] = CRGB(0,255,0); leds2[led + 1].fadeToBlackBy(DimArray[dimCount]); dimCount = dimCount + 1;} dimCount = 0; } else if(currentMillis - previousMillis < blinkspeed){ for(int led = ledValue1-3; led < ledValue1+7; led++) { leds[led] = CRGB::Black; leds2[led + 1] = CRGB::Black;} } else{ previousMillis = currentMillis; }} else{ for(int led = ledValue1-3; led < ledValue1+7; led++) { leds[led] = CRGB::White; leds[led].fadeToBlackBy(DimArray[dimCount]); leds2[led + 1] = CRGB::White; leds2[led + 1].fadeToBlackBy(DimArray[dimCount]); dimCount = dimCount + 1; blinkcount1 = 0;}} } //reset dim index in preparation for activating the second portion of the LED strip (the same way as the first) dimCount = 0; if(weight2 >= bikeweight){ if(weight2 >= carweight && blinkcount2 < blinkmax){ unsigned long currentMillis = millis(); if((currentMillis - previousMillis >= blinkspeed) && (currentMillis - previousMillis < (blinkspeed*2))){ for(int led = ledValue2-3; led < ledValue2+7; led++) { leds[led] = CRGB(100,255,0); leds[led].fadeToBlackBy(DimArray[dimCount]); leds2[led + 1] = CRGB(100,255,0); leds2[led + 1].fadeToBlackBy(DimArray[dimCount]); dimCount = dimCount + 1;} blinkcount2 = blinkcount2 + 1;} else if(currentMillis - previousMillis < blinkspeed){ for(int led = ledValue2-3; led < ledValue2+7; led++) { leds[led] = CRGB::Black; leds2[led + 1] = CRGB::Black;} } else{ previousMillis = currentMillis; }} else if(weight2 >= carweight && blinkcount2 >= blinkmax){ unsigned long currentMillis = millis(); if((currentMillis - previousMillis >= blinkspeed) && (currentMillis - previousMillis < (blinkspeed*2))){ for(int led = ledValue2-3; led < ledValue2+7; led++) { NewTone(buzzerPin, 523, 100); leds[led] = CRGB(0,255,0); leds[led].fadeToBlackBy(DimArray[dimCount]); leds2[led + 1] = CRGB(0,255,0); leds2[led + 1].fadeToBlackBy(DimArray[dimCount]); dimCount = dimCount + 1;} dimCount = 0; }//Think about changing the location of this to stop the flickering. else if(currentMillis - previousMillis < blinkspeed){ for(int led = ledValue2-3; led < ledValue2+7; led++) { leds[led] = CRGB::Black; leds2[led + 1] = CRGB::Black;} }//Think about changing the location of this to stop the flickering. else{ previousMillis = currentMillis; }} else{ for(int led = ledValue2-3; led < ledValue2+7; led++) { leds[led] = CRGB::White; leds[led].fadeToBlackBy(DimArray[dimCount]); leds2[led + 1] = CRGB::White; leds2[led + 1].fadeToBlackBy(DimArray[dimCount]); dimCount = dimCount + 1; blinkcount2 = 0;}} } FastLED.show(); } |