《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