基于超声波的人数统计系统-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 3154|回复: 0

基于超声波的人数统计系统

[复制链接]
发表于 2020-6-15 10:41 | 显示全部楼层 |阅读模式
    做这个项目的目的是由于受疫情影响,封闭区域的人流密度需要得到有效控制,降低感染病毒的风险。因此使用了M5Stack来统计并显示人数,同时接入两个超声波传感器通过判断触发的先后顺序来确定进门还是出门。下图是统计的基本原理
passingDoor.png
   使用方法:
把它放在门上之后,你只需按照菜单的提示来进行设置。
•是否激活区域自动计数
如果已经设置好了区域面积,那么你可以选择是否开启自动计算人数上限功能
optionFalse.png optionTrue.png
•设置区域面积
此选项可以设置场所的区域面积
areaLimit.png areaMenu0.png areaMenu30.png
•计数显示
该菜单将显示当前人数,如果自动计数被激活,它将显示一个人员限制。如果激活区域自动计数,但设置的区域面积不合适,则此菜单被锁定
counter.png counterWithoutLimit.png counterLimit0.png counterLimit3.png counterPopup.png
•距离菜单
此项将显示传感器的检测到的距离
distance.png distanceMenu.png distanceMenuError.png    
接线方式
pinMap.png
程序

[mw_shl_code=arduino,true]#include<M5Stack.h>

// sensors pin
const int trigPin = 22;
const int echoPin = 21;
const int trigPin2 = 16;
const int echoPin2 = 17;

// Variables
  // Distance tmp
float dist, dist2;
float oldDist, oldDist2;
  // room area
  
long area = 0 ;
  // timer loop -> display update
unsigned long timerLoop = millis();
  // To know which menu is highlighted and selected
int select[4];
int buttonSelect = 0;
int menu = 0;
  // To know which action is done
int action; //action {0, 1, 2, 3} -> {None, buttonA, buttonB, buttonC}
  // some usefull boolean
bool refresh = false;
bool aeraAutoCount = false;
bool majDistance = false;
  // Peoples number count
unsigned int peoplesNumber = 0;

// area int
int digits[] = {0,1,2,3,4,5,6,7,8,9};
  // iD[0] = 10e3, iD[1] = 10e2, iD[2] = 10e1, iD[3] = 10e0
int indexDigits[] = {0,0,0,0};
  // to know which digit has to be modify
int modifDigit = 0;
int selectDigits[] = {1,0,0,0};

  // define the perfect gray color <3
#define GRAY  BLUE+0.3*RED

// distance function working with the ultrasonic sensors
// write by ltombak
double distance(int triggerPin, int echoPin){
  
  float SOUND_SPEED = 340.0 / 1000; // son speed in mm/uS
  unsigned long MEASURE_TIMEOUT = 25000UL; // 25ms = ~8m at 340m/s
  
  digitalWrite(triggerPin, LOW);// checking if the sensor is low
  delayMicroseconds(5);
  digitalWrite(triggerPin, HIGH);// Wake up the sensor for 10uS
  delayMicroseconds(10);
  digitalWrite(triggerPin, LOW);// Switch off

  long measure = pulseIn(echoPin, HIGH, MEASURE_TIMEOUT);// measuring ultrasonic impulsion time and echo (if exist)
  float distance_cm = measure / 2.0 * SOUND_SPEED;// convert echo time to distance

  if (distance_cm < 1 || distance_cm > 3000) {// checking if the distance is ok
        return -1.0 ;// if not, sending error value
    } else {
        return (distance_cm / 10.0);// sending cm distance
    }
}

// update the distance with the two sensors
void takeDistance(){
  dist = 0;
  dist2 = 0;
  for(int i=0;i<5;i++){
    dist += distance(trigPin, echoPin);
    delay(5);
    dist2 += distance(trigPin2, echoPin2);
    delay(5);
  }
  dist /= 5;
  dist2 /= 5;
}

// draw a button, start at x and y, with w (width), h (height),
//              backC : Background color
//              textC : Text Color
//              btn1 : String displayed on the sensor
void drawButton(int x, int y, int w, int h, uint16_t backC, uint16_t textC , String btn1, bool center, bool highlight){
  M5.Lcd.setTextColor(textC);
  if (highlight){
    M5.Lcd.fillRoundRect(x-2, y-2, w+4 , h+4, 12,WHITE);
  }
  M5.Lcd.fillRoundRect(x, y, w , h, 10,backC);
  if (center){
    M5.Lcd.setCursor(x+int(w/2)-int(btn1.length()*6) , y+8);
  }
  else {
    M5.Lcd.setCursor(x+5 , y+8);
  }
  
  M5.Lcd.print(btn1);
}

// drawing the action button legend on the buttom screen
void drawButtonSection(String btn1, String btn2, String btn3){
  M5.Lcd.setTextSize(2);
  drawButton(35,205,70,30,GRAY,WHITE,btn1,true,false);
  drawButton(128,205,70,30,GRAY,WHITE,btn2,true,false);
  drawButton(222,205,70,30,GRAY,WHITE,btn3,true,false);
}

// reseting the select variable to enable loop in menu navigation -> 0 -> 1 -> 2 -> 3 -> 0 ...
void resetSelect(){
  for(int i=0;i<4;i++){
    select=0;
  }
}

// Menu Function
String btnName1, btnName2, btnName3;

// Preparing the highlight boolean array
// telling wich button has to be highlitgh
void nextButton(){
  buttonSelect += 1;
  resetSelect();
  if(buttonSelect >=4){
    buttonSelect = 0;
  }
  select[buttonSelect]=1;
}

// area calcul, all the digits to a number : [0 0 3 0] -> 30
void MAJarea() {
  area = digits[indexDigits[0]]*1000 + digits[indexDigits[1]]*100 + digits[indexDigits[2]]*10 + digits[indexDigits[3]];
}

// Lock the counter menu acces if you chose a area limited counter
// but without entering a area value
void popuparea(){
  M5.Lcd.setTextSize(2);
  drawButton(30,30,270,140,GRAY,RED,String(""),true,true);
  drawButton(40,85,250,30,GRAY,RED,String("! AREA INVALID !"),true,false);
  // option button section
  btnName1 = String("");
  btnName2 = String("Ok");
  btnName3 = String("");
  
  drawButtonSection( btnName1, btnName2, btnName3);
  while(not(M5.BtnB.wasReleased())){
    M5.update();  
  }
  return;
}

// First menu, Option Menu
void menuOption (){
  // action selection {none, btnA, btnB, btnC}
  switch(action){
    case 1: // btnA : Next button
      // Highlight the next button
      nextButton();
      break;
    case 2: // btnB : OK
      switch(buttonSelect){
        case 0: // activate the auto count of peoples
          aeraAutoCount = not(aeraAutoCount);
          break;
        case 1: // Go to area menu
          action = 0;
          menu = 2;
          menuarea();
          return;
         
        case 2: // Go to the counter
          action = 0;
          if (aeraAutoCount and area < 10){
            // if the area is invalid
            // we return to option screen
            popuparea();
            menu = 0;
            buttonSelect = 0;
            nextButton();
            break;
          }
          menu = 3;
          menuCounter();
          return;
        case 3: // Go to the distance menu
          menu = 4;
          action = 0;
          menuDistance();
          return;
        default:
          break;
      }
    default:
      break;
  }
  // Background
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setTextSize(3);
  M5.Lcd.setCursor(100, 10);
  M5.Lcd.print("OPTION:");
  M5.Lcd.setTextSize(2);
  // drawing button section
  drawButton(20,40,220,30,GRAY,WHITE,String("Auto counter:"),false,select[0]==1);
    // display auto count bolean
  if (aeraAutoCount){
    drawButton(165,40,70,30,GRAY,GREEN,String("true "),false,false);
  }
  else {
    drawButton(165, 40,70,30,GRAY,RED,String("false"),false,false);
  }
  drawButton(20,80,220,30,GRAY,WHITE,String("area limit..."),false,select[1]==1);
  drawButton(20,120,220,30,GRAY,WHITE,String("Counter..."),false,select[2]==1);
  drawButton(20,160,220,30,GRAY,WHITE,String("Distance..."),false,select[3]==1);

  // option button section
  btnName1 = String("Next");
  btnName2 = String("Ok");
  btnName3 = String("");
  
  drawButtonSection( btnName1, btnName2, btnName3);

}

// tell wich digits is selected, highlighted and has to be modify
void digitSelection(){
  modifDigit += 1;
  for (int i=0;i<4;i++){
    selectDigits = 0;
  }
  if (modifDigit>3){
    modifDigit=0;
  }
  selectDigits[modifDigit] = 1;
}

// area menu
void menuarea(){
  majDistance = true;
  // action selection {none, btnA, btnB, btnC}
  switch(action){
    case 0:
      break;
    case 1: // btnA : highlight next digit
      digitSelection();
      break;
    case 2: // btnB : add one to the selected digit
      indexDigits[modifDigit] +=1;
      if(indexDigits[modifDigit]>9){
        indexDigits[modifDigit]=0;
      }
      break;
    case 3: // Return to the option menu btnC
      menu = 0;
      action = 0;
      menuOption();
      return;
    default:
      break;
  }
  // Background
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setTextSize(3);
  M5.Lcd.setCursor(100, 10);
  M5.Lcd.print("AREA:");
  // Writing peoples number
  M5.Lcd.setTextSize(2);
  M5.Lcd.setCursor(10, 50);
  M5.Lcd.println("Shop area");
  MAJarea();
  M5.Lcd.setTextSize(5);
  for (int i=0;i<4;i++){   
    drawButton(30+int(i*40),75,35,50,BLACK,WHITE,String(digits[indexDigits]),false,selectDigits==1);
  }
  
  M5.Lcd.setCursor(200, 80);
  M5.Lcd.print("m");
  M5.Lcd.setTextSize(3);
  M5.Lcd.setCursor(230, 75);
  M5.Lcd.print("2");

  // option button section
  btnName1 = String("Next");
  btnName2 = String("+1");
  btnName3 = String("Back");
  
  drawButtonSection( btnName1, btnName2, btnName3);
}

// Counter Menu
void menuCounter(){
  // Activate the Distance display update
  majDistance = true;
  // action selection {none, btnA, btnB, btnC}
  switch(action){
    case 0:
      break;
    case 1: // btnA : reset
      peoplesNumber = 0;
      break;
    case 2: // btnB : add one people
      peoplesNumber += 1;
      break;
    case 3: // return to the option menu btnC
      menu = 0;
      action = 0;
      menuOption();
      return;
    default:
      break;
  }
  // Background
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setTextSize(3);
  M5.Lcd.setCursor(90, 10);
  M5.Lcd.print("COUNTER:");
  // Writing peoples number
  M5.Lcd.setTextSize(2);
  M5.Lcd.setCursor(10, 50);
  M5.Lcd.println("peoples in the shop");

  // testing if a people limit is set
  if (aeraAutoCount and area > 10){
    displayCountValue();
    M5.Lcd.setCursor(180,100);
    M5.Lcd.print("/");
    M5.Lcd.print(int(area / 10));
    M5.Lcd.setTextColor(WHITE);
  }
  else {
    M5.Lcd.setTextColor(WHITE);
    displayCountValue();
  }
  // option button section
  btnName1 = String("Reset");
  btnName2 = String("+1");
  btnName3 = String("Back");
  
  drawButtonSection( btnName1, btnName2, btnName3);
}

// Updating the count value display
void displayCountValue(){
  compteurDetection();
  M5.Lcd.setTextSize(5);
  M5.Lcd.fillRect(10, 90, 150, 50, BLACK);
  M5.Lcd.setCursor(10,90);
  if (peoplesNumber > 999){
    peoplesNumber=0;
  }
  // write in red if we aproch or reach the limit
  if(peoplesNumber >= int(area / 10)-1) {
    M5.Lcd.setTextColor(RED);
  }
  M5.Lcd.print(peoplesNumber);
    M5.Lcd.setTextColor(WHITE);
}

// Updating the distance display
void displayDistValue(){
  M5.Lcd.setTextSize(5);
  M5.Lcd.fillRect(10, 75, 300, 50, BLACK);
  M5.Lcd.setCursor(10, 75);
  if(dist < 0){
    M5.Lcd.print("Error");
  }
  else{
    M5.Lcd.print(dist);
    M5.Lcd.print(" cm");
  }
  M5.Lcd.fillRect(10, 150, 300, 50, BLACK);
  M5.Lcd.setCursor(10,150);
  if(dist2 < 0){
    M5.Lcd.print("Error");
  }
  else{
    M5.Lcd.print(dist2);
    M5.Lcd.print(" cm");
  }
}

// Distance menu
void menuDistance(){
  majDistance = true;
  // action selection {none, btnA, btnB, btnC}
  switch(action){
    case 0:
      break;
    case 1: // btnA
      break;
    case 2: // btnB
      break;
    case 3: // retun to the option menu btnC
      menu = 0;
      action = 0;
      menuOption();
      return;
    default:
      break;
  }
  // Background
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setTextSize(3);
  M5.Lcd.setCursor(90, 10);
  M5.Lcd.print("DISTANCE:");
  // Writing peoples numbe
    // text
  M5.Lcd.setTextSize(2);
  M5.Lcd.setCursor(5, 50);
  M5.Lcd.print("Sensor 1 :");
  M5.Lcd.setCursor(5, 125);
  M5.Lcd.print("Sensor 2 :");
    // value
  displayDistValue();
  // option button section
  btnName1 = String("");
  btnName2 = String("");
  btnName3 = String("Back");
  
  drawButtonSection( btnName1, btnName2, btnName3);
}

// refresh dpisplay each 500ms
void distanceMajTimerLoop(){
  if (majDistance){ // if the maj distance is asked
    if (micros() - timerLoop > 500){ //and the timer is out the time loop
      switch(menu){
        case 3: // counter menu
          displayCountValue();// refresh people count value on the screen
          break;
        case 4: // Distance menu
          displayDistValue(); // refresh value on the screen
          break;
        default:
        break;
      }
      timerLoop = micros(); // reset the timer
    }
  }

}

// detect the peoples direction add tell if
// they are entering or exiting
void compteurDetection(){
  if(oldDist-dist > 20){ // input detection
    while ( oldDist2-dist2 < 21 and oldDist-dist > 20){
      M5.Lcd.setTextSize(2);
      M5.Lcd.fillRect(90, 150, 100, 50, BLACK);
      takeDistance();
      if (oldDist2-dist2 > 20 ){
        peoplesNumber += 1;
        return;
      }
      delay(2);
    }
  }
  else if(oldDist2-dist2 > 20){ // output detection
      M5.Lcd.setTextSize(2);
    while ( oldDist-dist < 21 and oldDist2-dist2 > 20){
      M5.Lcd.fillRect(90, 150, 100, 50, BLACK);
      takeDistance();
      if (oldDist-dist > 20 ){
        peoplesNumber -= 1;
        return;
      }
      delay(2);
    }
  }
  return;
}


void setup() {
  M5.begin();
  // pinmod for ultrasonic sensors
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(trigPin2, OUTPUT);
  pinMode(echoPin2, INPUT);
  // init to the first selection button
  select[0] = 1;
  menu = 0;
  // display the option menu
  menuOption();
}

void loop() {
  M5.update();
  oldDist = dist;
  oldDist2 = dist2;

  // updating distance
  takeDistance();
  
  if (refresh){
    switch(menu){
      case 0: // Option menu
        majDistance = false;
        menuOption();
        
        break;
      case 1: // area limite menu
        majDistance = false;
        break;
      case 2: // area modif menu
        majDistance = false;
        menuarea();
        break;
      case 3: // counter menu
        menuCounter();
        break;
      case 4: // Distance menu
        menuDistance();
        break;
      default:
        menu = 0;
        break;
    }
    refresh = false;
  }

  // action button section
  action = 0;
  if (M5.BtnA.wasReleased()) {
    action = 1;
    refresh = true;
  }
  if (M5.BtnB.wasReleased()) {
    action = 2;
    refresh = true;
  }
  if (M5.BtnC.wasReleased()) {
    action = 3;
    refresh = true;
     
  }
  distanceMajTimerLoop();
}[/mw_shl_code]

GitHub链接:https://github.com/JonathanCourtois/M5Stack-Counter
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-12-1 00:56 , Processed in 0.124918 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表