We've all experienced that frustrating moment: waiting at a red light with no traffic from the other direction to justify the delay.
It doesn't have to be this way.
Using computer vision, I'm building a new class of traffic light that adjusts the lights according to the amount of traffic it detects from each street. Throughout this project, I have encountered numerous probelms. Through preserverance, I was able to ultimately resolve these issues and significantly increased my understanding of computer vision.
I am building this system on a Raspberry Pi 4, which has presented a large problem: the Raspberry Pi only supports one camera, while I need four (one for each street side). As a solution, I decided to put the camera on a stepper motor, where the motor rotates 90 degrees to capture each street side.
The first step of this project was to select a traffic dataset. I searched all over Kaggle and Google Search, but I couldn't find anything that fit my needs. Building a dataset like I did with my traffic sign classification project was impractical because unlike traffic signs, vehicles vary in size, shape, color, and features, which increases the complexity of assembling a good dataset. I ultimately found COCO, a large 120,000 image dataset with images for 80 different classes - ranging from different types of vehicles, to people, to toothbrushes.
Next, I needed to choose an object detection algorithm. I was tempted to use a Convolutional Neural Network again for its high accuracy; but because training models is computationally expensive, I opted for an algorithm called YOLO (You Only Look Once) which is a much faster and computationally efficent algorithm, albeit slightly less accurate than a CNN. The next big problem came to training a model. Downloading the entire COCO dataset was over 30 GB, and the dataset would then need to be put in a special format to be passed to YOLO. I spent 2 months trying to figure out how to create this model. I tried numerous libraries, searched all over the internet, but none of the guides made any sense. I then found Ultralytics, an API with a pretrained YOLOv8 (newest/fastest version of YOLO) on the COCO dataset.
The Ultralytics API was a lifesaver; it detected multiple different classes with high accuracy. Here's a photo of the model on an Adobe stock video:
At first, I had all my code in one large file. This method made the program very hard to read and view, so I split the code into classes: a Detector class, which detects and classifies objects; a TrafficLight class, which controls the traffic light and contains the algorithm for controlling lights; and a Motor class, which controlls the stepper motor.
Next, I created a algorithm for controlling the traffic light. The algorithm is as follows:
Here is the algorithm I coded for my TrafficLight class:
python
def set_traffic_lights(self):
# Case 1: All sides have less than 2 cars; intersection will act as a 4-way stop.
if self.traffic_dataframe.iloc[-1, 1] <= 2 and self.traffic_dataframe.iloc[-1, 2] <= 2 and self.traffic_dataframe.iloc[-1, 3] <= 2 and self.traffic_dataframe.iloc[-1, 4] <= 2:
# turn all greens and yellows off; all reds blink to signal 4-way stop
print("Less than 2 cars for each side; intersection functioning as a 4 way stop.")
self.set_flash_red('Street 1')
self.set_flash_red('Street 2')
# Case 2: some sides have more than 2 cars, so the ratio between the 2 streets will be calculated using the calculate_traffic_ratio function.
# That ratio will then be used to determine the timing of the lights.
else:
ratio = self.calculate_traffic_ratio(self.traffic_dataframe.iloc[-1])
# Case A: Street 2 has more traffic than Street 1; Street 2 gets green light priority, unless Street 2 has had a green light for more than 3 minutes.
if ratio < 1:
if not(self.last_street_change_time()):
self.set_red_light('Street 1')
self.set_green_light('Street 2')
print("Street 2 has more traffic than Street 1; Street 2 gets green light priority")
else:
self.set_red_light('Street 2')
self.set_green_light('Street 1')
print("Street 2 has had green light for more than 3 minutes, so Street 1 gets red light.")
# Case B: Street 1 has more traffic than Street 2; Street 1 gets green light priority, unless Street 1 has had a green light for more than 3 minutes.
elif ratio > 1:
if not (self.last_street_change_time()):
# Set street 1 lights (green[0] and green[2]) to green and street 2 lights to red.
self.set_red_light('Street 2')
self.set_green_light('Street 1')
print("Street 1 has more traffic than Street 2; Street 1 gets green light priority.")
else:
# Set street 2 lights (green[1] and green[3]) to green and street 1 lights to red.
self.set_red_light('Street 1')
self.set_green_light('Street 2')
print("Street 1 has had green light for more than 3 minutes, so Street 2 gets red light.")
else:
# The streets have equal traffic amounts.
print("The streets have equal traffic amounts.")
# If traffic_dataframe row is first row, randomly select street to get green light priority
current_row_index = self.traffic_dataframe.index.get_loc(self.traffic_dataframe.index[-1])
if current_row_index == 0:
random_street = random.randint(1, 2)
if random_street == 1:
print("No existing data; street 1 randomly selected to receive green light priority")
# Street 1 gets green light priority
green_light_time = self.get_green_light_time('Side 1')
# Set green and red lights
self.set_red_light('Street 2')
self.set_green_light('Street 1')
else:
print("No existing data; street 2 randomly selected to receive green light priority")
# Street 2 gets green light priority
green_light_time = self.get_green_light_time('Side 2')
# Set green and red lights
self.set_red_light('Street 1')
self.set_green_light('Street 2')
# Else, give green light priority to the last street to have red light.
else:
print("The last street to get the green gets a red light.")
street_green = self.get_last_green()
self.set_red_light(street_green)
if street_green == 'Street 1':
self.set_green_light('Street 2')
print("Street 2 gets a green light.")
else:
self.set_green_light('Street 1')
print("Street 1 gets a green light.")
self.traffic_dataframe.to_csv('Traffic_Data',index=False,mode='w')
The last line saves the Pandas dataframe to a csv. I plan to eventually replace the algorithm above with a deep learning model that can better make predictions, but I first need to collect a lot of training data from this light.