esp32 cam+esp8266用micropython实现人脸识别开门

2024-02-24 18:10

本文主要是介绍esp32 cam+esp8266用micropython实现人脸识别开门,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

前面文章讲了编译micropython的编译和图传,这篇记录一下我自己DIY人脸识别门锁的经验。

为什么用esp8266,因为比esp32便宜几块钱,批发甚至只要6块,哈哈

由于micropython我也刚上手,也是学一点记录一点,当帮大家提前踩坑了~

废话不多说,直接上例子!

---------------------------------------------------------------------------------------------------------------------------------

千万不要被标题骗了,光单片机是达不到人脸识别的,性能肯定不够。那么我们需要一个服务器来对人脸进行比对,我用的是自己的一个arm服务器。80多买的n1盒子,刷的centos,1.5G 4线程处理器对于我个人还是够用。如果有同学需要,我也可以单独出一篇我的服务器搭建踩坑经历。当然你也可以用你的pc来当服务器,性能绝对够用。如何使用wsl刷入Ubuntu,我前面也有说,就不废话了。

一、服务器环境搭建

我用的是centos 7,经过实验,Ubuntu的操作差不多。由于是使用centos,gcc版本肯定不够。那么需要升级gcc版本,可以升级到7或9,我就是这个坑浪费了一个多小时。

python3安装(都使用micropython了,肯定都有py3,不废话了)

centos升级:

# yum安装gcc7$ yum install devtoolset-7-gcc*# 用gcc7自带的脚本添加到环境变量$ scl enable devtoolset-7 bash# 查看gcc版本$ gcc -v

Ubuntu不用升级gcc,自带最新的

安装boost、cmake、git

centos:

$ yum install -y boost,cmake,git

Ubuntu

$ sudo apt-get install -y git,cmake,libboost-all-dev

编译dlib(我们用的人脸识别依赖这个库)

# 克隆dlib源代码
$ git clone https://github.com/davisking/dlib.git$ cd dlib
$ mkdir build
$ cd build#这一部分是使用硬件加速的,如果硬件支持,人脸识别是很快的
$ cmake .. -DDLIB_USE_CUDA=1 -DUSE_AVX_INSTRUCTIONS=1
$ cmake --build .(注意中间有个空格)
$ cd ..#python安装dlib
$ python3 setup.py install

重点!!!!!

编译dlib库建议空闲内存4G以上,不够可以临时使用swap,不然会编译失败

最后安装face_recognition

# 安装 face_recognition
$ pip3 install face_recognition

至此,我们的人脸识别环境就搭建好了

可以用一下代码测试是否安装成功

# 准备两个文件夹,一个是参照图片,一个是要识别的图片$ face_recognition ./file1/ ./file2/

能识别出就会显示第一个文件夹内的照片的名字。

二、创建人脸识别接口

我们先使用以下代码生成自己的人脸数组

import face_recognition# 打开你的图片文件
img = face_recognition.load_image_file('face.jpg')
# 将人脸编码成素组
face_encodings = face_recognition.face_encodings(img)# 打印出的就是你的人脸数组,复制到下面的代码中,也可以保存到数据库(只需要list,不要将整个tuple都复制)
print(face_encodings)

使用python 的socket库来监听端口,当文件传入的时候进行识别

from face_recognition import load_image_file, face_encodings, compare_faces
import time
import json
import hashlib
import binascii
import os
import socket
import iodef detect_faces_in_image(file_stream):# 人脸数组face_list = [[[-0.09634063,  0.12095481, -0.00436332, -0.07643753,  0.0080383,0.01902981, -0.07184699, -0.09383309,  0.18518871, -0.09588896,0.23951106,  0.0986533 , -0.22114635, -0.1363683 ,  0.04405268,0.11574756, -0.19899382, -0.09597053, -0.11969153, -0.12277931,0.03416885, -0.00267565,  0.09203379,  0.04713435, -0.12731361,-0.35371891, -0.0503444 , -0.17841317, -0.00310897, -0.09844551,-0.06910533, -0.00503746, -0.18466514, -0.09851682,  0.02903969,-0.02174894,  0.02261871,  0.0032102 ,  0.20312519,  0.02999607,-0.11646006,  0.09432904,  0.02774341,  0.22102901,  0.26725179,0.06896867, -0.00490024, -0.09441824,  0.11115381, -0.22592428,0.06230862,  0.16559327,  0.06232892,  0.03458837,  0.09459756,-0.18777156,  0.00654241,  0.08582542, -0.13578284,  0.0150229 ,0.00670836, -0.08195844, -0.04346499,  0.03347827,  0.20310158,0.09987706, -0.12370517, -0.06683611,  0.12704916, -0.02160804,0.00984683,  0.00766284, -0.18980607, -0.19641446, -0.22800779,0.09010898,  0.39178532,  0.18818057, -0.20875394,  0.03097027,-0.21300618,  0.02532415,  0.07938635,  0.01000703, -0.07719778,-0.12651891, -0.04318593,  0.06219772,  0.09163868,  0.05039065,-0.04922386,  0.21839413, -0.02394437,  0.06173781,  0.0292527 ,0.06160797, -0.15553983, -0.02440624, -0.17509389, -0.0630486 ,0.01428208, -0.03637431,  0.03971229,  0.13983178, -0.23006812,0.04999552,  0.0108454 , -0.03970895,  0.02501768,  0.08157793,-0.03224047, -0.04502571,  0.0556995 , -0.24374914,  0.25514284,0.24795187,  0.04060191,  0.17597422,  0.07966681,  0.01920104,-0.01194376, -0.02300822, -0.17204897, -0.0596558 ,  0.05307484,0.07417042,  0.07126575,  0.00209804],'奥巴马']]# 加载上载的图像文件img = load_image_file(file_stream)# 获取上传图像中任何人脸的人脸编码unknown_face_encodings = face_encodings(img)is_name = 'unknow'# 图片编码成功if len(unknown_face_encodings) > 0:# 查看上传图像中的第一张面孔是否与已知面孔匹配for face_who in face_list:match_results = compare_faces([face_who[0]], unknown_face_encodings[0])if match_results[0]:is_name = face_who[1]else:print('图片编码失败')return is_namedef socket_conn(conn, addr):print('新连接:{0}'.format(addr))data = str(conn.recv(1024), 'utf-8')# 认证字符串if data == 'face0831':conn.send(bytes('认证成功!', 'utf-8'))# 文件信息dicfiledic = json.loads(str(conn.recv(1024), 'utf-8'))conn.send(bytes('文件信息接收成功!', 'utf-8'))filename = filedic['filename']filesize = filedic['filesize']filesha1 = filedic['sha1']# hash sha1加密验证(因为micropython没有md5)m = hashlib.sha1()print('文件名称:', filename)print('文件大小:', filesize)print('文件特征码:', filesha1)# 循环接收文件分包print('正在接收文件...')# 使用io将文件保存在内存中imgIO = io.BytesIO()while filesize:data = conn.recv(1024)m.update(data)imgIO.write(data)filesize -= len(data)# 比对文件特征码if str(binascii.hexlify(m.digest()), 'utf-8') == filesha1:print('文件验证成功,正在识别...')conn.send(bytes('正在识别...', 'utf-8'))# 识别图片face = detect_faces_in_image(imgIO)print('识别结果->', face)conn.send(bytes(face, 'utf-8'))else:print('文件验证失败')conn.send(bytes('文件验证失败', 'utf-8'))else:conn.send(bytes('认证失败!', 'utf-8'))if __name__ == "__main__":# 进入指定文件夹os.chdir('/root/face/')ADDR = ('0.0.0.0', 10086)# 设置套接字s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 绑定套接字s.bind(ADDR)# 允许一个连接s.listen(1)while True:print('等待连接...')# 阻塞连接conn, addr = s.accept()try:socket_conn(conn, addr)except Exception as e:print(e)finally:print('关闭连接')conn.close()

三、人脸识别和开锁代码

esp32cam 负责拍照和上传到服务器识别,识别成功后将开门指令发送给esp8266开门

为什么要弄两个单片机,不直接用esp32cam开门?

因为我们使用的场景一般esp32cam会放置到室外,如果用esp32cam直接控制电机会有安全问题,所以加了一块8266放置到室内来开门。

esp32cam代码:

import urequests,time,socket# 开门函数
def socket_client():try:s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.settimeout(20)s.connect(('esp8266地址', 10086))data = b'open's.send(data)while 1:msg1 = str(s.recv(1024),'utf-8')print(msg1 )if msg1 == 'over': return True# breakexcept socket.error as msg:print(msg)finally:s.close()# 14号引脚中断回调函数
def handle_interrupt(pin):global motionmotion = Trueglobal interrupt_pininterrupt_pin = pin# 上传函数
def get_face(data):print('打开socket')try:s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.connect(('你的服务器地址', 10086))# 发送认证字符串,简单认证s.send(bytes('face0831','utf-8'))print(str(s.recv(1024),'utf-8'))# 使用sha1获得文件特征码,为什么不用md5?因为micropython的hashlib中没有...m = hashlib.sha1()m.update(data)filename = str(int(time.time()))+'.jpg'filesize = len(data)# 文件信息dic = {"filename": filename, "filesize": filesize,"sha1":str(binascii.hexlify(m.digest()),'utf-8')}fileinfo = bytes(json.dumps(dic),'utf-8')s.send(fileinfo)print(str(s.recv(1024),'utf-8'))# 文件分片start = 0num = 1while filesize:ends = num * 1024updatafile = data[start:ends]s.send(updatafile)num =num + 1start = endsfilesize -= len(updatafile)print(str(s.recv(1024),'utf-8'))face = str(s.recv(1024),'utf-8')print ('识别结果->',face)return faceexcept Exception as e:print(e)finally:s.close()# 中断后拍照解锁    
def do_camera(led):#拍照动作buf = camera.capture()try:print ("正在识别...")# 调用上传函数face = get_face(buf)# 判断返回结果if face == '奥巴马':print ('识别成功->',face)led.value(0)# 开门suo = jiesuo.socket_client()if suo ==True:print('开门成功!')return Trueelse:print('开门失败!')return Falseexcept Exception as e:print(e)            #运行   
print('开始工作...')
print('初始化相机配置...')
import camera
camera.init(0, format=camera.JPEG)
#上翻下翻
camera.flip(0)
#左/右
camera.mirror(1)
# 框架
camera.framesize(camera.FRAME_SVGA)
#特效
camera.speffect(camera.EFFECT_NONE)
#白平衡
camera.whitebalance(camera.WB_NONE)
#饱和
camera.saturation(0)
#亮度
camera.brightness(0)
#对比度
camera.contrast(0)
#质量
camera.quality(10)print('初始化引脚...')
from machine import Pin
motion = False
# 控制led
led = Pin(4, Pin.OUT)
# 信号引脚,接按钮或pir红外模块
p14 = Pin(14, Pin.IN)print('正在监控画面移动...')
p14.irq(trigger=Pin.IRQ_RISING, handler=handle_interrupt)# 识别次数
photoNum = 1while True:# 14号引脚收到电平信号if motion:if photoNum ==1:# 打开单片机自带的闪光灯,esp32cam是4号引脚led.value(1)print('检测到移动:来自引脚', interrupt_pin ,',上传图片...')print('开始第{0}次识别'.format(str(photoNum)))# 拍照并上传can_camera = do_camera(led)# 识别成功if can_camera:motion = FalsephotoNum = 1# 识别不成功重复执行5次,每次间隔2s,防止拍照模糊识别失败,我们用次数来弥补摄像头的不足else:photoNum = photoNum + 1if photoNum == 6:print ('识别失败')motion = False# 关闭闪光灯led.value(0)photoNum = 1time.sleep(2)

esp8266代码:

import machine,socket,urequests,time# 重启函数
def do_reset(data):global workif work == False:print('暂无任务运行,正在重启--',data)machine.reset()else:print('正在开门,取消重启')# 操控引脚函数
def de_pin(oc,conn):global pin14global pin12global pin4# 收到开门指令if oc == 'open':# 开门pin12.value(0)print('pin12 : 1')# 引脚4的当前电压(当引脚4发生电压变化,则电机开始转动,开始检测动作是否到位)pin4sta = pin4.value()print('pin4:',pin4sta)# 检测电机是否开始转动while 1:# 不断检测引脚4的电压ss = pin4.value()print('pin4:',ss)# 引脚4发生电压变化,电机开始转动,停止当前循环if ss != pin4sta:breaktime.sleep(0.1)# 检测开门动作是否到位while 1:time.sleep(0.1)ss = pin4.value()print('pin4:',ss)# 如果触碰到限位器,则动作到位,电机停止转动if ss == 0:pin12.value(1)print('pin12 : 0')break# 发送开门完成的指令'isopen'conn.send(bytes('isopen','utf8'))# 等待10s,你可以推门了time.sleep(10)# 开始关门pin14.value(0)print('pin14 : 1')# 引脚4的当前电压(当引脚4发生电压变化,则电机开始转动,开始检测动作是否到位)pin4sta = pin4.value()print('pin4:',pin4sta)# 检测电机是否开始转动while 1:# 不断检测引脚4的电压ss = pin4.value()print('pin4:',ss)# 引脚4发生电压变化,点机开始转动,停止当前循环if ss != pin4sta:breaktime.sleep(0.1)# 检测关门动作是否到位while 1:time.sleep(0.1)ss = pin4.value()print('pin4:',ss)# 如果触碰到限位器,则动作到位,电机停止转动if ss == 0:time.sleep(0.5)pin14.value(1)print('pin14 : 0')break# 完成后通知esp32camconn.send(bytes('over','utf8'))# 处理套接字的函数
def deal_data(conn, addr):print('新连接:{0}'.format(addr))conn.send(bytes('连接成功!','utf8'))# 监听data = conn.recv(1024)# 收到的数据data = str(data,'utf-8')print(data)# 如果收到的为'open'指令,那么开门if data == 'open':# 开门de_pin(data,conn)# 关闭连接conn.close()# 设置一个定时器,定时重启,因为8266不稳定,经常连不上
from machine import Timer
tim = Timer(-1)
# tim.init(period=600000, mode=Timer.ONE_SHOT, callback=do_reset)# 设定工作状态,防止开锁期间系统自动重启
work = False
# 定时器,定时重启,30min
tim.init(period=1800000, mode=Timer.PERIODIC, callback=do_reset)# 监听的地址和端口,0.0.0.0为所有的来源
ADDR = ('0.0.0.0',10086)
# 使用tcp
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定套接字
s.bind(ADDR)
s.listen(1)#关门引脚
pin14 = machine.Pin(14,machine.Pin.OUT)
#开门引脚
pin12 = machine.Pin(12,machine.Pin.OUT)
#初始化,由于我使用的是低电平触发的继电器,常闭口接的是负极,所以要设置为高电平,让电机电路不带电压,防止短路和漏电(我使用的12V 3A的电源手碰到还是会麻的)
pin14.value(1)
pin12.value(1)
# 限位器引脚 内部上拉 防止出现不确定的情况
pin4 = machine.Pin(4,machine.Pin.IN,machine.Pin.PULL_UP)
# led灯,用于显示单片机是否正常工作
pin2 = machine.Pin(2,machine.Pin.OUT)while True:print('等待连接...')# led灯,注意,这个led灯是低电平打开,高电平关闭pin2.value(0)# 开始监听conn, addr = s.accept()try:# 处理套接字deal_data(conn, addr)except:passfinally:conn.close()

附:开门的材料和引脚接线

材料

开门采用两路5v低电平继电器、12v减速电机(30转/分,可以拉40斤,卖家说的,不知道真假)、限位器(自制,门把手转动到位后停止电机转动,防止电机或把手损坏,下附图)、12v电源、12v降5v降压板

引脚:

esp32cam

  • gpio 14 接触发装置,开关或红外

esp8266

  • gipo 14 开门 接继电器
  • gpio 12 关门 接继电器
  • gpio 4 限位器触发引脚 接限位器

继电器:

控制端

vcc接3.3v(这个要看你引脚的电平,因为esp系列引脚都是3.3v,那么继电器的电源一定要接3.3v,不然无法触发)

GND接GND

IN接控制引脚12、14

被控端

将12v电源线正极负极分别一分二分成两跟,共四根线接两路继电器。

常开接口(一般是第一个)接12v正,中间接电机,常闭接口(一般第三个)接12v负(两个继电器的接线一定要一样,比如第一个接线口接正,那么另一个继电器的第一个引脚也必须接正,不然无法控制,要不你就自己改代码。另外常闭和常开必须严格按前面说的接正、接负和做好绝缘,否则漏电烧坏设备和不小心短路起火概不负责)

限位器长下面这样子:

 上下两个接触片为GND,中间把手上的接触针为gpio4(已经内部上拉,为3.3v)。电机收到开门指令后转动,当针离开下接触片的时候,开始监测gpio4 的电压,此时gpio4为高电平。当接触到上接触片的时候,门锁已打开,gpio4为低电平,电机停转。关门同理。

完成后测试:

diy人脸识别开门门锁,使用esp32cam和esp8266单片机

最后:

打算加个扬声器,用于播报识别结果。目前还在啃DAC的文档,先上这些经验为敬~

辛辛苦苦码字不容易,给个评论鼓励下呗~

这篇关于esp32 cam+esp8266用micropython实现人脸识别开门的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/742923

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机