Smart management of the study rooms through computer vision

Smart management of the Università della Calabria's study rooms using DFRobot AI Starter EEDU Kit to detect people through computer vision.

IntermediateFull instructions provided16 hours249
Smart management of the study rooms through computer vision

Things used in this project

Hardware components

DFRobot EEDU Kit for Arduino AI Starter
×1

Software apps and online services

Arduino IoT Cloud
Arduino IoT Cloud
Arduino IDE
Arduino IDE

Story

Read more

Schematics

Schematics.fzz

Schematics in Fritzing format

FireBeetle Fritzing part

HuskyLens-Vision-Sensor Fritzing part

Code

RoomCounter.ino

C/C++
The ino file
/***************************************************
 
 Use of the Hackster & DFRobot AI Starter EEDU Kit and Arduino Cloud to detect and count how many people are in a room at any given time through vision applications.
  
 Created 2023-June
 By [Yunior Cabrera](https://yvcabrerago.com/)
 
 This code is in the public domain.

 ****************************************************/

#include "arduino_secrets.h"
#include "thingProperties.h"
#include "HUSKYLENS.h"
#include <LinkedList.h>

// meta parameters
#define MIN_DIST 100                    // used to adjust the min distance to asociate objects
#define FRAME_TO_REMOVE 10              // used to adjust the min number of frame to remove object

int inFrameCounter = 0;

// object to save the points detected
class PointTracked
{
	public:
    int id;
    int x;
    int y;
    int refresh_counter;
    bool already_visited;
    char direction;
};

HUSKYLENS huskylens;
//HUSKYLENS green line >> SDA; blue line >> SCL
int ID1 = 1; //first learned results, colored result on HUSKYLENS screen

void printResult(HUSKYLENSResult result);
double euclidianDistance(int x0, int y0, int x1, int y1);

LinkedList<PointTracked*> myLinkedList = LinkedList<PointTracked*>();

void setup() {
  // Initialize serial:
  Serial.begin(115200);
  
  delay(1500); 
  
  // Defined in thingProperties.h
  initProperties();

  // Connect to Arduino IoT Cloud
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  
  /*
     The following function allows you to obtain more information
     related to the state of network and IoT Cloud connection and errors
     the higher number the more granular information you’ll get.
     The default is 0 (only errors).
     Maximum is 4
 */
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();

  Wire.begin();
  while (!huskylens.begin(Wire)){
    Serial.println(F("Begin failed!"));
    Serial.println(F("1.Please recheck the \"Protocol Type\" in HUSKYLENS (General Settings>>Protocol Type>>I2C)"));
    Serial.println(F("2.Please recheck the connection."));
    delay(100);
  }

  peopleCounter = 0;
  huskylens.writeAlgorithm(ALGORITHM_OBJECT_RECOGNITION);
}

void loop() {
  ArduinoCloud.update();
  // Your code here 
  delay(200);
  if (huskylens.requestBlocksLearned()){    //request blocks tangged ID != ID0 from HUSKYLENS
    if (huskylens.count(ID1) > 0){
      if (inFrameCounter == 0){     // the room is empty
        Serial.println("List is empty");
        // we put all points in the linked list
        for (int i = 0; i < huskylens.count(ID1); i++){
          HUSKYLENSResult result = huskylens.get(ID1, i);
          printResult(result);

          PointTracked *p = new PointTracked();
          p->id = inFrameCounter;
          p->x = result.xCenter;
          p->y = result.yCenter;
          p->direction = '0';     // 0 = new point, we don't have the direction yet
          p->already_visited = true;
          p->refresh_counter = 0;
          myLinkedList.add(p);
          inFrameCounter++;
          Serial.print("There are ");Serial.print(myLinkedList.size());Serial.println(" points being tracked");
        }
      } 
      else{
        Serial.print("There are ");Serial.print(myLinkedList.size());Serial.println(" points being tracked");
        for (int i = 0; i < huskylens.count(ID1);i++){
          HUSKYLENSResult result = huskylens.get(ID1, i);
          printResult(result);
          int new_point = -1;
          double min_distance = 300;  //  a large number
          for (int j=0; j< myLinkedList.size();j++){
            PointTracked *p = myLinkedList.get(j);
            double dist = euclidianDistance(p->x, p->y, result.xCenter, result.yCenter);
            Serial.print("Distance from point ");Serial.print(p->id);Serial.print(" to actual point = ");Serial.println(dist);
            if (dist <= min_distance){
              min_distance = dist;
              new_point = j;
            }
          }
        
          if (min_distance <= MIN_DIST){ // is the distance is less than MIN_DIST, then is the same point
            PointTracked *p = myLinkedList.get(new_point);
            if (p->x < result.xCenter){
              p->direction = 'r';         // based on the position of the centroid, can detect the direction of movement.
            }
            else{
              p->direction = 'l';
            }
            p->x = result.xCenter;
            p->y = result.yCenter;
            p->already_visited = true;
            Serial.print("Point ");Serial.print(p->id);Serial.print(" is updated, is moving to ");Serial.println(p->direction);
            p->refresh_counter = 0;
          }
          else{        // is a new point
            PointTracked *p = new PointTracked();
            p->id = inFrameCounter;
            p->x = result.xCenter;
            p->y = result.yCenter;
            p->direction = '0';
            p->already_visited = true;
            p->refresh_counter = 0;
            myLinkedList.add(p);
            inFrameCounter++;
          }
        }
      }
    }
  }
  else{
    Serial.println("Fail to request objects from Huskylens!");
  }
  
  // checking for object leaving the room
  for (int j=0; j< myLinkedList.size();j++){
    PointTracked *p = myLinkedList.get(j);
    if (p->already_visited == false){
      p->refresh_counter++;
    }
    p->already_visited = false;
    if (p->refresh_counter == FRAME_TO_REMOVE){   // the object is removed because left the room
      Serial.print("Point removed ");Serial.print(p->id);Serial.print(" was moving to ");Serial.println(p->direction);
      myLinkedList.remove(j);
      if (p->direction == 'r'){    // we asume the right is the entrance and the left the exit
        peopleCounter++;
      } 
      else{
        peopleCounter--;
      }
      inFrameCounter--;
    }
  }
}

// function to calculate euclidian distance (https://en.wikipedia.org/wiki/Euclidean_distance)
double euclidianDistance(int x0, int y0, int x1, int y1){
  return sqrt(abs((x0-x1)*(x0-x1)+(y0-y1)*(y0-y1)));
}

// useful function to debug in serial port
void printResult(HUSKYLENSResult result){
    if (result.command == COMMAND_RETURN_BLOCK){//result is a block
        Serial.println(String()+F("Block:xCenter=")+result.xCenter+F(",yCenter=")+result.yCenter+F(",width=")+result.width+F(",height=")+result.height+F(",ID=")+result.ID);
    }
    else{//result is unknown.
        Serial.println("Object unknown!");
    }
}

Arduino_Secrets.h

C/C++
The secret to connect to WiFi network and Arduino Cloud
#define SECRET_SSID "put your network name here"
#define SECRET_OPTIONAL_PASS "put your network password here"
#define SECRET_DEVICE_KEY "put your Arduino Cloud Key here"

ThingProperties.h

C/C++
Configuration to use with Arduino Cloud
// Code generated by Arduino IoT Cloud

#include <ArduinoIoTCloud.h>
#include <Arduino_ConnectionHandler.h>

const char DEVICE_LOGIN_NAME[]  = "put your device id here";

const char SSID[]               = SECRET_SSID;    // Network SSID (name)
const char PASS[]               = SECRET_OPTIONAL_PASS;    // Network password (use for WPA, or use as key for WEP)
const char DEVICE_KEY[]  = SECRET_DEVICE_KEY;    // Secret device password


int peopleCounter;                              // the cloud variable

void initProperties(){

  ArduinoCloud.setBoardId(DEVICE_LOGIN_NAME);
  ArduinoCloud.setSecretDeviceKey(DEVICE_KEY);
  ArduinoCloud.addProperty(peopleCounter, READ, ON_CHANGE, NULL);

}

WiFiConnectionHandler ArduinoIoTPreferredConnection(SSID, PASS);

Credits

Yunior Vicente Cabrera González

Yunior Vicente Cabrera González

4 projects • 18 followers
Telecommunications and Electronics Engineer. I am passionate about designing electronic circuits and systems, as well as front-end software.
Danelis Garrido Guillan

Danelis Garrido Guillan

0 projects • 12 followers
Telecommunications and Electronics Engineer

Comments