Zumo Robot Object Detection & Counting
Developed an algorithm for Zumo robot navigation, obstacle detection (IR/Reflectance), and counting within a defined area.
Project Abstract
This project focuses on the details of a system designed to diversify the usage of the Zumo robot, which is developed on an Arduino board. The system aims to enhance the performance exhibited by the robot in specific scenarios. Towards this goal, specific algorithms were incorporated to ensure the effective operation of the project. The uniqueness of the created algorithm is a notable advantage. The preferred code approach allows the system to detect, count, and notify the user of desired obstacles in a defined area using an infrared sensor, LED, and a buzzer, thereby increasing its functionality.
Methodology & Proposed Solution
The project aimed to enable the Zumo robot to count different numbers of obstacles within a circular area limited by white tape and notify the user via LED and buzzer signals. The core challenge was to develop an algorithm that could accurately scan the area and count objects, especially when objects might be lined up or difficult to distinguish from a single viewpoint.
The proposed solution involved the following steps:
- Calibration: The robot starts with a calibration phase for its reflectance sensors.
- Movement & Edge Detection: The robot moves forward until it detects the white boundary of the circular arena. If an obstacle is detected by the IR sensor during forward movement, the robot turns left to avoid it and then continues.
- Scanning & Object Counting: Upon reaching the white edge, the robot performs a turn (left or right based on which side detected the edge). During this turn (the "detectionPeriod"), any objects detected by the IR sensor are counted.
- Trial Repetition: This process of moving to the edge and scanning is repeated for a set number of trials (NUM_TRIALS = 10 in the code) from different points along the circle's edge to improve accuracy by observing objects from multiple perspectives.
- Result Determination: After all trials, the maximum object count recorded in any single trial is taken as the final result.
- Notification: The robot signals the final count by blinking an LED and sounding a buzzer for a number of times equal to the counted objects.
Visuals & Experiments
Images of the Zumo robot during testing and experimentation.


Full Project Report
The detailed methodology, experimental setup, results, and discussion are documented in the full project report, embedded below:
Conclusion & Future Works
The project successfully implemented a Zumo robot capable of navigating its circular environment, detecting white boundaries, avoiding obstacles, and counting objects using IR and reflectance sensors. The system provided effective feedback through LED and buzzer signals. The algorithm demonstrated good results in various scenarios, though challenges were noted with objects placed very close to the arena edge. Future work could involve refining the edge detection logic, improving sensor fusion for more robust object discrimination, and potentially adding more sophisticated scanning patterns.
#include <ZumoMotors.h>
#include <ZumoReflectanceSensorArray.h>
#include <ZumoBuzzer.h>
#define PIN_IR 6
#define PIN_LED 13
#define SENSORS_COUNT 6
#define THRESHOLD_WHITE 550 // Adjust based on sensor readings for white
#define NUM_TRIALS 10
ZumoMotors motors;
ZumoReflectanceSensorArray sensors;
ZumoBuzzer buzzer;
unsigned int sensorReadings[SENSORS_COUNT];
int dataValues[NUM_TRIALS];
int trialsCompleted = 0;
int objectCount = 0;
bool detectionPeriod = false;
void setup() {
Serial.begin(9600);
pinMode(PIN_LED, OUTPUT);
pinMode(PIN_IR, INPUT);
//Start with open led to show you are calibrating
digitalWrite(PIN_LED, HIGH);
delay(3000);
digitalWrite(PIN_LED, LOW);
delay(3000);
//Calibration start
sensors.init();
for (int i = 0; i < 400; i++) {
sensors.calibrate();
if (i % 50 == 0) {
Serial.print("Calibrating");
}
}
Serial.println("\nCalibration complete.");
buzzer.play("!L16 V10 cdegreg4"); // Signal end of calibration
delay(2000); // Put me to the black surface...
//Blink one time fastly to declare you are starting
digitalWrite(PIN_LED, HIGH);
delay(500);
digitalWrite(PIN_LED, LOW);
}
void loop() {
if (trialsCompleted < NUM_TRIALS) {
moveUntilWhiteDetected();
evaluateWhiteDetection();
} else {
int result = getMaxCount();
delay(2000);
blinkAndBuzz(result);
motors.setSpeeds(0, 0); // Stop the robot
while(true); // Halt further actions
}
}
void moveUntilWhiteDetected() {
while (true) {
sensors.readLine(sensorReadings);
if (digitalRead(PIN_IR) == LOW) { // Check if the IR sensor detects an object
while (digitalRead(PIN_IR) == LOW) { // Turn left until the IR sensor no longer detects anything
motors.setSpeeds(-150, 150); // Turn left
}
motors.setSpeeds(0, 0); // Stop turning once the object is no longer detected
}
else if (isOnBlackSurface()) { // Continue moving forward if on black surface and no object detected
motors.setSpeeds(100, 100); // Move forward
} else {
motors.setSpeeds(0, 0); // Stop motors if white is detected
break;
}
}
}
bool isOnBlackSurface() {
for (int i = 0; i < SENSORS_COUNT; i++) {
if (sensorReadings[i] < THRESHOLD_WHITE) {
return false; // White detected
}
}
return true; // No white detected
}
void evaluateWhiteDetection() {
if (sensorReadings[0] < THRESHOLD_WHITE || sensorReadings[1] < THRESHOLD_WHITE || sensorReadings[2] < THRESHOLD_WHITE) {
// Left sensors detected white, turn right
detectionPeriod = true;
turnRight();
} else if (sensorReadings[3] < THRESHOLD_WHITE || sensorReadings[4] < THRESHOLD_WHITE || sensorReadings[5] < THRESHOLD_WHITE) {
// Right sensors detected white, turn left
detectionPeriod = true;
turnLeft();
}
}
void turnLeft() {
while (!isLeftGroupWhite()) {
motors.setSpeeds(-150, 155); // Continue turning left
checkForObject(); // Check for objects and count if detected
}
motors.setSpeeds(0, 0); // Stop turning
delay(200); // Small delay to ensure complete stop
dataValues[trialsCompleted++] = objectCount; // Store the count
objectCount = 0; // Reset count
detectionPeriod = false; // End detection period
while (!isRightGroupWhite()){
motors.setSpeeds(155, -150);
}
// Turn right for a brief period
motors.setSpeeds(-130, 130);
delay(200); // Turn right for 200ms
motors.setSpeeds(0, 0); // Stop turning
}
void turnRight() {
detectionPeriod = true; // This was missing in the original turnRight, added for consistency
while (!isRightGroupWhite()) {
motors.setSpeeds(155, -150); // Continue turning right
checkForObject(); // Check for objects and count if detected
}
motors.setSpeeds(0, 0); // Stop turning
delay(1000); // Small delay to ensure complete stop (was 1000, can be adjusted)
dataValues[trialsCompleted++] = objectCount; // Store the count
objectCount = 0; // Reset count
detectionPeriod = false; // End detection period
while (!isLeftGroupWhite()){
motors.setSpeeds(-150, 155);
}
// Turn left for a brief period
motors.setSpeeds(130, -130);
delay(200); // Turn left for 200ms
motors.setSpeeds(0, 0); // Stop turning
}
bool isLeftGroupWhite() {
sensors.readLine(sensorReadings);
return sensorReadings[0] < THRESHOLD_WHITE || sensorReadings[1] < THRESHOLD_WHITE || sensorReadings[2] < THRESHOLD_WHITE;
}
bool isRightGroupWhite() {
sensors.readLine(sensorReadings);
return sensorReadings[3] < THRESHOLD_WHITE || sensorReadings[4] < THRESHOLD_WHITE || sensorReadings[5] < THRESHOLD_WHITE;
}
void checkForObject() {
if (detectionPeriod && digitalRead(PIN_IR) == LOW) {
objectCount++;
delay(200); // Debounce delay
}
}
int getMaxCount() {
int maxCount = 0;
for (int i = 0; i < trialsCompleted; i++) {
if (dataValues[i] > maxCount) {
maxCount = dataValues[i];
}
}
return maxCount;
}
void blinkAndBuzz(int count) {
for (int i = 0; i < count; i++) {
digitalWrite(PIN_LED, HIGH);
buzzer.playFrequency(2000, 500, 5); // Frequency, duration, volume
delay(500);
digitalWrite(PIN_LED, LOW);
delay(500);
}
}