回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

#创客来了#努力学习提醒机 简单

头像 云天 2021.08.17 809 1

步骤1 项目背景

夏天已到,火辣辣的太阳晒着大地,好像要把所有的东西都熔化掉似的。机器在不停地转动着,在这样的一个季节学习变得更加的艰辛了。由太阳散发出的热量会导致人们精神疲劳,因此在夏天需要有个小伙伴在我们瞌睡提醒我们,继续努力学习。就像每天早起时,我们需要“闹钟”叫醒我们。

步骤2 设计思路

本项目,使用树莓派上的摄像头获取学习者的人脸图像,通过EAR算法,判断瞌睡状态,利用声光提醒。如果还瞌睡,将使用“喷雾”装置进行提醒。

project-image

步骤3 硬件连接

project-image

步骤4 LED测试

 

LED灯接在树莓派(显示屏)的27引脚上。

project-image
代码

import RPi.GPIO as GPIO

import time

GPIO.setmode(GPIO.BCM)

GPIO.setup(27,GPIO.OUT)

for i in range(30):

  GPIO.output(27,GPIO.HIGH)

  time.sleep(1)

  GPIO.output(27,GPIO.LOW)

  time.sleep(1)

GPIO.cleanup()

步骤5 录放模块测试

 

录放模块接在接在显示屏的Gravity GPIO 引脚,对应引脚D26。

project-image
project-image
代码

import RPi.GPIO as GPIO

import time

GPIO.setmode(GPIO.BCM)

GPIO.setup(26,GPIO.OUT)

for i in range(30):

  GPIO.output(26,GPIO.HIGH)

  GPIO.output(26,GPIO.LOW)

  time.sleep(5)

GPIO.cleanup()

步骤6 喷雾装置测试

喷雾装置中的舵机接在21引脚上,因使用的是DSS-M15舵机,因电流较大,单独供电。(大型号的舵机电流过大对控制板安全不利。)

project-image

GPIO的控制电压是 3.3 V。

树莓派1代B型的26个针脚里,有一个特殊的GPIO口是支持硬件PWM的,不过从B+开始不知道什么原因这个很实用的接口被去掉了。

代码

import RPi.GPIO as GPIO

import time

GPIO.setmode(GPIO.BCM)

GPIO.setup(21, GPIO.OUT)
# 初始化PWM的频率,frequency=50Hz
pwm = GPIO.PWM(21, 50)
pwm.start(2.5)
try:
 for i in range(5):
# 修改pwm占空比
 
  pwm.ChangeDutyCycle(3.0)
  time.sleep(0.5)
  
  pwm.ChangeDutyCycle(10)
  time.sleep(3)

except KeyboardInterrupt:#试图强制中断时,程序不会马上停止而是会先跳到这里来做一些你想做完的事情,比如清理GPIO口。
 pass
# 关闭pwm
pwm.stop()
GPIO.cleanup()

步骤7 程序环境搭建

 

(1)Python3.7安装与环境搭建 Python官网

(2)Opencv4.1 安装与环境搭建 Opencv官网

(3)安装pip 21.1.1(它是Python包管理工具,提供了Python包的查找、下载、安装、卸载的功能,有利于之后的环境搭建。)

(以下操作都可以用pip进行安装)

Win+R窗口输入“cmd”,然后输入对应的库安装,

(4)安装opencv-python 4.2.0.34 计算机视觉库。

在cmd里输入在线安装“pip3 install opencv-python”

(5)安装Numpy 1.18.5 开源的数值计算扩展,功能是科学计算,数据分析与处理。

“pip3 install Numpy”

(6)安装Scipy 1.7.0 距离计算库。

“pip3 install Scipy”

步骤8 人眼疲倦检测开源算法

 

EAR(eye aspect ratio)计算函数 

我们首先需要确定眼睛的位置,在确定眼睛位置之后,选择6个点来表示眼睛,具体如下图所示:

project-image

标号的顺序是从眼睛的左角开始,然后顺时针绕着眼睛进行编号。

根据这六个点我们便可以表示眼睛的睁开和闭上的状态。当开启的时候,上图中竖着的黄色箭头会变得比较高,而眼睛闭上(疲劳状态)这个箭头就会变矮。但是由于观看的距离不同,单纯用高度来表示状态缺少参考比较,因此提出如下公式表示状态:

project-image

用这个数据便可以相对客观的表示眼睛的状态,于是通过大量测试发现一个统计结果,当EAR小于0.25很多的时候,便是疲劳状态。

project-image

步骤9 face_recognition人脸识别库

 

1、安装

face_recognition使用世界上最简单的人脸识别库,在Python或命令行中识别和操作人脸。 使用dlib最先进的人脸识别技术构建而成,并具有深度学习功能。

pip3 install face_recognition

2、识别人脸关键点

加载图像后,调用face_recognition.face_landmarks(image)可识别出人脸关键点信息,包括眼睛、鼻子、嘴巴和下巴等,参数仍是加载的图像image,返回值是包含面部特征字典的列表,列表中每一项对应一张人脸,包括nose_bridge、right_eyebrow、right_eye、chine、left_eyebrow、bottom_lip、nose_tip、top_lip、left_eye几个部分,每个部分包含若干个特征点(x,y),总共有68个特征点。列表长度就是图中识别出的人脸数,可遍历此列表和字典的键值对,打印出所有面部特征点,也可在图像上画出来,代码如下:

代码
import numpy as np
import cv2
import face_recognition

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    print(ret)
    frame = cv2.resize(frame, (0,0), fx=0.2, fy=0.2)

    # Find all facial features in all the faces in the video
    face_landmarks_list = face_recognition.face_landmarks(frame)

    for face_landmarks in face_landmarks_list:
        # Loop over each facial feature (eye, nose, mouth, lips, etc)
        for name, list_of_points in face_landmarks.items():
          #if name=="left_eye":
            hull = np.array(face_landmarks[name])
            hull_landmark = cv2.convexHull(hull)
            cv2.drawContours(frame, hull_landmark, -1, (0, 255, 0), 3)
    frame = cv2.resize(frame, (0,0), fx=2, fy=2)

    cv2.imshow("Frame", frame)

    ch = cv2.waitKey(1)
    if ch & 0xFF == ord('q'):
        break
cap.release()
cv2.destroyAllWindows()
project-image
project-image

步骤10 眼睛纵横比(EAR)函数

# 这个方程的分子是计算垂直眼睛标志之间的距离,而分母是计算水平眼睛标志之间的距离,由于水平点只有一组,而两组垂直点,所以分母乘上了2,以保证两组特征点的权重相同。使用这个简单的方程,我们可以避免使用图像处理技术,简单地依靠眼睛地标距离的比例来确定一个人是否眨眼。

def eye_aspect_ratio(eye):

             # 计算距离,竖直的

         A = dist.euclidean(eye[1], eye[2])

         B = dist.euclidean(eye[4], eye[5])

             # 计算距离,水平的

         C = dist.euclidean(eye[0], eye[3])

             # ear值

         ear = (A + B) / (2.0 * C)

         return ear

步骤11 判断瞌睡代码

代码
import numpy as np
import cv2
import face_recognition
from scipy.spatial import distance as dist
cap = cv2.VideoCapture(0)
# 眼睛纵横比(EAR)
# 这个方程的分子是计算垂直眼睛标志之间的距离,而分母是计算水平眼睛标志之间的距离,由于水平点只有一组,而两组垂直点,所以分母乘上了2,以保证两组特征点的权重相同。使用这个简单的方程,我们可以避免使用图像处理技术,简单地依靠眼睛地标距离的比例来确定一个人是否眨眼。
def eye_aspect_ratio(eye):
        #计算距离,竖直的
        A=dist.euclidean(eye[1],eye[5])
        B=dist.euclidean(eye[2],eye[4])
        #计算距离,水平的
        C=dist.euclidean(eye[0],eye[3])
        #ear值
        ear=(A+B)/(2.0*C)
        return ear

close_eye=0 # 闭眼计数
ClOSE_EYE=25 # 闭眼次数阈值
EYE_EAR = 0.25 # EAR阈值
while True:
    ret, frame = cap.read()

    frame = cv2.resize(frame, (0,0), fx=0.2, fy=0.2)

    # Find all facial features in all the faces in the video
    face_landmarks_list = face_recognition.face_landmarks(frame)
    left_eye_points=[]
    right_eye_points=[]
    for face_landmarks in face_landmarks_list:
        # Loop over each facial feature (eye, nose, mouth, lips, etc)
        for name, list_of_points in face_landmarks.items():
           if name=='left_eye':
            left_eye_points=list_of_points
            hull = np.array(face_landmarks[name])
            hull_landmark = cv2.convexHull(hull)
            cv2.drawContours(frame, hull_landmark, -1, (0, 255, 0), 3)
           if name=='right_eye':
            right_eye_points=list_of_points
            hull = np.array(face_landmarks[name])
            hull_landmark = cv2.convexHull(hull)
            cv2.drawContours(frame, hull_landmark, -1, (0, 255, 0), 3)
    if len(right_eye_points)!=0 and len(left_eye_points)!=0:
      # 分别计算两眼ear值
      leftEAR = eye_aspect_ratio(left_eye_points)
      rightEAR = eye_aspect_ratio(right_eye_points)
      # 算一个平均的ear值
      ear = (leftEAR + rightEAR) / 2.0

      # 当“ear”小于阈值时,为闭眼一次
      if ear<EYE_EAR:
          close_eye=close_eye+1
      else:
          close_eye=0

      if close_eye>ClOSE_EYE:
          cv2.putText(frame,"Sleep!",(20,200),cv2.FONT_HERSHEY_SIMPLEX,2,(0,0,255),3)
      cv2.putText(frame,"close_eye:"+str(close_eye),(30,30),cv2.FONT_HERSHEY_SIMPLEX,1,(0,0,255),1)
      print(ear)
    frame = cv2.resize(frame, (0,0), fx=2, fy=2)
    cv2.imshow("Frame", frame)

    ch = cv2.waitKey(1)
    if ch & 0xFF == ord('q'):
        break
cap.release()
cv2.destroyAllWindows()
project-image

步骤12 组装设备

project-image
project-image
project-image

评论

user-avatar
  • hacker_

    hacker_2023.05.10

    666

    0