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

科技 科技 1914 人阅读 | 0 人回复

<
媒介

前里文章讲了编译micropython的编译战图传,那篇记载一下我本人DIY人脸辨认门锁的经验。
为何用esp8266,由于比esp32便宜几块钱,批收以至只需6块,哈哈
因为micropython我也刚上脚,也是教一面记载一面,当帮大家提早踩坑了~
空话未几道,间接上例子!
---------------------------------------------------------------------------------------------------------------------------------
万万没有要被题目骗了,光单片机是达没有到人脸辨认的,机能必定不敷。那末我们需求一个效劳器去对人脸停止比对,我用的是本人的一个arm效劳器。80多购的n1盒子,刷的centos,1.5G 4线程处置器关于我小我私家仍是够用。假如有同窗需求,我也能够零丁出一篇我的效劳器拆建踩坑阅历。当然您也能够用您的pc去当效劳器,机能尽对够用。怎样利用wsl刷进Ubuntu,我前里也有道,便没有空话了。
1、效劳器状况拆建

我用的是centos 7,颠末尝试,Ubuntu的操纵好未几。因为是利用centos,gcc版本必定不敷。那末需求晋级gcc版本,能够晋级到7或9,我便是那个坑华侈了一个多小时。
python3装置(皆利用micropython了,必定皆有py3,没有空话了)
centos晋级:
  1. # yum装置gcc7
  2. $ yum install devtoolset-7-gcc*
  3. # 用gcc7自带的剧本增加到情况变量
  4. $ scl enable devtoolset-7 bash
  5. # 检察gcc版本
  6. $ gcc -v
复造代码
Ubuntu不消晋级gcc,自带最新的
装置boost、cmake、git
centos:
  1. $ yum install -y boost,cmake,git
复造代码
Ubuntu
  1. $ sudo apt-get install -y git,cmake,libboost-all-dev
复造代码
编译dlib(我们用的人脸辨认依靠那个库)
  1. # 克隆dlib源代码
  2. $ git clone https://github.com/davisking/dlib.git
  3. $ cd dlib
  4. $ mkdir build
  5. $ cd build
  6. #那一部门是利用硬件加快的,假如硬件撑持,人脸辨认是很快的
  7. $ cmake .. -DDLIB_USE_CUDA=1 -DUSE_AVX_INSTRUCTIONS=1
  8. $ cmake --build .(留意中心有个空格)
  9. $ cd ..
  10. #python装置dlib
  11. $ python3 setup.py install
复造代码
重面!!!!!
编译dlib库倡议闲暇内乱存4G以上,不敷能够暂时利用swap,否则会编译失利

最初装置face_recognition
  1. # 装置 face_recognition
  2. $ pip3 install face_recognition
复造代码
至此,我们的人脸辨认状况便拆建好了
能够用一下代码测试能否装置胜利
  1. # 筹办两个文件夹,一个是参照图片,一个是要辨认的图片
  2. $ face_recognition ./file1/ ./file2/
复造代码
能辨认出便会显现第一个文件夹内乱的照片的名字。
2、创立人脸辨认接心

我们先利用以下代码天生本人的人脸数组
  1. import face_recognition
  2. # 翻开您的图片文件
  3. img = face_recognition.load_image_file(&#39;face.jpg&#39;)
  4. # 将人脸编码成素组
  5. face_encodings = face_recognition.face_encodings(img)
  6. # 挨印出的便是您的人脸数组,复造到上面的代码中,也能够保留到数据库(只需求list,没有要将全部tuple皆复造)
  7. print(face_encodings)
复造代码
利用python 的socket库去监听端心,当文件传进的时分停止辨认
  1. import socket,threading,os
  2. import face_recognition
  3. # 进进指定目次施行
  4. os.chdir(&#39;/root/face/&#39;)
  5. # 图片格局
  6. ALLOWED_EXTENSIONS = {&#39;png&#39;, &#39;jpg&#39;, &#39;jpeg&#39;, &#39;gif&#39;}
  7. def detect_faces_in_image(file_stream):
  8.     #人脸数组
  9.     face_list = [[[-0.09634063,  0.12095481, -0.00436332, -0.07643753,  0.0080383,
  10.                             0.01902981, -0.07184699, -0.09383309,  0.18518871, -0.09588896,
  11.                             0.23951106,  0.0986533 , -0.22114635, -0.1363683 ,  0.04405268,
  12.                             0.11574756, -0.19899382, -0.09597053, -0.11969153, -0.12277931,
  13.                             0.03416885, -0.00267565,  0.09203379,  0.04713435, -0.12731361,
  14.                            -0.35371891, -0.0503444 , -0.17841317, -0.00310897, -0.09844551,
  15.                            -0.06910533, -0.00503746, -0.18466514, -0.09851682,  0.02903969,
  16.                            -0.02174894,  0.02261871,  0.0032102 ,  0.20312519,  0.02999607,
  17.                            -0.11646006,  0.09432904,  0.02774341,  0.22102901,  0.26725179,
  18.                             0.06896867, -0.00490024, -0.09441824,  0.11115381, -0.22592428,
  19.                             0.06230862,  0.16559327,  0.06232892,  0.03458837,  0.09459756,
  20.                            -0.18777156,  0.00654241,  0.08582542, -0.13578284,  0.0150229 ,
  21.                             0.00670836, -0.08195844, -0.04346499,  0.03347827,  0.20310158,
  22.                             0.09987706, -0.12370517, -0.06683611,  0.12704916, -0.02160804,
  23.                             0.00984683,  0.00766284, -0.18980607, -0.19641446, -0.22800779,
  24.                             0.09010898,  0.39178532,  0.18818057, -0.20875394,  0.03097027,
  25.                            -0.21300618,  0.02532415,  0.07938635,  0.01000703, -0.07719778,
  26.                            -0.12651891, -0.04318593,  0.06219772,  0.09163868,  0.05039065,
  27.                            -0.04922386,  0.21839413, -0.02394437,  0.06173781,  0.0292527 ,
  28.                             0.06160797, -0.15553983, -0.02440624, -0.17509389, -0.0630486 ,
  29.                             0.01428208, -0.03637431,  0.03971229,  0.13983178, -0.23006812,
  30.                             0.04999552,  0.0108454 , -0.03970895,  0.02501768,  0.08157793,
  31.                            -0.03224047, -0.04502571,  0.0556995 , -0.24374914,  0.25514284,
  32.                             0.24795187,  0.04060191,  0.17597422,  0.07966681,  0.01920104,
  33.                            -0.01194376, -0.02300822, -0.17204897, -0.0596558 ,  0.05307484,
  34.                             0.07417042,  0.07126575,  0.00209804],&#39;奥巴马&#39;]]
  35.     #减载上传的暂时图片
  36.     img = face_recognition.load_image_file(file_stream)
  37.     #获得上传图象中的人脸编码
  38.     unknown_face_encodings = face_recognition.face_encodings(img)
  39.     # 界说辨认成果变量
  40.     is_name = &#39;unknow&#39;
  41.     if len(unknown_face_encodings) > 0:
  42.         #检察上传图象中的第一张面目面貌能否取已知面目面貌婚配
  43.         for face_who in face_list:
  44.             match_results = face_recognition.compare_faces([face_who[0]], unknown_face_encodings[0])
  45.             # 辨认胜利
  46.             if match_results[0]:
  47.                 is_name = face_who[1]
  48.     #将成果返回
  49.     return is_name
  50. def deal_data(conn, addr):
  51.     print(&#39;新毗连 {0}&#39;.format(addr))
  52.     # 两进造jpg数据
  53.     buff = b&#39;&#39;
  54.     while 1:
  55.         data = conn.recv(1024)
  56.         # print(&#39;{0} 客户端收收数据是 {1}&#39;.format(addr, data.decode()))
  57.         print(&#39;支到数据,正正在处置...&#39;)
  58.         # 因为会分包收收,我们领受的时分要兼并数据
  59.         buff += data
  60.         # 我利用endsend去标识表记标帜文件收收完毕
  61.         if data[-7:] == b&#39;endsend&#39;:
  62.             # 来失落endsend
  63.             buff = buff.strip(b&#39;endsend&#39;)
  64.             # 创立暂时文件用去存与支到的图片数据
  65.             tempfile = &#39;temp.jpg&#39;
  66.             # 保留图片
  67.             with open(tempfile,&#39;wb&#39;) as f:
  68.                 f.write(buff)
  69.                 f.close()
  70.             # 辨认人脸图片,只需返回的没有是&#39;unknow&#39;,便代表辨认胜利
  71.             ss = detect_faces_in_image(tempfile)
  72.             # 收收辨认成果
  73.             conn.send(bytes(ss,&#39;UTF-8&#39;))
  74.             # 删除暂时图片文件
  75.             os.remove(tempfile)
  76.             # 封闭毗连
  77.             print(&#39;{0} 毗连封闭&#39;.format(addr))
  78.             conn.close()
  79.             break
  80.         # 封闭毗连
  81.         elif not data:
  82.             print(&#39;{0} 毗连封闭&#39;.format(addr))
  83.             conn.close()
  84.             break
  85.    
  86.    
  87. if __name__ == "__main__":
  88.     # 监听的地点
  89.     ADDR = (&#39;0.0.0.0&#39;,10086)
  90.     #socket利用TCP毗连
  91.     s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  92.     s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  93.     # 绑定套接字
  94.     s.bind(ADDR)
  95.     # 最年夜毗连数
  96.     s.listen(10)
  97.     print(&#39;等候毗连...&#39;)
  98.     while 1:
  99.         # 开端监听端心
  100.         conn, addr = s.accept()
  101.         # 另开线程运转
  102.         t = threading.Thread(target=deal_data, args=(conn, addr))
  103.         t.start()
复造代码
3、人脸辨认战开锁代码

esp32cam 卖力照相战上传到效劳器辨认,辨认胜利后将开门指令收收给esp8266开门
为何要弄两个单片机,没有间接用esp32cam开门?
由于我们利用的场景普通esp32cam会安排到室中,假如用esp32cam间接掌握机电会有宁静成绩,以是减了一块8266安排到室内乱去开门。
esp32cam代码:
  1. import urequests,time,socket,sys
  2. # 开门函数
  3. def socket_client():
  4.     try:
  5.         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  6.         s.connect((&#39;esp8266地点&#39;, 10086))
  7.     except socket.error as msg:
  8.         print(msg)
  9.         sys.exit(1)
  10.     # 开门指令为open
  11.     data = b&#39;open&#39;
  12.     # 收收到esp8266
  13.     s.send(data)
  14.     while 1:
  15.         # 壅闭承受数据
  16.         msg1 = str(s.recv(1024),&#39;utf-8&#39;)
  17.         print(msg1 )
  18.         # 支到over后封闭毗连
  19.         if msg1 == &#39;over&#39;:
  20.             s.close()
  21.             return True
  22. # 14号引足中止回调函数
  23. def handle_interrupt(pin):
  24.     global motion
  25.     motion = True
  26.     global interrupt_pin
  27.     interrupt_pin = pin
  28. # 上传函数
  29. def get_face(buf):
  30.     print(&#39;翻开socket&#39;)
  31.     try:
  32.         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  33.         s.connect((&#39;您的效劳器地点&#39;, 10086))
  34.     except socket.error as msg:
  35.         print(msg)
  36.     # 图片数据   
  37.     data = buf
  38.     # 收收图片
  39.     s.send(data)
  40.     # 收收完毕字符串标记
  41.     s.send(b&#39;endsend&#39;)
  42.     # 辨认成果
  43.     face = str(s.recv(1024),&#39;utf8&#39;)
  44.     print(&#39;辨认成果->&#39;,face)
  45.     # 封闭毗连
  46.     s.close()
  47.     # 返回辨认成果
  48.     return face
  49. # 中止后照相解锁   
  50. def do_camera(led):
  51.     #照相行动
  52.     buf = camera.capture()
  53.     try:
  54.         print ("正正在辨认...")
  55.         # 挪用上传代码
  56.         face = get_face(buf)
  57.         # 成果没有是&#39;unknow&#39;辨认胜利
  58.         if face != &#39;unknow&#39;:
  59.             print (&#39;辨认胜利->&#39;,face)
  60.             led.value(0)
  61.             # 开门
  62.             suo = jiesuo.socket_client()
  63.             if suo ==True:
  64.                 print(&#39;开门胜利!&#39;)
  65.                 return True
  66.             else:
  67.                 print(&#39;开门失利!&#39;)
  68.                 return False
  69.     except Exception as e:
  70.         print(e)            
  71.         
  72. #运转   
  73. print(&#39;开端事情...&#39;)
  74. print(&#39;初初化相机设置...&#39;)
  75. import camera
  76. camera.init(0, format=camera.JPEG)
  77. #上翻下翻
  78. camera.flip(0)
  79. #左/左
  80. camera.mirror(1)
  81. # 框架
  82. camera.framesize(camera.FRAME_SVGA)
  83. #殊效
  84. camera.speffect(camera.EFFECT_NONE)
  85. #利剑均衡
  86. camera.whitebalance(camera.WB_NONE)
  87. #饱战
  88. camera.saturation(0)
  89. #明度
  90. camera.brightness(0)
  91. #比照度
  92. camera.contrast(0)
  93. #量量
  94. camera.quality(10)
  95. print(&#39;初初化引足...&#39;)
  96. from machine import Pin
  97. from time import sleep
  98. motion = False
  99. # 掌握led
  100. led = Pin(4, Pin.OUT)
  101. # 旌旗灯号引足,接按钮或pir白中模块
  102. p14 = Pin(14, Pin.IN)
  103. print(&#39;正正在监控绘里挪动...&#39;)
  104. p14.irq(trigger=Pin.IRQ_RISING, handler=handle_interrupt)
  105. # 辨认次数
  106. photoNum = 1
  107. while True:
  108.     # 14号引足支到电仄旌旗灯号
  109.     if motion:
  110.         if photoNum ==1:
  111.             # 翻开单片机自带的闪光灯,esp32cam是4号引足
  112.             led.value(1)
  113.             print(&#39;检测到挪动:去自引足&#39;, interrupt_pin ,&#39;,上传图片...&#39;)
  114.             
  115.         print(&#39;开端第{0}次辨认&#39;.format(str(photoNum)))
  116.         # 照相并上传
  117.         can_camera = do_camera(led)
  118.         # 辨认胜利
  119.         if can_camera:
  120.             motion = False
  121.             photoNum = 1
  122.         # 辨认没有胜利反复施行5次,每次距离2s,避免照相恍惚辨认失利,我们用次数去补偿摄像头的不敷
  123.         else:
  124.             photoNum = photoNum + 1
  125.             if photoNum == 6:
  126.                 print (&#39;辨认失利&#39;)
  127.                 motion = False
  128.                 # 封闭闪光灯
  129.                 led.value(0)
  130.                 photoNum = 1
  131.             time.sleep(2)
复造代码
esp8266代码:
  1. import machine,socket,urequests,time
  2. # 操控引足函数
  3. def de_pin(oc,conn):
  4.     global pin14
  5.     global pin12
  6.     global pin4
  7.     # 支到开门指令
  8.     if oc == &#39;open&#39;:
  9.         # 开门
  10.         pin12.value(0)
  11.         print(&#39;pin12 : 1&#39;)
  12.         # 引足4确当前电压(当引足4发作电压变革,则机电开端动弹,开端检测行动能否到位)
  13.         pin4sta = pin4.value()
  14.         print(&#39;pin4:&#39;,pin4sta)
  15.         # 检测机电能否开端动弹
  16.         while 1:
  17.             # 不竭检测引足4的电压
  18.             ss = pin4.value()
  19.             print(&#39;pin4:&#39;,ss)
  20.             # 引足4发作电压变革,机电开端动弹,截至当前轮回
  21.             if ss != pin4sta:
  22.                 break
  23.             time.sleep(0.1)
  24.         # 检测开门行动能否到位
  25.         while 1:
  26.             time.sleep(0.1)
  27.             ss = pin4.value()
  28.             print(&#39;pin4:&#39;,ss)
  29.             # 假如触碰着限位器,则行动到位,机电截至动弹
  30.             if ss == 1:
  31.                 pin12.value(1)
  32.                 print(&#39;pin12 : 0&#39;)
  33.                 break
  34.         # 收收开门完成的指令&#39;isopen&#39;
  35.         conn.send(bytes(&#39;isopen&#39;,&#39;utf8&#39;))
  36.         # 等候10s,您能够排闼了
  37.         time.sleep(10)
  38.         # 开端闭门
  39.         pin14.value(0)
  40.         print(&#39;pin14 : 1&#39;)
  41.         # 引足4确当前电压(当引足4发作电压变革,则机电开端动弹,开端检测行动能否到位)
  42.         pin4sta = pin4.value()
  43.         print(&#39;pin4:&#39;,pin4sta)
  44.         # 检测机电能否开端动弹
  45.         while 1:
  46.             # 不竭检测引足4的电压
  47.             ss = pin4.value()
  48.             print(&#39;pin4:&#39;,ss)
  49.             # 引足4发作电压变革,面机开端动弹,截至当前轮回
  50.             if ss != pin4sta:
  51.                 break
  52.             time.sleep(0.1)
  53.         # 检测闭门行动能否到位
  54.         while 1:
  55.             time.sleep(0.1)
  56.             ss = pin4.value()
  57.             print(&#39;pin4:&#39;,ss)
  58.             # 假如触碰着限位器,则行动到位,机电截至动弹
  59.             if ss == 1:
  60.                 time.sleep(0.5)
  61.                 pin14.value(1)
  62.                 print(&#39;pin14 : 0&#39;)
  63.                 break
  64.    
  65. # 处置套接字的函数
  66. def deal_data(conn, addr):
  67.     print(&#39;新毗连:{0}&#39;.format(addr))
  68.     conn.send(bytes(&#39;毗连胜利!&#39;,&#39;utf8&#39;))
  69.     # 监听
  70.     data = conn.recv(10240)
  71.     # 支到的数据
  72.     data = str(data,&#39;utf-8&#39;)
  73.     print(data)
  74.     # 假如支到的为&#39;open&#39;指令,那末开门
  75.     if data == &#39;open&#39;:
  76.         # 开门
  77.         de_pin(data,conn)
  78.         # 完成后告诉esp32cam
  79.         conn.send(bytes(&#39;over&#39;,&#39;utf8&#39;))
  80.     # 封闭毗连
  81.     conn.close()
  82. # 监听的地点战端心,0.0.0.0为一切的滥觞
  83. ADDR = (&#39;0.0.0.0&#39;,10086)
  84. # 利用tcp
  85. s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  86. s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  87. # 绑定套接字
  88. s.bind(ADDR)
  89. s.listen(1)
  90. #开门引足
  91. pin14 = machine.Pin(14,machine.Pin.OUT)
  92. #闭门引足
  93. pin12 = machine.Pin(12,machine.Pin.OUT)
  94. #初初化,因为我利用的是低电仄触收的继电器,以是要设置为下电仄
  95. pin14.value(1)
  96. pin12.value(1)
  97. pin4 = machine.Pin(4,machine.Pin.IN)
  98. # led灯,我的单片机是个蓝色的,挺都雅,我便翻开了,没有念要能够正文失落
  99. pin2 = machine.Pin(2,machine.Pin.OUT)
  100. while True:
  101.     print(&#39;等候毗连...&#39;)
  102.     # led灯,我的单片机是个蓝色的,挺都雅,我便翻开了,没有念要能够正文失落
  103.     pin2.value(0)
  104.     # 开端监听
  105.     conn, addr = s.accept()
  106.     try:
  107.         # 处置套接字
  108.         deal_data(conn, addr)
  109.     except:
  110.         pass
复造代码
附:开门的质料战引足接线

质料
开门接纳两路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背(两个继电器的接线必然要一样,好比第一个接线心接正,那末另外一个继电器的第一个引足也必需接正,否则没法掌握,要没有您便本人改代码。此外常闭战常开必需严厉按前里道的接正、接背战做好尽缘,不然泄电烧坏装备战没有当心短路起水概没有卖力)
限位器少上面如许子:
105147ncz60cxa6xvxpux0.jpg

 高低两个打仗片为3.3v(下电仄),中心把脚上的打仗针为gpio4。机电支到开门指令后动弹,当针分开下打仗片的时分,开端监测gpio4 的电压,此时gpio4为低电仄。当打仗到上打仗片的时分,门锁已翻开,gpio4为下电仄,机电停转。闭门同理。
完成后测试:
   diy人脸辨认开门门锁,利用esp32cam战esp8266单片机
最初:

筹算减个扬声器,用于播报辨认成果。今朝借正在啃DAC的文档,先上那些经验为敬~
辛辛劳苦码字没有简单,给个批评鼓舞下呗~

免责声明:假如进犯了您的权益,请联络站少,我们会实时删除侵权内乱容,感谢协作!
1、本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,按照目前互联网开放的原则,我们将在不通知作者的情况下,转载文章;如果原文明确注明“禁止转载”,我们一定不会转载。如果我们转载的文章不符合作者的版权声明或者作者不想让我们转载您的文章的话,请您发送邮箱:Cdnjson@163.com提供相关证明,我们将积极配合您!
2、本网站转载文章仅为传播更多信息之目的,凡在本网站出现的信息,均仅供参考。本网站将尽力确保所提供信息的准确性及可靠性,但不保证信息的正确性和完整性,且不对因信息的不正确或遗漏导致的任何损失或损害承担责任。
3、任何透过本网站网页而链接及得到的资讯、产品及服务,本网站概不负责,亦不负任何法律责任。
4、本网站所刊发、转载的文章,其版权均归原作者所有,如其他媒体、网站或个人从本网下载使用,请在转载有关文章时务必尊重该文章的著作权,保留本网注明的“稿件来源”,并自负版权等法律责任。
回复 关闭延时

使用道具 举报

 
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则