- 2024-09-01
-
发表了主题帖:
【2024 DigiKey 创意大赛】基于树莓派的室内CO2浓度检测仪-开箱帖
得捷电子上下单很方便,发货挺快,美国到国内5天就到了。
发的顺丰快递
内部有塑料袋和减震泡沫
树莓派5 4G版本
SGP30空气质量传感器,用来检测CO2浓度。
开箱结束。
- 2024-06-29
-
加入了学习《PI PowiGaN 系列视频》,观看 氮化镓进化史 - PI 高层座谈 - PowerUP Expo 2023
- 2024-03-22
-
加入了学习《【得捷电子Follow me第4期】演示视频》,观看 【得捷电子Follow me第4期】演示视频
- 2024-03-18
-
加入了学习《泰克MSO6B探索营》,观看 MSO6B技术介绍
-
加入了学习《泰克MSO6B探索营》,观看 MSO6B-360度介绍
- 2024-03-11
-
发表了主题帖:
【得捷电子Follow me第4期】补充项目材料
终极任务二 使用主控板内存储和以太网接口,实现轻量ftp文件服务器,可以上传、下载、删除文件
参考大佬们的方案,采用microPython 进行开发,开发板重新刷入microPython固件。然后采用相同局域网网段的PC电脑对开发板进行FTP访问。
import socket
import network
import uos
import gc
from time import localtime
from machine import Pin,SPI
import time
def w5x00_init():
#spi init
spi=SPI(0,2_000_000, mosi=Pin(19),miso=Pin(16),sck=Pin(18))
nic = network.WIZNET5K(spi,Pin(17),Pin(20)) #spi,cs,reset pin
nic.active(True)#network active
nic.ifconfig(('192.168.1.100','255.255.255.0','192.168.1.1','8.8.8.8'))#Set static network address information
while not nic.isconnected():
time.sleep(1)
print(nic.regs())#Print register information
#Print network address information
print("IP Address:",nic.ifconfig()[0])
print("Subnet Mask:",nic.ifconfig()[1])
print("Gateway:",nic.ifconfig()[2])
print("DNS:",nic.ifconfig()[3])
return nic
month_name = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
def send_list_data(path, dataclient, full):
try: # whether path is a directory name
for fname in uos.listdir(path):
dataclient.sendall(make_description(path, fname, full))
except: # path may be a file name or pattern
pattern = path.split("/")[-1]
path = path[:-(len(pattern) + 1)]
if path == "": path = "/"
for fname in uos.listdir(path):
if fncmp(fname, pattern) == True:
dataclient.sendall(make_description(path, fname, full))
def make_description(path, fname, full):
if full:
stat = uos.stat(get_absolute_path(path,fname))
file_permissions = "drwxr-xr-x" if (stat[0] & 0o170000 == 0o040000) else "-rw-r--r--"
file_size = stat[6]
tm = localtime(stat[7])
if tm[0] != localtime()[0]:
description = "{} 1 owner group {:>10} {} {:2} {:>5} {}\r\n".format(
file_permissions, file_size, month_name[tm[1]], tm[2], tm[0], fname)
else:
description = "{} 1 owner group {:>10} {} {:2} {:02}:{:02} {}\r\n".format(
file_permissions, file_size, month_name[tm[1]], tm[2], tm[3], tm[4], fname)
else:
description = fname + "\r\n"
return description
def send_file_data(path, dataclient):
with open(path, "r") as file:
chunk = file.read(512)
while len(chunk) > 0:
dataclient.sendall(chunk)
chunk = file.read(512)
def save_file_data(path, dataclient, mode):
with open(path, mode) as file:
chunk = dataclient.read(512)
while len(chunk) > 0:
file.write(chunk)
chunk = dataclient.read(512)
def get_absolute_path(cwd, payload):
# Just a few special cases "..", "." and ""
# If payload start's with /, set cwd to /
# and consider the remainder a relative path
if payload.startswith('/'):
cwd = "/"
for token in payload.split("/"):
if token == '..':
if cwd != '/':
cwd = '/'.join(cwd.split('/')[:-1])
if cwd == '':
cwd = '/'
elif token != '.' and token != '':
if cwd == '/':
cwd += token
else:
cwd = cwd + '/' + token
return cwd
# compare fname against pattern. Pattern may contain
# wildcards ? and *.
def fncmp(fname, pattern):
pi = 0
si = 0
while pi < len(pattern) and si < len(fname):
if (fname[si] == pattern[pi]) or (pattern[pi] == '?'):
si += 1
pi += 1
else:
if pattern[pi] == '*': # recurse
if (pi + 1) == len(pattern):
return True
while si < len(fname):
if fncmp(fname[si:], pattern[pi+1:]) == True:
return True
else:
si += 1
return False
else:
return False
if pi == len(pattern.rstrip("*")) and si == len(fname):
return True
else:
return False
def ftpserver():
DATA_PORT = 13333
ftpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
datasocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ftpsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
datasocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ftpsocket.bind(socket.getaddrinfo("0.0.0.0", 21)[0][4])
datasocket.bind(socket.getaddrinfo("0.0.0.0", DATA_PORT)[0][4])
ftpsocket.listen(1)
datasocket.listen(1)
datasocket.settimeout(10)
msg_250_OK = '250 OK\r\n'
msg_550_fail = '550 Failed\r\n'
try:
dataclient = None
fromname = None
while True:
cl, remote_addr = ftpsocket.accept()
cl.settimeout(300)
cwd = '/'
try:
# print("FTP connection from:", remote_addr)
cl.sendall("220 Hello, this is the ESP8266.\r\n")
while True:
gc.collect()
data = cl.readline().decode("utf-8").rstrip("\r\n")
if len(data) <= 0:
print("Client disappeared")
break
command = data.split(" ")[0].upper()
payload = data[len(command):].lstrip()
path = get_absolute_path(cwd, payload)
print("Command={}, Payload={}, Path={}".format(command, payload, path))
if command == "USER":
cl.sendall("230 Logged in.\r\n")
elif command == "SYST":
cl.sendall("215 UNIX Type: L8\r\n")
elif command == "NOOP":
cl.sendall("200 OK\r\n")
elif command == "FEAT":
cl.sendall("211 no-features\r\n")
elif command == "PWD":
cl.sendall('257 "{}"\r\n'.format(cwd))
elif command == "CWD":
try:
files = uos.listdir(path)
cwd = path
cl.sendall(msg_250_OK)
except:
cl.sendall(msg_550_fail)
elif command == "CDUP":
cwd = get_absolute_path(cwd, "..")
cl.sendall(msg_250_OK)
elif command == "TYPE":
# probably should switch between binary and not
cl.sendall('200 Transfer mode set\r\n')
elif command == "SIZE":
try:
size = uos.stat(path)[6]
cl.sendall('213 {}\r\n'.format(size))
except:
cl.sendall(msg_550_fail)
elif command == "QUIT":
cl.sendall('221 Bye.\r\n')
break
elif command == "PASV":
addr = nic.ifconfig()[0]
cl.sendall('227 Entering Passive Mode ({},{},{}).\r\n'.format(
addr.replace('.',','), DATA_PORT>>8, DATA_PORT%256))
dataclient, data_addr = datasocket.accept()
# print("FTP Data connection from:", data_addr)
elif command == "LIST" or command == "NLST":
if not payload.startswith("-"):
place = path
else:
place = cwd
try:
send_list_data(place, dataclient, command == "LIST" or payload == "-l")
cl.sendall("150 Here comes the directory listing.\r\n")
cl.sendall("226 Listed.\r\n")
except:
cl.sendall(msg_550_fail)
if dataclient is not None:
dataclient.close()
dataclient = None
elif command == "RETR":
try:
send_file_data(path, dataclient)
cl.sendall("150 Opening data connection.\r\n")
cl.sendall("226 Transfer complete.\r\n")
except:
cl.sendall(msg_550_fail)
if dataclient is not None:
dataclient.close()
dataclient = None
elif command == "STOR":
try:
cl.sendall("150 Ok to send data.\r\n")
save_file_data(path, dataclient, "w")
cl.sendall("226 Transfer complete.\r\n")
except:
cl.sendall(msg_550_fail)
if dataclient is not None:
dataclient.close()
dataclient = None
elif command == "APPE":
try:
cl.sendall("150 Ok to send data.\r\n")
save_file_data(path, dataclient, "a")
cl.sendall("226 Transfer complete.\r\n")
except:
cl.sendall(msg_550_fail)
if dataclient is not None:
dataclient.close()
dataclient = None
elif command == "DELE":
try:
uos.remove(path)
cl.sendall(msg_250_OK)
except:
cl.sendall(msg_550_fail)
elif command == "RMD":
try:
uos.rmdir(path)
cl.sendall(msg_250_OK)
except:
cl.sendall(msg_550_fail)
elif command == "MKD":
try:
uos.mkdir(path)
cl.sendall(msg_250_OK)
except:
cl.sendall(msg_550_fail)
elif command == "RNFR":
fromname = path
cl.sendall("350 Rename from\r\n")
elif command == "RNTO":
if fromname is not None:
try:
uos.rename(fromname, path)
cl.sendall(msg_250_OK)
except:
cl.sendall(msg_550_fail)
else:
cl.sendall(msg_550_fail)
fromname = None
else:
cl.sendall("502 Unsupported command.\r\n")
# print("Unsupported command {} with payload {}".format(command, payload))
except Exception as err:
print(err)
finally:
cl.close()
cl = None
finally:
datasocket.close()
ftpsocket.close()
if dataclient is not None:
dataclient.close()
nic = w5x00_init()
ftpserver()
运行如图
建立了地址为192.168.1.100的ftp
在PC机上使用Xftp软件,建立连接
建立连接后传输一个文件到FTP上 将DGBCDX64.exe传输到ftp上
演示视频:
https://training.eeworld.com.cn/video/39557
代码下载:
源码
总结
以前没接触过网络编程这次学到了很多网络底层的协议,提高了不少!
感谢EEWorld和DigiKey!
-
加入了学习《【DigiKey创意大赛】人脸识别储物柜 》,观看 【DigiKey创意大赛】人脸识别储物柜
-
上传了资料:
【得捷电子Follow me第4期】代码汇总
- 2024-03-06
-
发表了主题帖:
【得捷Follow me第4期】作品提交
本帖最后由 xscc 于 2024-3-6 06:17 编辑
【得捷Follow me第4期】项目提交
硬件选用官方推荐的组合,w5500模块加Adafruit LCD显示屏。焊接好排线后,因电路比较简单,用面包板搭接电路。并刷入固件和所用到的库文件,按住boot键,同时将w5500插入U口,拖拽下好的固件至u盘即可,使用adafruit-circuitpython-wiznet_w5500_evb_pico-en_US-8.2.9.uf2。
W5500引脚图
一、入门任务:
使用circuit python进行开发工作,库文件比较全,开发简单。
基础任务1,驱动led闪烁,驱动lcd进行文字和线条测试。按照spi = GP14, MOSI=15,scs =GP13连接lcd和w5500。
写入如下程序
import sys
import time
import board
import digitalio
import busio
import adafruit_sharpmemorydisplay
# 打印输入到stdout
print("start:xxx")
# 初始化LED引脚
led = digitalio.DigitalInOut(board.GP25)
led.direction = digitalio.Direction.OUTPUT
# Initialize SPI bus and control pins
spi = busio.SPI(board.GP14, MOSI=board.GP15)
scs = digitalio.DigitalInOut(board.GP13) # inverted chip select
# pass in the display size, width and height, as well
# display = adafruit_sharpmemorydisplay.SharpMemoryDisplay(spi, scs, 96, 96)
display = adafruit_sharpmemorydisplay.SharpMemoryDisplay(spi, scs, 144, 168)
print("Pixel test")
# Clear the display. Always call show after changing pixels to make the display
# update visible!
display.fill(1)
display.show()
# Set a pixel in the origin 0,0 position.
display.pixel(0, 0, 0)
# Set a pixel in the middle position.
display.pixel(display.width // 2, display.width // 2, 0)
# Set a pixel in the opposite corner position.
display.pixel(display.width - 1, display.height - 1, 0)
display.show()
time.sleep(2)
print("Lines test")
# we'll draw from corner to corner, lets define all the pair coordinates here
corners = (
(0, 0),
(0, display.height - 1),
(display.width - 1, 0),
(display.width - 1, display.height - 1),
)
display.fill(1)
for corner_from in corners:
for corner_to in corners:
display.line(corner_from[0], corner_from[1], corner_to[0], corner_to[1], 0)
display.show()
time.sleep(2)
print("Rectangle test")
display.fill(1)
w_delta = display.width / 10
h_delta = display.height / 10
for i in range(11):
display.rect(0, 0, int(w_delta * i), int(h_delta * i), 0)
display.show()
time.sleep(2)
print("Text test")
display.fill(1)
display.text(" hello world!", 0, 0, 0)
display.text(" This is the", 0, 8, 0)
display.text(" CircuitPython", 0, 16, 0)
display.text("adafruit library", 0, 24, 0)
display.text(" for the SHARP", 0, 32, 0)
display.text(" Memory Display :) ", 0, 40, 0)
display.show()
# 循环使能LED点亮
while True:
# 点亮LED
led.value = True
time.sleep(1)
led.value = False
time.sleep(1)
结果如图所示
文字显示
线条显示
[localvideo]cffc6364a2ef43c49165e917ade9fe33[/localvideo]
演示视频
基础任务2:完成主控板W5500初始化(静态IP配置),并能使用局域网电脑ping通,同时W5500可以ping通互联网站点;通过抓包软件(Wireshark、Sniffer等)抓取本地PC的ping报文,展示并分析。
查看本机的IP参数如下
配置w5500网络参数
IP_ADDRESS = (192, 168, 1, 123)
SUBNET_MASK = (255, 255, 255, 0)
GATEWAY_ADDRESS = (192, 168, 1, 1)
DNS_SERVER = (8, 8, 8, 8)
代码如下
import board
import busio
import digitalio
import time
from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K
##SPI0
SPI0_SCK = board.GP18
SPI0_TX = board.GP19
SPI0_RX = board.GP16
SPI0_CSn = board.GP17
##reset
W5x00_RSTn = board.GP20
print("Wiznet5k Ping Test (no DHCP)")
# Setup your network configuration below
# random MAC, later should change this value on your vendor ID
MY_MAC = (0x00, 0x01, 0x02, 0x03, 0x04, 0x05)
IP_ADDRESS = (192, 168, 1, 123)
SUBNET_MASK = (255, 255, 255, 0)
GATEWAY_ADDRESS = (192, 168, 1, 1)
DNS_SERVER = (8, 8, 8, 8)
led = digitalio.DigitalInOut(board.GP25)
led.direction = digitalio.Direction.OUTPUT
ethernetRst = digitalio.DigitalInOut(W5x00_RSTn)
ethernetRst.direction = digitalio.Direction.OUTPUT
# For Adafruit Ethernet FeatherWing
cs = digitalio.DigitalInOut(SPI0_CSn)
# For Particle Ethernet FeatherWing
# cs = digitalio.DigitalInOut(board.D5)
spi_bus = busio.SPI(SPI0_SCK, MOSI=SPI0_TX, MISO=SPI0_RX)
# Reset W5500 first
ethernetRst.value = False
time.sleep(1)
ethernetRst.value = True
# Initialize ethernet interface with DHCP
# eth = WIZNET5K(spi_bus, cs)
# Initialize ethernet interface without DHCP
eth = WIZNET5K(spi_bus, cs, is_dhcp=False, mac=MY_MAC)
# Set network configuration
eth.ifconfig = (IP_ADDRESS, SUBNET_MASK, GATEWAY_ADDRESS, DNS_SERVER)
print("Chip Version:", eth.chip)
print("MAC Address:", [hex(i) for i in eth.mac_address])
print("My IP address is:", eth.pretty_ip(eth.ip_address))
while True:
led.value = not led.value
time.sleep(1)
print("Done!")
在本机上ping开发板如图所示
正常ping通。
使用wireshark进行抓包操作,用规则ip.addr == 192.168.1.123过滤出和W5500的交互数据。
进阶任务1 从NTP服务器(注意数据交互格式的解析)同步时间,获取时间送显示屏(串口)显示.
代码如下
import board
import busio
import digitalio
import time
import array
import struct
import adafruit_requests as requests
from adafruit_wiznet5k.adafruit_wiznet5k import *
import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket
from adafruit_wiznet5k.adafruit_wiznet5k_ntp import NTP
import adafruit_wiznet5k.adafruit_wiznet5k_dns as dns
import adafruit_ntp
days = ("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
##SPI0
SPI0_SCK = board.GP18
SPI0_TX = board.GP19
SPI0_RX = board.GP16
SPI0_CSn = board.GP17
##reset
W5x00_RSTn = board.GP20
print("Wiznet5k NTP Client (DHCP)")
# Setup your network configuration below
# random MAC, later should change this value on your vendor ID
MY_MAC = (0x00, 0x01, 0x02, 0xFF, 0xFF, 0xFF)
IP_ADDRESS = (192, 168, 1, 123)
SUBNET_MASK = (255, 255, 255, 0)
GATEWAY_ADDRESS = (192, 168, 1, 1)
DNS_SERVER = (8, 8, 8, 8)
port = 5000
ntp_server_port= 123
led = digitalio.DigitalInOut(board.GP25)
led.direction = digitalio.Direction.OUTPUT
ethernetRst = digitalio.DigitalInOut(W5x00_RSTn)
ethernetRst.direction = digitalio.Direction.OUTPUT
# For Adafruit Ethernet FeatherWing
cs = digitalio.DigitalInOut(SPI0_CSn)
# cs = digitalio.DigitalInOut(board.D5)
spi_bus = busio.SPI(SPI0_SCK, MOSI=SPI0_TX, MISO=SPI0_RX)
# Reset W5500 first
ethernetRst.value = False
time.sleep(1)
ethernetRst.value = True
# Initialize ethernet interface without DHCP
#eth = WIZNET5K(spi_bus, cs, is_dhcp=False, mac=MY_MAC, debug=False)
# Initialize ethernet interface with DHCP
eth = WIZNET5K(spi_bus, cs, is_dhcp=True, mac=MY_MAC, debug=False)
print("Chip Version:", eth.chip)
print("MAC Address:", [hex(i) for i in eth.mac_address])
print("My IP address is:", eth.pretty_ip(eth.ip_address))
# Initialize a socket for our server
socket.set_interface(eth)
# Set network configuration
eth.ifconfig = (IP_ADDRESS, SUBNET_MASK, GATEWAY_ADDRESS, DNS_SERVER)
ntp = adafruit_ntp.NTP(socket)
print(ntp.datetime)
串口输出结果如下
- 2024-02-06
-
加入了学习《直播回放: FollowMe 4 W5500-EVB-Pico 使用入门》,观看 W5500-EVB-Pico 使用入门
- 2024-01-17
-
发表了主题帖:
【DigiKey“智造万物,快乐不停”创意大赛】+人脸识别储物柜 作品提交
作品名称:人脸识别储物柜
作者:xscc
一、作品简介
人脸识别储物柜,通过人脸识别完成储物操作,解决传统储物柜钥匙或小票丢失带来的不便。项目主控采用树莓派4B,外扩摄像头和执行机构完成设计。
成品照片:
项目用到的板卡和模块有,1、树莓派4B,2、罗技USB摄像头,3、继电器模块,4、电控锁,5、电源。
树莓派4B
USB摄像头
继电器模块
电控锁
主要功能通过摄像头采集人员面部信息,在树莓派上运行OPENCV图像库,进行图形采集和特征提取工作,如果和存储的特征库吻合,则通过树莓派上GPIO17驱动继电器打开柜门锁完成存储工作。
二、系统框图
项目选用树莓派4B作为主控,安装官方64位系统,编程语言选用Python开发,Python编程符合自然语言习惯,上手快,官方系统已经安装Python。然后安装OpenCV库,OpenCV是开源计算机视觉库,拥有强大的内置函数和开源社群。OpenCV配合便携开源廉价的树莓派,可以直接读取来自摄像头的视频,进行人脸识别、边缘检测、图像识别等各种计算机视觉开发任务。树莓派系统和OpenCV安装网上教程很多,按教程一步步完成就行,此处不再重复。
硬件设计采用树莓派4B作为主控、HDMI显示屏、USB摄像头、一位继电器模块、电控门锁和电源,TF卡等附件组成。电路图如下图所示:
树莓派4B运行操纵系统和OPENCV图像识别库,USB摄像头负责采集人脸信息,继电器和电控锁完成开锁工作。
当人脸特征符合存储的数据后,树莓派GPIO17输出高电平,驱动继电器接通三秒,打开电控锁,弹出柜门。
软件流程见下图:
三、各部分功能说明
由于是演示只设计了一个柜门,硬件电路较为简单,就是通过树莓派GPIO17脚驱动继电器打开电控锁,扩展一个继电器是因为电控锁动作所需电流较大5V1A,树莓派GPIO引脚提供不了所需电流。
开发工具使用的是 Python2 作为开发语言,OpenCV 作为图像处理库,使用树莓派自带的 Thonny编辑器。
软件部分采用Python语言开发,代码参考了OpenCV例程。Python编辑器采用Thonny。它简单易用,可以在树莓派上轻松使用。它的用户界面直观,具有代码自动缩进、语法高亮等功能。
在工程目录下创建dataset文件夹储存人脸样本,和trainer文件夹储存训练数据。并将OpenCV提供的haarcascade 分类器(haarcascade_frontalface_default.xml)拷贝到工程目录下。目录结构和文件见下图。
dataset:放人脸图片的文件,里面有诸如ABCD文件夹,每个文件夹里是诸如ABCD的数据图片。
faces:数据文件,存放id和name.
face_dataset_01.py: 人脸数据采集
face_training_02.py:训练已有数据并生成模型
face_recognition_03.py:人脸识别比对和开锁工作
首先是人脸数据的采集,在Thonny编辑器中运行程序1。人脸录入过程是用USB摄像头对准屏幕采集预先下载好的人脸图像,能够将视频图像显示到界面中,并对人脸进行检测,将图像中所有的人脸检测出来,并用矩形框框出。
采集30张灰度图做图像特征识别。程序会收集30个样本数据,存储样本数据在dataset中,可在用户界面直接打查看。
运行程序2进行训练人脸特征数据,会读取捕获的人脸图像进行识别训练,并将训练数据保存在程序目录下的文件中。
运行程序3进行人脸识别比对和开锁工作,用摄像头对准事先下载的图片,依次进行识别。
如人像比对不正确,显示unknow。
如人像比对正确,显示OPEN DOOR,并驱动电控锁打开柜门。
四、作品源码
1.face_dataset_01.py: 人脸数据采集
import cv2
import os
cam = cv2.VideoCapture(0)
cam.set(3, 640) # set video width
cam.set(4, 480) # set video height
face_detector = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
# For each person, enter one numeric face id
face_id = input('\n enter user id end press <return> ==> ')
print("\n [INFO] Initializing face capture. Look the camera and wait ...")
# Initialize individual sampling face count
count = 0
while(True):
ret, img = cam.read()
img = cv2.flip(img, -1) # flip video image vertically
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_detector.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
cv2.rectangle(img, (x,y), (x+w,y+h), (255,0,0), 2)
count += 1
# Save the captured image into the datasets folder
cv2.imwrite("dataset/User." + str(face_id) + '.' + str(count) + ".jpg", gray[y:y+h,x:x+w])
cv2.imshow('image', img)
k = cv2.waitKey(100) & 0xff # Press 'ESC' for exiting video
if k == 27:
break
elif count >= 30: # Take 30 face sample and stop video
break
# Do a bit of cleanup
print("\n [INFO] Exiting Program and cleanup stuff")
cam.release()
cv2.destroyAllWindows()
2.face_training_02_02.py:训练已有数据并生成模型
import numpy as np
from PIL import Image
import os
import cv2
# Path for face image database
path = 'dataset'
recognizer = cv2.face.LBPHFaceRecognizer_create()
detector = cv2.CascadeClassifier("haarcascade_frontalface_default.xml");
# function to get the images and label data
def getImagesAndLabels(path):
imagePaths = [os.path.join(path,f) for f in os.listdir(path)]
faceSamples=[]
ids = []
for imagePath in imagePaths:
PIL_img = Image.open(imagePath).convert('L') # convert it to grayscale
img_numpy = np.array(PIL_img,'uint8')
id = int(os.path.split(imagePath)[-1].split(".")[1])
faces = detector.detectMultiScale(img_numpy)
for (x,y,w,h) in faces:
faceSamples.append(img_numpy[y:y+h,x:x+w])
ids.append(id)
return faceSamples,ids
print ("\n [INFO] Training faces. It will take a few seconds. Wait ...")
faces,ids = getImagesAndLabels(path)
recognizer.train(faces, np.array(ids))
# Save the model into trainer/trainer.yml
recognizer.write('trainer/trainer.yml') # recognizer.save() worked on Mac, but not on Pi
# Print the numer of faces trained and end program
print("\n [INFO] {0} faces trained. Exiting Program".format(len(np.unique(ids))))
3、face_recognition_03.py:人脸识别比对和开锁工作
import cv2
import numpy as np
import os
from gpiozero import LED
from time import sleep
# 定义引脚编号
pin = 17
# 创建LED对象
led = LED(pin)
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read('trainer/trainer.yml')
cascadePath = "haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascadePath);
font = cv2.FONT_HERSHEY_SIMPLEX
#iniciate id counter
id = 0
# names related to ids: example ==> 1: id=1, etc
names = ['None', '1', '2', '3', '4', '5']
# Initialize and start realtime video capture
cam = cv2.VideoCapture(0)
cam.set(3, 640) # set video widht
cam.set(4, 480) # set video height
# Define min window size to be recognized as a face
minW = 0.1*cam.get(3)
minH = 0.1*cam.get(4)
#开门
def open_door():
print("开锁...")
#开锁三秒
led.on()
sleep(3)
led.off()
# 清理资源
led.close()
while True:
ret, img =cam.read()
img = cv2.flip(img, -1) # Flip vertically
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(
gray,
scaleFactor = 1.2,
minNeighbors = 5,
minSize = (int(minW), int(minH)),
)
for(x,y,w,h) in faces:
cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
id, confidence = recognizer.predict(gray[y:y+h,x:x+w])
# Check if confidence is less them 100 ==> "0" is perfect match
if (confidence < 60):
id = names[id]
confidence = " {0}%".format(round(100 - confidence))
#cv2.putText(img, str(id), (x+5,y-5), font, 1, (255,255,255), 2)
cv2.putText(img, str("OPEN DOOR"), (x+5,y-5), font, 1, (255,255,255), 2)
#cv2.putText(img, str(confidence), (x+5,y+h-5), font, 1, (255,255,0), 1)
cv2.imshow('camera',img)
open_door()
else:
id = "unknown"
confidence = " {0}%".format(round(100 - confidence))
cv2.putText(img, str(id), (x+5,y-5), font, 1, (255,255,255), 2)
cv2.putText(img, str(confidence), (x+5,y+h-5), font, 1, (255,255,0), 1)
cv2.imshow('camera',img)
k = cv2.waitKey(10) & 0xff # Press 'ESC' for exiting video
if k == 27:
break
# Do a bit of cleanup
print("\n [INFO] Exiting Program and cleanup stuff")
cam.release()
cv2.destroyAllWindows()
五、作品功能演示视频
[localvideo]9c33fb7cb1e544847471943914fb4152[/localvideo]
六、项目总结
在Raspberry Pi上使用OpenCV和Python进行图像处理非常简便快捷,可以实现实时面部检测,检测速度也很快,达到了实用级别。虽然当前使用Haar 级联的设置非常有效,但今后随着深度学习等进步可以提高准确性。
目前项目只是实现了基本功能,后面还需要进一步改进,使用QT完成界面开发,屏幕换用触控屏幕,增加活体检测功能提高安全性。
分享帖子链接
1、【DigiKey“智造万物,快乐不停”创意大赛】+人脸识别储物柜 GPIO设置和柜门机构
https://bbs.eeworld.com.cn/thread-1269691-1-1.html
2、【DigiKey“智造万物,快乐不停”创意大赛】+人脸识别储物柜 摄像头选择与配置
https://bbs.eeworld.com.cn/thread-1269587-1-1.html
3、【DigiKey创意大赛】+收货开箱 https://bbs.eeworld.com.cn/thread-1261214-1-1.html
4、代码下载地址https://download.eeworld.com.cn/detail/xscc/630812
5、word文档下载地址https://download.eeworld.com.cn/detail/xscc/630813
-
上传了资料:
【DigiKey“智造万物,快乐不停”创意大赛】+人脸识别储物柜 开发文档
-
上传了资料:
【DigiKey“智造万物,快乐不停”创意大赛】+人脸识别储物柜 代码
-
回复了主题帖:
【DigiKey“智造万物,快乐不停”创意大赛】+人脸识别储物柜 GPIO设置和柜门机构
Jacktang 发表于 2024-1-13 20:58
电控锁的电源是5V供电吧
锁体和锁舌装在小机箱上比效果更好
就是
- 2024-01-12
-
发表了主题帖:
【DigiKey“智造万物,快乐不停”创意大赛】+人脸识别储物柜 作品提交
先占楼,资料正在整理。
补充内容 (2024-1-17 11:28):
内容见新帖
https://bbs.eeworld.com.cn/thread-1270057-1-1.html
-
发表了主题帖:
【DigiKey“智造万物,快乐不停”创意大赛】+人脸识别储物柜 GPIO设置和柜门机构
储物柜柜门开启通过树莓派GPIO进行,树莓派 4B,有 40 个 GPIO 引脚,树莓派官方操作系统 Raspbian 下,可以使用系统默认安装的 python 中 RPi.GPIO 库,进行操作。
GPIO定义
由于使用的是64位系统,需使用gpiozero进行控制,GPIO Zero库是树莓派官方目前推荐的用于操作树莓派上GPIO口的Python库,该库最早是在RPi.GPIO库之上开发而来的。
官方的镜像已经包含了GPIO Zero库,不用单独安装。
测试一下GPIO控制,首先搭建测试,点亮一个LED灯。
连接图
可以使用下面代码来控制LED亮灭交替闪烁:
# 引入LED类
from gpiozero import LED
from time import sleep
red = LED(17) #物理引脚11
while True:
red.on()
sleep(2)
red.off()
sleep(2)
LED灯点亮,闪烁。
储物柜柜门开锁方式选用储物柜专用电控锁。
这个电控锁设计的很巧妙,是利用一根特殊金属丝,通电后热涨冷缩的原理来控制锁的开闭,控制简单可靠。根据实际电源情况,选用的是5v型号。
通电测试运行可靠。
采用一个塑料盒模拟储物柜
用将锁体和锁舌用热熔胶分别固定在盒身和盒盖上。
开好孔洞将控制线引出。
电路部分,由于电控锁电压为5V,且控制电流较大,树莓派的GPIO为3.3V,不能直接驱动,所以GPIO先控制一个3.3V的继电器,再通过继电器控制电控锁。GPIO使用物理第11引脚,接线如下图所示:
图中中间位置的调试器,只用来给电控锁提供5V电源。
编写如下程序进行测试
from gpiozero import LED
from time import sleep
# 定义引脚编号
pin = 17
# 创建LED对象
led = LED(pin)
print("开锁...")
#开锁三秒
led.on()
sleep(3)
led.off()
# 清理资源
led.close()
运行后,如图
运行程序,输出开锁信息,电控锁动作,储物柜柜门弹开。
[localvideo]38518db66f1cd5f6b5e76373181a2fe7[/localvideo]
-
发表了主题帖:
【DigiKey“智造万物,快乐不停”创意大赛】+人脸识别储物柜 摄像头选择与配置
本设计采用树莓派作为主控实现相关功能,首先安装树莓派系统,教程很多此处不再重复,然后是摄像头的选择。
树莓派上经常使用的摄像头有raspberry pi camera module和通用USB摄像头两种。
raspberry pi camera module
USB摄像头
两者都能满足人脸识别要求,经过试用决定采用USB摄像头,原因是USB摄像头线长便于操作,而raspberry pi camera module线短,要带着树莓派操作,太不方便了。
首先是配置摄像头,安装fswebcam软件包
sudo apt install fswebcam
运行结果如下图
安装成功后,就可以拍照测试了
fswebcam image.jpg
拍照结果见下图
可以看到图像采集成功,但分辨率比较低,可以使用参数-r设置分辨率
fswebcam -r 1280x720 --no-banner image.jpg
拍摄了一幅分辨率为 1280 x 720的图像,至此摄像头配置完毕。
- 2023-12-18
-
加入了学习《【得捷Follow me第3期】项目贴》,观看 【得捷电子Follow me第3期】项目报告
- 2023-12-17
-
上传了资料:
【得捷电子Follow me第3期】项目源码
-
发表了主题帖:
【得捷电子Follow me第3期】项目报告
项目视频: https://training.eeworld.com.cn/video/38855
必做任务1:使用MicroPython系统【对该任务的介绍、功能对应的主要代码片段及说明、功能展示及说明、心得体会建议】
并给开发板刷写MicroPython系统,完成入门程序的运行
这里采用Thonny环境搭建micropython
下载安装Thonny,4.1.4版本安装不上,最后安装3.3.13版本
我通过在线安装方式,开发板刷写MicroPython系统,完成入门程序的运行,不用下载软件,点击就能完成。
https://dev.16302.com/tools/#/
点击 初始化设备(固件)工具 安提示进行。
安装完成后,编写一个 hello world 程序,结果如图
必做任务2:驱动扩展板上的OLED屏幕【对该任务的介绍、功能对应的主要代码片段及说明、功能展示及说明、心得体会建议】
首先通过THONNY集成开发环境加载SSD1306的库文件
显示一个方框,内部写一段文字”Follow me 3”。
import time
from machine import Pin, SoftI2C
import ssd1306
import math
i2c = SoftI2C(scl=Pin(7), sda=Pin(6))# Adjust the Pin numbers based on your connections
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
oled.fill(0)# Clear the screen
oled.text("----------------", 0, 0)
oled.text("Follow me 3", 10, 20)
oled.text("----------------", 0, 40)
oled.rect(0,10, 128,54, 1)
oled.show()# Show the text
结果如图
必做任务3:控制蜂鸣器播放音乐【对该任务的介绍、功能对应的主要代码片段及说明、功能展示及说明、心得体会建议】
IO5脚直接驱动蜂鸣器就行
from machine import Pin, PWM
import time
buzzer = PWM(Pin(5, Pin.OUT))
buzzer.freq(5000)
while True:
buzzer.duty_u16(32767)
time.sleep(1)
buzzer.duty_u16(0)
time.sleep(1)
结果见视频
必做任务4:连接WiFi网络【对该任务的介绍、功能对应的主要代码片段及说明、功能展示及说明、心得体会建议】
ESP 32的最大优势就是联网功能强大,有库支持使用也很简单。
先安装WiFi支持库network
通过WiFi连接授时网站,显示时间和日期。
from machine import Pin, SoftI2C
import ssd1306
from time import sleep
import time
import network
import urequests
import ujson
# ESP32 Pin assignment
# i2c = SoftI2C(scl=Pin(22), sda=Pin(21))
# ESP8266 Pin assignment
i2c = SoftI2C(scl=Pin(7), sda=Pin(6)) # Adjust the Pin numbers based on your connections
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
station = network.WLAN(network.STA_IF)
station.active(True)
# Network settings
wifi_ssid = "100"
wifi_password = "87654321"
url = "http://worldtimeapi.org/api/timezone/America/New_York"
print("Scanning for WiFi networks, please wait...")
authmodes = ['Open', 'WEP', 'WPA-PSK' 'WPA2-PSK4', 'WPA/WPA2-PSK']
for (ssid, bssid, channel, RSSI, authmode, hidden) in station.scan():
print("* {:s}".format(ssid))
print(" - Channel: {}".format(channel))
print(" - RSSI: {}".format(RSSI))
print(" - BSSID: {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}".format(*bssid))
print()
# Continually try to connect to WiFi access point
while not station.isconnected():
# Try to connect to WiFi access point
print("Connecting...")
station.connect(wifi_ssid, wifi_password)
time.sleep(10)
# Display connection details
print("Connected!")
print("My IP Address:", station.ifconfig()[0])
while True:
# Perform HTTP GET request on a non-SSL web
response = urequests.get(url)
# Check if the request was successful
if response.status_code == 200:
# Parse the JSON response
data = ujson.loads(response.text)
# Extract the "datetime" field for New York
ny_datetime = data["datetime"]
# Split the date and time components
date_part, time_part = ny_datetime.split("T")
# Get only the first two decimal places of the time
time_part = time_part[:8]
# Get the timezone
timezone = data["timezone"]
# Clear the OLED display
oled.fill(0)
# Display the New York date and time on separate lines
oled.text("XIAN Date:", 0, 0)
oled.text(date_part, 0, 10)
oled.text("XIAN Time:", 0, 20)
oled.text(time_part, 0, 30)
oled.text("Timezone:", 0, 40)
oled.text(timezone, 0, 50)
# Update the display
oled.show()
else:
oled.text("Failed to get the time for XIAN!")
# Update the display
oled.show()
设置好自己wifi的用户名和密码,安装好外接天线,联网过程稍微慢一些,结果如下图
必做任务5:使用外部传感器【对该任务的介绍、功能对应的主要代码片段及说明、功能展示及说明、心得体会建议】
使用温湿度传感器和光照传感器采集数据,显示在OLED上。
首先安装ahtx0库文件,用来支持AHT20 温湿度传感器,是IIC接口,光照传感器是模拟量接口,两个传感器接到对应的扩展口上,不要接错了。
from machine import Pin, SoftI2C, ADC
import ssd1306
import utime
import time
from ahtx0 import AHT20
i2c = SoftI2C(scl=Pin(7), sda=Pin(6))
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
# 清空屏幕,并显示任务要求
oled.fill(0)
oled.text("Light:", 0, 48)
oled.text("Temp:", 0, 16)
oled.text("Humi:", 0, 32)
oled.show()
# aht20
aht = AHT20(i2c)
# 光照部分
adc = ADC(Pin(2))
adc.atten(ADC.ATTN_11DB)
adc.width(ADC.WIDTH_12BIT) #4095
while True:
temp = aht.temperature
humi = aht.relative_humidity
light_adc = adc.read()
# 计算光照强度单位Lux
light_lux = light_adc * 350 * 1.0 / 4095
# 算出电阻值单位K
light_res = (4095 - light_adc) * 10.0 / light_adc
print("Temp(°):\n");
print('{:.2f}'.format(temp))
print("Humi(%):\n");
print('{:.2f}'.format(humi))
print("Light(lux)\n");
print('{:.2f}'.format(light_lux))
print("Light(K)\n");
print('{:.2f}'.format(light_res))
# 清除变化部分的内容
oled.fill_rect(64,16,64,48,0)
oled.text('{:.2f}'.format(temp), 64, 16)
oled.text('{:.2f}'.format(humi), 64, 32)
oled.text('{:.2f}'.format(light_lux), 64, 48)
oled.show()
# 延时1秒
time.sleep(1)
运行结果见下图
心得体会
Seeed Studio XIAO开发板虽然体积小但功能强大,得益于 ESP32C3芯片主频高,存储也不小、联网功能还强,结合不错的低功耗特性,在可穿戴设备开发方面优势不少。
源码已上传
https://download.eeworld.com.cn/detail/xscc/630303