【Arduino 动手做】Esghati:一个由垃圾制成的机器人

在这里插入图片描述

《Arduino 手册(思路与案例)》栏目介绍:
在电子制作与智能控制的应用领域:广泛涉及了Arduino BLDC、Arduino CNC、Arduino ESP32 SPP、Arduino FreeRTOS、Arduino FOC、Arduino GRBL、Arduino HTTP、Arduino HUB75、Arduino IoT Cloud、Arduino JSON、Arduino LCD、Arduino OLED、Arduino LVGL、Arduino PID 及 Arduino TFT 等方面的相关拓展思路和众多参考案例。本专栏目前博客近2300篇。
https://blog.csdn.net/weixin_41659040/category_12422453.html

Arduino是一个开放源码的电子原型平台,它可以让你用简单的硬件和软件来创建各种互动的项目。Arduino的核心是一个微控制器板,它可以通过一系列的引脚来连接各种传感器、执行器、显示器等外部设备。Arduino的编程是基于C/C++语言的,你可以使用Arduino IDE(集成开发环境)来编写、编译和上传代码到Arduino板上。Arduino还有一个丰富的库和社区,你可以利用它们来扩展Arduino的功能和学习Arduino的知识。

Arduino的特点是:
1、开放源码:Arduino的硬件和软件都是开放源码的,你可以自由地修改、复制和分享它们。
2、易用:Arduino的硬件和软件都是为初学者和非专业人士设计的,你可以轻松地上手和使用它们。
3、便宜:Arduino的硬件和软件都是非常经济的,你可以用很低的成本来实现你的想法。
4、多样:Arduino有多种型号和版本,你可以根据你的需要和喜好来选择合适的Arduino板。
5、创新:Arduino可以让你用电子的方式来表达你的创意和想象,你可以用Arduino来制作各种有趣和有用的项目,如机器人、智能家居、艺术装置等。

在这里插入图片描述

Esghati 是一款基于 ESP32-CAM 和 Arduino nano 的非常简单的机器人。我为学校的学生制作了它,这样他们就可以自己制作,并认为与世界各地的其他孩子分享它会很有趣。它超级便宜、超级简单且极具教育意义。

该项目的制作方式是,每一步您都会学习制作一个单独的小工具。首先,您将学习制作移动电源,下一步您将 ESP32-CAM 添加为具有人脸识别功能的摄像头,然后添加移动机构和身体,使其成为机器人。

那么它是如何工作的呢?这个机器人不需要控制器,只需要你的手机或笔记本电脑或任何带有 Web 浏览器的东西。您可以使用任何设备连接到 Esghati 的 wifi,您可以通过它的摄像头看到并使用浏览器控制它。它还具有人脸识别功能!

我想向我的学生展示,很酷的东西是由激情创造的,不花钱购买花哨的机器人零件就可以完成,我还希望为他们所有人提供一个平等的竞争平台,无论他们的家庭财富如何。所以所有的身体部位都是直接来自垃圾的。

在这里插入图片描述

材料与用品

身体部位没什么特别的,只是我身边的一些垃圾。你可以用你的想象力来设计你的机器人的身体。它可以是一个破损的玩具,或者一个手工制作的纸板机器人,或者像我这样完全无关紧要的东西。我使用了四个坏掉的 LED 灯和一个旧的 wifi 路由器外壳作为机身。

电气部件包括:
1 个 ESP32-CAM
1x Arduino Nano (根据您的模块购买 USB 数据线)
2 节锂离子电池 18650 3.7V,至少 2000 mAh
2x 母针接头 2.54mm 2 排 40 针 直角
1x FT232RL Mini USB 转 TTL 串口转换器适配器模块 3.3V/5.5V
1 个 ON/OFF 开关
1 个移动电源模块,带 2 个 USB 输出
1 x 公 USB 转 USB 电缆(购买前请阅读文章)
2x MG996R 360 度舵机(建议使用金属变速箱)
一些电线

工具:
使用 3 毫米钻头钻孔
迷你角磨机或钢锯
烙铁
焊锡丝和油
螺丝刀
热胶枪
统治者
金属剪刀
刀具

第 1 步:底盘和车轮

我会告诉你我为 Esghati 的身体所做的一切,以防你想制作精确的机器人。

车轮:
首先,我们从 LED PCB 后面切割两个相同尺寸的 LED 灯。你最后应该得到像照片这样的东西。然后将 LED PCB 中间的伺服手柄连接起来。断开灯泡与 PCB 部分的连接,并开始从内部粘合 PCB 的角落,以获得额外的保护。将它们放在一边晾干,然后进行下一步。

基础:

底座由旧的破损 wifi 路由器制成,因此请拿起螺丝刀打开路由器并移除所有电子设备。我保持天线完好无损,因为它看起来很酷,但如果你愿意,你可以把它取下来。现在您需要为机器人的前部选择路由器的一侧。选择一侧后,开始测量伺服系统,然后根据测量值开始切割路由器的两侧。现在将伺服器安装在路由器中,如图所示。您可以使用热胶或伺服系统随附的螺钉,不用说始终建议使用螺钉。

您可能希望稍后安装轮子,因为安装其他没有轮子的部件更容易。我将它们连接起来以进行演示。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第 2 步:移动电源

布线:

接线非常简单,先将电池并联在一起,然后将负极连接到移动电源的负极。然后用中间的开/关开关将它们的正极连接在一起。

蒙太奇:

现在是时候将我们的移动电源连接到机箱了。首先,仔细测量移动电源模块端口,在机身的任何一侧打一些适合它们的孔,然后将电池和模块粘在机箱上。我亲自将它们连接到机器人的顶部前部。然后测量开/关开关并在机箱上为其留出一个位置。建议您将所有组件粘在机箱的一侧,以便可以轻松地继续其余的蒙太奇作。

最后,确保您的移动电源电池电量指示器通过机箱可见,无论是 LED 指示灯还是 LCD 指示灯。现在您可以继续下一步。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第 3 步:上半身

我们需要一个更大的灯泡和一个较小的上半身灯泡。从大灯泡上取下灯泡,剪断盖子,如图所示插入第二个小盖子。现在,您可以切割或钻孔较小的灯泡帽的底部,以便电线可以进入槽。您可以将这两个部分首尾相连,但不要将它们连接到机箱。

拿一块垃圾板金属,做一个至少 12 厘米长、3.5 厘米宽的 T 形支架。然后根据较小灯泡的底座弯曲 T 型支架的底部,并将其拧到小灯泡的顶部。

接下来,将轮子上剩余的灯泡体连接到机器人眼睛的 T 形支架的顶部。

最后,将大灯泡从底座上切成两半,并将其底部粘在机箱上,以便将上半身安装在其上。我这样做是为了随时可以拆下上半身,而且它也可以牢牢地安装在机箱上。但仍然不要将上半身连接到底盘。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第 4 步:ESP32-CAM 支架

现在我们需要两个 8 行 2.54mm 2 行 40 针直角 将 ESP32-CAM 安装在 T 型支架上。首先,将内排排针连接到模块,使排针彼此指向。然后将外引脚粘在 T 型支架上,不要打扰内引脚,因为我们需要它们连接到模块。

一旦胶水固定好,您就可以开始为电子设备布线了。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第 5 步:电子元件

在这里,您需要决定是要使用 USB 端口为整个系统供电,还是要将电源线直接焊接到移动电源 PCB 上的 USB 引脚。我将电线焊接到 PCB 上,因为它更干净,您还可以使用额外的 USB 端口来连接其他电子设备,甚至为一些手机和小工具充电。

Arduino 适合 wifi 路由器机箱,不需要特殊支架,只是不要将电线直接焊接到上面;使用 pin 接头。现在一切都已连接,我们可以继续下一步。

在这里插入图片描述

第 6 步:项目代码

我基于这个项目开发了 Esghati 的平台并添加了更多功能。我在 GitHub 上上传了该项目的源代码,单击此处重定向。

#include <SoftwareSerial.h>
#include <VarSpeedServo.h> 

SoftwareSerial mySerial(4, 3); 

VarSpeedServo  Rwheel;
VarSpeedServo  Lwheel;

int VSPEED = 5 , HSPEED = 10 , MSPEED = 50, MACCEL = 125;
int MAX_VSPEED = 10 , MAX_HSPEED = 20 , MAX_MSPEED = 255, MSTOP = 1500, MSTOPdeg = 90;

String command;
bool commandEnd=false;
char inByte;
int arg = 0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  mySerial.begin(9600);

  Rwheel.attach(6);
  delay(100);
  Rwheel.write(MSTOP);
  delay(300);

  Lwheel.attach(5);
  delay(100);
  Lwheel.write(MSTOP);
  delay(300);
}

void loop() {
  // put your main code here, to run repeatedly:
  if (mySerial.available() > 0) {

      if(commandEnd){
      command="";
      commandEnd=false;
    }
      inByte = mySerial.read();
      if (inByte != '\n') {
        if (inByte == '=') {
          arg = mySerial.parseInt();
          //Serial.println(arg);      
        } else {
          command += inByte;
        }
      }else{
        commandEnd=true;
        Serial.println(command);
        execute(command, arg);
      }
  }
    
}

void execute(String command, int arg){
if(compareString(command, "l-rotate")){
      Rwheel.write(MSTOPdeg-MSPEED,MACCEL);
      Lwheel.write(MSTOPdeg-MSPEED,MACCEL);
      command="";
    }else if(compareString(command, "r-rotate")){
      Rwheel.write(MSTOPdeg+MSPEED,MACCEL);
      Lwheel.write(MSTOPdeg+MSPEED,MACCEL);
      command="";
    }else if(compareString(command, "f-forward")){
      Rwheel.write(MSTOPdeg-MSPEED, MACCEL);
      Lwheel.write(MSTOPdeg+MSPEED, MACCEL);
      command="";
    }else if(compareString(command, "f-left")){
      Rwheel.write(MSTOPdeg-MSPEED,MACCEL);
      Lwheel.write(MSTOP);
      command="";
    }else if(compareString(command, "f-right")){
      Rwheel.write(MSTOP);
      Lwheel.write(MSTOPdeg+MSPEED,MACCEL);
      command="";
    }else if(compareString(command, "b-backward")){
      Rwheel.write(MSTOPdeg+MSPEED,MACCEL);
      Lwheel.write(MSTOPdeg-MSPEED,MACCEL);
      command="";
    }else if(compareString(command, "b-right")){
      Rwheel.write(MSTOPdeg+MSPEED,MACCEL);
      Lwheel.write(MSTOP);
      command="";
    }else if(compareString(command, "b-left")){
      Rwheel.write(MSTOP);
      Lwheel.write(MSTOPdeg-MSPEED,MACCEL);
      command="";
    }else if(compareString(command, "stop")){
      Rwheel.write(MSTOP);
      Lwheel.write(MSTOP);
      command="";
    }
    command="";
}

boolean compareString(String a, String b) {
  if (a.length() != b.length() + 1) {
    return false;
  }
  for (int i = 0; i < a.length() - 1; i++) {
    if (a[i] != b[i]) {
      return false;
    }
  }
  return true;
}
#include <ArduinoWebsockets.h>
#include "esp_http_server.h"
#include "esp_timer.h"
#include "esp_camera.h"
#include "camera_index.h"
#include "Arduino.h"
#include "fd_forward.h"
#include "fr_forward.h"
#include "fr_flash.h"
#include "fb_gfx.h"

const char* ssid = "your ssid";
const char* password = "your password";

#define ENROLL_CONFIRM_TIMES 5
#define FACE_ID_SAVE_NUMBER 7
#define LED_BUILTIN 4
bool INT_LED = false;
hw_timer_t *My_timer = NULL;
// Select camera model
//#define CAMERA_MODEL_WROVER_KIT
//#define CAMERA_MODEL_ESP_EYE
//#define CAMERA_MODEL_M5STACK_PSRAM
//#define CAMERA_MODEL_M5STACK_WIDE
#define CAMERA_MODEL_AI_THINKER
#include "camera_pins.h"

using namespace websockets;
WebsocketsServer socket_server;

camera_fb_t * fb = NULL;

long current_millis;
long last_detected_millis = 0;

#define RED 15 // pin 12 can also be used
#define GREEN 14
#define BLUE 2
#define Mic 12
int light = 0;
unsigned long door_opened_millis = 0;
long interval = 5000;           // open lock for ... milliseconds
bool face_recognised = false;

const int numReadings = 5;
int readings[numReadings];      // the readings from the analog input
int readIndex = 0;
int total = 0;                  // the running total
int average_face_size = 0;      // the average
int face_distance;
int face_id = 0;
String command;
bool commandEnd=false;
char inByte;
int arg = 0;
bool follow = 0;

//void app_facenet_main();
//void app_httpserver_init();

typedef struct
{
  uint8_t *image;
  box_array_t *net_boxes;
  dl_matrix3d_t *face_id;
} http_img_process_result;


static inline mtmn_config_t app_mtmn_config()
{
  mtmn_config_t mtmn_config = {0};
  mtmn_config.type = FAST;
  mtmn_config.min_face = 80;
  mtmn_config.pyramid = 0.707;
  mtmn_config.pyramid_times = 4;
  mtmn_config.p_threshold.score = 0.6;
  mtmn_config.p_threshold.nms = 0.7;
  mtmn_config.p_threshold.candidate_number = 20;
  mtmn_config.r_threshold.score = 0.7;
  mtmn_config.r_threshold.nms = 0.7;
  mtmn_config.r_threshold.candidate_number = 10;
  mtmn_config.o_threshold.score = 0.7;
  mtmn_config.o_threshold.nms = 0.7;
  mtmn_config.o_threshold.candidate_number = 1;
  return mtmn_config;
}
mtmn_config_t mtmn_config = app_mtmn_config();

face_id_name_list st_face_list;
static dl_matrix3du_t *aligned_face = NULL;

httpd_handle_t camera_httpd = NULL;

typedef enum
{
  START_STREAM,
  START_DETECT,
  SHOW_FACES,
  START_RECOGNITION,
  START_ENROLL,
  ENROLL_COMPLETE,
  DELETE_ALL,
} en_fsm_state;
en_fsm_state g_state;

typedef struct
{
  char enroll_name[ENROLL_NAME_LEN];
} httpd_resp_value;

httpd_resp_value st_name;
void IRAM_ATTR onTimer(){
  Serial.println("task");
}
void setup() {
  Serial.begin(9600);
  Serial.setDebugOutput(true);
  Serial.println();

  pinMode (LED_BUILTIN, OUTPUT);
  pinMode(RED, OUTPUT);
  pinMode(GREEN, OUTPUT);
  pinMode(BLUE, OUTPUT);
  pinMode(33, OUTPUT); 
  pinMode(Mic, INPUT);
  digitalWrite(33, HIGH);
  digitalWrite(RED, LOW);
  digitalWrite(GREEN, LOW);
  digitalWrite(BLUE, LOW);
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 10000000;
  config.pixel_format = PIXFORMAT_JPEG;
  //init with high specs to pre-allocate larger buffers
  if (psramFound()) {
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

#if defined(CAMERA_MODEL_ESP_EYE)
  pinMode(13, INPUT_PULLUP);
  pinMode(14, INPUT_PULLUP);
#endif

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }

  sensor_t * s = esp_camera_sensor_get();
  s->set_framesize(s, FRAMESIZE_QVGA);

#if defined(CAMERA_MODEL_M5STACK_WIDE)
  s->set_vflip(s, 1);
  s->set_hmirror(s, 1);
#endif

  WiFi.softAP(ssid, password);
  IPAddress IP = WiFi.softAPIP();
  Serial.print("ACCESS POINT IP address: ");
  Serial.println(IP);

  app_httpserver_init();
  app_facenet_main();
  socket_server.listen(82);

  Serial.print("Camera Ready! Use 'http://");
  Serial.print(WiFi.localIP());
  Serial.println("' to connect");

  My_timer = timerBegin(0, 80, true);
  timerAttachInterrupt(My_timer, &onTimer, true);
  timerAlarmWrite(My_timer, 300000000, true);
  timerAlarmEnable(My_timer);
}

static esp_err_t index_handler(httpd_req_t *req) {
  httpd_resp_set_type(req, "text/html");
  httpd_resp_set_hdr(req, "Content-Encoding", "gzip");
  return httpd_resp_send(req, (const char *)index_ov2640_html_gz, index_ov2640_html_gz_len);
}

httpd_uri_t index_uri = {
  .uri       = "/",
  .method    = HTTP_GET,
  .handler   = index_handler,
  .user_ctx  = NULL
};

void app_httpserver_init ()
{
  httpd_config_t config = HTTPD_DEFAULT_CONFIG();
  if (httpd_start(&camera_httpd, &config) == ESP_OK)
    Serial.println("httpd_start");
  {
    httpd_register_uri_handler(camera_httpd, &index_uri);
  }
}

void app_facenet_main()
{
  face_id_name_init(&st_face_list, FACE_ID_SAVE_NUMBER, ENROLL_CONFIRM_TIMES);
  aligned_face = dl_matrix3du_alloc(1, FACE_WIDTH, FACE_HEIGHT, 3);
  read_face_id_from_flash_with_name(&st_face_list);
}

static inline int do_enrollment(face_id_name_list *face_list, dl_matrix3d_t *new_id)
{
  ESP_LOGD(TAG, "START ENROLLING");
  int left_sample_face = enroll_face_id_to_flash_with_name(face_list, new_id, st_name.enroll_name);
  ESP_LOGD(TAG, "Face ID %s Enrollment: Sample %d",
           st_name.enroll_name,
           ENROLL_CONFIRM_TIMES - left_sample_face);
  return left_sample_face;
}

static esp_err_t send_face_list(WebsocketsClient &client)
{
  client.send("delete_faces"); // tell browser to delete all faces
  face_id_node *head = st_face_list.head;
  char add_face[64];
  for (int i = 0; i < st_face_list.count; i++) // loop current faces
  {
    sprintf(add_face, "listface:%s", head->id_name);
    client.send(add_face); //send face to browser
    head = head->next;
  }
}

static esp_err_t delete_all_faces(WebsocketsClient &client)
{
  delete_face_all_in_flash_with_name(&st_face_list);
  client.send("delete_faces");
}

void handle_message(WebsocketsClient &client, WebsocketsMessage msg)
{
  timerWrite(My_timer, 0); //reset timer (feed watchdog)
  //Serial.println(msg.data());
  if (msg.data() == "stream") {
    g_state = START_STREAM;
    client.send("STREAMING");
  }else if (msg.data() == "detect") {
    g_state = START_DETECT;
    client.send("DETECTING");
  }else if (msg.data().substring(0, 8) == "capture:") {
    g_state = START_ENROLL;
    char person[FACE_ID_SAVE_NUMBER * ENROLL_NAME_LEN] = {0,};
    msg.data().substring(8).toCharArray(person, sizeof(person));
    memcpy(st_name.enroll_name, person, strlen(person) + 1);
    client.send("CAPTURING");
  }else if (msg.data() == "recognise") {
    g_state = START_RECOGNITION;
    client.send("RECOGNISING");
  }else if (msg.data().substring(0, 7) == "remove:") {
    char person[ENROLL_NAME_LEN * FACE_ID_SAVE_NUMBER];
    msg.data().substring(7).toCharArray(person, sizeof(person));
    delete_face_id_in_flash_with_name(&st_face_list, person);
    send_face_list(client); // reset faces in the browser
  }else if (msg.data().substring(0, 5) == "tilt:") {
    //int i = msg.data().substring(5).toInt();
    Serial.print("tilt=");
    Serial.println(msg.data().substring(5));
  }else if (msg.data().substring(0, 4) == "pan:") {
    //int j = msg.data().substring(4).toInt();
    Serial.print("pan=");
    Serial.println(msg.data().substring(4));
  }else if (msg.data().substring(0, 9) == "lr-rhand:") {
    //int i = msg.data().substring(5).toInt();
    Serial.print("lr-rhand=");
    Serial.println(msg.data().substring(9));
  }else if (msg.data().substring(0, 9) == "ud-rhand:") {
    //int j = msg.data().substring(4).toInt();
    Serial.print("ud-rhand=");
    Serial.println(msg.data().substring(9));
  }else if (msg.data().substring(0, 9) == "lr-lhand:") {
    //int i = msg.data().substring(5).toInt();
    Serial.print("lr-lhand=");
    Serial.println(msg.data().substring(9));
  }else if (msg.data().substring(0, 9) == "ud-lhand:") {
    //int j = msg.data().substring(4).toInt();
    Serial.print("ud-lhand=");
    Serial.println(msg.data().substring(9));
  }else if (msg.data() == "stop") {
    Serial.println("stop");
  }else if (msg.data() == "r-rotate") {
    Serial.println("r-rotate");
  }else if (msg.data() == "l-rotate") {
    Serial.println("l-rotate");
  }else if (msg.data() == "f-left") {
    Serial.println("f-left");
  }else if (msg.data() == "f-forward") {
    Serial.println("f-forward");
  }else if (msg.data() == "f-right") {
    Serial.println("f-right");
  }else if (msg.data() == "b-left") {
    Serial.println("b-left");
  }else if (msg.data() == "b-backward") {
    Serial.println("b-backward");
  }else if (msg.data() == "b-right") {
    Serial.println("b-right");
  }else if (msg.data() == "introduce") {
    Serial.println("introduce");
  }else if (msg.data() == "greet") {
    Serial.println("greet");
  }else if (msg.data() == "find" && g_state == START_RECOGNITION) {
    Serial.println("find");
  }else if (msg.data() == "follow") {
    
    if(!follow){
        Serial.println("follow");
        follow = true;
    }else{
      Serial.println("nofollow");
      follow = false;
    }
    
  }else if (msg.data() == "dance") {
    Serial.println("dance");
  }else if (msg.data() == "random") {
    Serial.println("random");
  }else if (msg.data() == "history") {
    Serial.println("history");
  }else if (msg.data() == "hymn") {
    Serial.println("hymn");
  }else if (msg.data() == "hreset") {
    Serial.println("hreset");
  }else if (msg.data() == "reset") {
    Serial.println("reset");
  }else if (msg.data() == "light") {
    if(INT_LED){
      digitalWrite(LED_BUILTIN, LOW);
      INT_LED = false;
    }else{
      digitalWrite(LED_BUILTIN, HIGH);
      INT_LED = true;
    }
    
    Serial.println("light");
  }else if (msg.data() == "rgb") {
    if(light == 0){
      digitalWrite(RED, HIGH);
      digitalWrite(GREEN, LOW);
      digitalWrite(BLUE, LOW);
      light++;
      }else if(light == 1){
      digitalWrite(RED, LOW);
      digitalWrite(GREEN, HIGH);
      digitalWrite(BLUE, LOW);
      light++;
      }else if(light == 2){
      digitalWrite(RED, LOW);
      digitalWrite(GREEN, LOW);
      digitalWrite(BLUE, HIGH);
      light++;
      }else{
      digitalWrite(RED, LOW);
      digitalWrite(GREEN, LOW);
      digitalWrite(BLUE, LOW);
      light=0;
      }
    Serial.println("rgb");
  }else if (msg.data() == "fire") {
    Serial.println("fire");
  }else if (msg.data() == "voicemode") {
    Serial.println("voicemode");
  }else if (msg.data() == "clearcache") {
    Serial.println("free heap: ");
    Serial.println(ESP.getFreeHeap());
  }else if (msg.data() == "seq1") {
    Serial.println("seq1");
  }else if (msg.data() == "seq2") {
    Serial.println("seq2");
  }else if (msg.data() == "seq3") {
    Serial.println("seq3");
  }else if (msg.data() == "seq4") {
    Serial.println("seq4");
  }
  
}

void open_door(WebsocketsClient &client) {
  if (true/*digitalRead(relay_pin) == LOW*/) {
    //digitalWrite(relay_pin, HIGH); //close (energise) relay so door unlocks
    Serial.println("sorush");
    client.send("door_open");
    door_opened_millis = millis(); // time relay closed and door opened
  }
}
static void draw_face_boxes(dl_matrix3du_t *image_matrix, box_array_t *boxes)
{
  int x, y, w, h, i, half_width, half_height;
  fb_data_t fb;
  fb.width = image_matrix->w;
  fb.height = image_matrix->h;
  fb.data = image_matrix->item;
  fb.bytes_per_pixel = 3;
  fb.format = FB_BGR888;
  for (i = 0; i < boxes->len; i++) {

    // Convoluted way of finding face centre...
    x = ((int)boxes->box[i].box_p[0]);
    w = (int)boxes->box[i].box_p[2] - x + 1;
    half_width = w / 2;
    int face_center_pan = x + half_width; // current face centre x co-ordinate

    y = (int)boxes->box[i].box_p[1];
    h = (int)boxes->box[i].box_p[3] - y + 1;
    half_height = h / 2;
    int face_center_tilt = y + half_height;  // current face centre y co-ordinate

    // subtract the last reading:
    total = total - readings[readIndex];
    // add current face height:
    readings[readIndex] = h;
    // add the reading to the total:
    total = total + readings[readIndex];
    // advance to the next position in the array:
    readIndex = readIndex + 1;

    // if we're at the end of the array...
    if (readIndex >= numReadings) {
      // ...wrap around to the beginning:
      readIndex = 0;
    }

    // calculate the average:
    average_face_size = total / numReadings;

    int eq_top = 3.6 * 200 * 240; //f(mm) x real height(mm) x image height(px)
    int eq_bottom = average_face_size * 2.7; //object height(px) x sensor height(mm)
    int face_distance = eq_top / eq_bottom;

    /*Serial.print('<'); // start marker
    Serial.print(face_center_pan);
    Serial.print(','); // comma separator
    Serial.print(face_center_tilt);
    Serial.print(','); // comma separator
    Serial.print(face_distance);
    Serial.println('>'); // end marker*/
    Serial.print("autopan=");
    Serial.println(face_center_pan);
    Serial.print("autotilt=");
    Serial.println(face_center_tilt);
    Serial.print("distance=");
    Serial.println(face_distance);

  }
}
void loop() {
  
  auto client = socket_server.accept();
  client.onMessage(handle_message);
  dl_matrix3du_t *image_matrix = dl_matrix3du_alloc(1, 320, 240, 3);
  http_img_process_result out_res = {0};
  out_res.image = image_matrix->item;

  send_face_list(client);
  client.send("STREAMING");


  while (client.available()) {
    client.poll();

    // char voice[12];
    //   sprintf(voice, "voice:%s", (const char *)analogRead(12));
    //   client.send(voice);
    //   Serial.println(analogRead(12));
    //   client.sendBinary((const char *)analogRead(12));

    if (Serial.available()) {
      
    String command = Serial.readStringUntil('\n');
    if (command == "red") {
      digitalWrite(RED, HIGH);
      digitalWrite(GREEN, LOW);
      digitalWrite(BLUE, LOW);
    } else if (command == "green") {
      digitalWrite(RED, LOW);
      digitalWrite(GREEN, HIGH);
      digitalWrite(BLUE, LOW);
    } else if (command == "blue") {
      digitalWrite(RED, LOW);
      digitalWrite(GREEN, LOW);
      digitalWrite(BLUE, HIGH);
    } else if (command == "norgb") {
      digitalWrite(RED, LOW);
      digitalWrite(GREEN, LOW);
      digitalWrite(BLUE, LOW);
    }
  }

    if (millis() - interval > door_opened_millis) { // current time - face recognised time > 5 secs
      //digitalWrite(relay_pin, LOW); //open relay
    }

    fb = esp_camera_fb_get();

    if (g_state == START_DETECT || g_state == START_ENROLL || g_state == START_RECOGNITION)
    {
      out_res.net_boxes = NULL;
      out_res.face_id = NULL;

      fmt2rgb888(fb->buf, fb->len, fb->format, out_res.image);

      out_res.net_boxes = face_detect(image_matrix, &mtmn_config);

      if (out_res.net_boxes)
      {
        if (align_face(out_res.net_boxes, image_matrix, aligned_face) == ESP_OK)
        {

          out_res.face_id = get_face_id(aligned_face);
          last_detected_millis = millis();
          if (g_state == START_DETECT) {
            client.send("FACE DETECTED");
          }
          if(follow){
            draw_face_boxes(image_matrix, out_res.net_boxes);
        }
          if (g_state == START_ENROLL)
          {
            int left_sample_face = do_enrollment(&st_face_list, out_res.face_id);
            char enrolling_message[64];
            sprintf(enrolling_message, "SAMPLE NUMBER %d FOR %s", ENROLL_CONFIRM_TIMES - left_sample_face, st_name.enroll_name);
            client.send(enrolling_message);
            if (left_sample_face == 0)
            {
              ESP_LOGI(TAG, "Enrolled Face ID: %s", st_face_list.tail->id_name);
              g_state = START_STREAM;
              char captured_message[64];
              sprintf(captured_message, "FACE CAPTURED FOR %s", st_face_list.tail->id_name);
              client.send(captured_message);
              send_face_list(client);

            }
          }

          if (g_state == START_RECOGNITION  && (st_face_list.count > 0))
          {
            face_id_node *f = recognize_face_with_name(&st_face_list, out_res.face_id);
            if (f)
            {
              char recognised_message[64];
              sprintf(recognised_message, "Hi %s", f->id_name);
              open_door(client);
              client.send(recognised_message);
            }
            else
            {
              client.send("FACE NOT RECOGNISED");
              Serial.println("stranger");
            }
          }
          dl_matrix3d_free(out_res.face_id);
        }
          free(out_res.net_boxes->score);  // Free allocated memory
          free(out_res.net_boxes->box);
          free(out_res.net_boxes->landmark);
          free(out_res.net_boxes);
      }
      else
      {
        if (g_state != START_DETECT) {
          client.send("NO FACE DETECTED");
        }
      }

      if (g_state == START_DETECT && millis() - last_detected_millis > 500) { // Detecting but no face detected
        client.send("DETECTING");
      }

    }

    client.sendBinary((const char *)fb->buf, fb->len);

    esp_camera_fb_return(fb);
    fb = NULL;
  }
}
boolean compareString(String a, String b) {
  if (a.length() != b.length() + 1) {
    return false;
  }
  for (int i = 0; i < a.length() - 1; i++) {
    if (a[i] != b[i]) {
      return false;
    }
  }
  return true;
}

ESP32-CAM 系列:

首先,您需要从开发板管理器安装 ESP32 1.0.4 版本(或其他版本)。然后安装所有缺少的库。将您的板子连接到 USB 到 TTL 转换器。将 GPIO0 连接到 GND 以启用引导模式。从工具中选择 AI Thinker ESP32-CAM 板,然后选择 Huge APP 作为您的分区方案。最后输入 wifi 凭据,选择您的 COM 端口并开始上传草图。

注意:您可能会遇到诸如 guru meditation 或 brownout detector was triggered 之类的错误。它们通常是由电缆连接质量差或电力短缺引起的。为了消除错误,请将板连接到外部电源并确保连接良好。此外,将地线连接到系统和电源也非常重要。

注意:建议您使用 5V 引脚旁边的 GND 引脚为模块供电。

现在,您可以测试您的模块,退出引导模式并重新启动模块。然后用手机或笔记本电脑连接到 ESP32-CAM,并在 chrome 浏览器中输入 192.168.4.1。您应该会看到如图所示的控制面板。灯光和人脸识别功能可以立即进行测试。

Arduino Nano:

首先安装所有库并从板管理器中选择 arduino nano。选择 COM 并单击 upload (上传)。如果您在上传处理器类型时遇到问题。当处理器设置为 ATmega328P Old Bootloader 时,我可以对我的 arduino nanos 进行编程。

现在您可以将轮子连接到框架上,两个在伺服系统上,一个在后面,以保持平衡。现在我们已经完成了这个项目!

享受你的机器人。

在这里插入图片描述
在这里插入图片描述

【Arduino 动手做】Esghati:一个由垃圾制成的机器人
项目链接:https://www.instructables.com/Esghati-a-Robot-Made-From-Garbage/
项目作者:伊朗 索拉什·莫拉迪萨尼(Sorush Moradisani)
项目视频(1分钟):https://www.youtube.com/watch?v=d39NgJqNWr8&t=57s
项目开源代码:https://github.com/Sorush-moradisani/Esghati
参考:https://github.com/robotzero1/esp32cam-access-control

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

驴友花雕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值