This is an overview of my final year thesis project for my Computer Engineering degree at the University of Cyprus. The goal of my project was to develop a solar-powered device that counts the number of vehicles passing in each direction through a specific point on the university campus in real time and periodically upload them to a cloud database for remote access. The solution is camera based so as to not interfere with the road infrastructure or the flow of traffic.
This is still a work in progress.
The hardware I chose to run my software on is a Raspberry Pi 5 4GB running Raspberry Pi OS, with a connected 720p USB webcam. I chose this platform because it is widely used and thus has good documentation and custom software (the OS for example) that guarantee a good level of compatibility with drivers.
The 2GB version of the Raspberry Pi 5 will also be able to run this software without any problems.
For the software to run properly and detect the direction vehicles are moving in, you will first need to adjust some parameters on a scene-by-scene basis:
POINT_A and POINT_B are the 2 directions vehicles will be moving towards. If we assume the road is horizontal to the camera's frame then point A will be at the left of the frame and point B at the right. You can use the road's dividing line as a reference.
CROP_RECT is a 4 element array where you can specify how many pixels from each side (top, bottom, left, right) you want to remove from the frame before you start processing it. You can use it to remove parking spaces that may confuse the algorithm, or unimportant parts of the scene.
During startup, the program checks if a camera is connected to the system, and if the remote MySQL database is online and can accepts the provided credentials. (see picture below)
Firstly, the device captures a frame using the connected camera and crops it based on the configuration. After that, the cropped frame is given to the YOLOv8 Nano machine vision model which outputs the bounding boxes for each element that belongs to a vehicle class (car, truck, bus, motorcycle). Each of these bounding boxes is called a "detection".
Following that, the software uses a Kalman filter to predict the current position of previously tracked vehicles based on their last known positions and velocities. Then the program uses a Greedy IoU (Intersection over Union) algorithm to match the detections from the current frame with the predicted positions of existing tracks, thus identifying a vehicle in the present frame with itself in the last frame and and maintaining the same ID.
If a track cannot be matched to any detection (meaning no vehicle was detected where it was predicted to be), the program continues to update the Kalman filter using predicted positions for a set number of frames until the object is marked as lost and the track is deleted. If a detection doesn't match any existing track, the program creates a new track object and assigns a unique ID to it.
To classify the direction a track is moving towards, the software needs to have tracked an object for a set number of frames. When that number is reached, the program calculates the projection of the track's position onto the line between POINT_A and POINT_B and analyzes how this projection has changed over time to determine if the vehicle has been moving towards POINT_A or POINT_B. When a track has been classified and added to the counter, it is flagged as counted to prevent double-counting. The bounding box is initially teal colored for objects that have not been classified as moving left or right. When the program determines the direction, it updates the bounding box color for that object to red if it moves to the left and green if it moves to the right.
If enabled in the configuration, the program will periodically upload the number of vehicles it has counted to the online MySQL database specified in the configuration. Each upload creates a new record in the database table, consisting of a UTC timestamp, the number of vehicles moving towards POINT_A, and the number of vehicles moving towards POINT_B. After each upload, the counters are reset to zero for the next period.
To achieve my targets for performance and power draw I had to tweak the software and hardware and make some compromises. Here is a list of the settings I used:
Disabled Bluetooth
Disabled PCIe slot
Disabled audio
Disabled both HDMI ports
Underclocked CPU to 2000MHz
Undervolted CPU using the -2 preset
Underclocked GPU to 400MHz
Set CPU governor to "conservative"
Fused the YOLOv8 Nano model
Used memory pre-allocation
Skip rendering of unseen objects
Removing unimportant parts of the frame instead of just masking them
These optimizations improved the performance by 35% while simultaneously reducing power consumption by 37%.
To power this device and keep it 100% self sufficient I opted to use a solar + battery solution. The final configuration I decided to use is a 100W solar panel mounted at 45° tilt angle facing south and a 600Wh 12V lead-acid battery. To verify my proposed setup I used the data from a residential solar installation as well as the Photovoltaic Geographical Information System (PVGIS). The results showed that even in the worst case scenario the system should be self sufficient for at least 3 winter days.
The device has a measured average power draw of 5.8W which results in a daily energy consumption of 140Wh. Below is the proposed setup:
This setup is meant to be as simple as possible. Charging stops automatically when the battery reaches 13.5V which is well within the safe limit for a leadacid battery. The device has priority over charging the battery by default. The only drawback is that there is no way to stop the battery from over-discharging which is not catastrophic but can damage the battery if done too often.
Using a prerecorded video as a standardized test case, the software managed a 96.2% detection and classification accuracy.