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);
  }
}
                    

Music Player