所有分类
主题 主题
平台 平台
我的工作台
userHead
注册时间 [[userInfo.create_time]]
创造力 [[userInfo.creativity]]
[[userInfo.remark]]
[[d.project_title]]
articleThumb
[[d.material_name]]
timelineThumb
进入工作台
折叠
所有分类 我的工作台
展开

【科学探究】手势控制播报温湿度

云天 云天 2022-05-07 19:39:11

一、前言——Mediapipe

基于Google的Mediapipe框架,利用其自身返回的坐标,计算手指的弯曲角度。效果图如下。

projectImage

二、实现过程

手部的节点,每只手有21个。

projectImage

上面“手掌”的部分节点序号标在手上是这样的。

projectImage

现在知道了坐标,也知道了点在哪,开始计算手指运动过程中的夹角。以食指为例,计算∠876。推导过程比较简单,因为知道三个点的xy坐标,借助三角函数弧度rad=arctan(y/x)分别求出两条直线的正切角,再相减即可。

projectImage

Python里该如何实现呢,以右手掌为例。首先,视频流处理函数返回右手地标right_hand_landmarks,该地标包含坐标xyz与可见度visibility两个信息,只需提取坐标landmark。而landmark就像是数组,只要知道对应节点的序号(索引)就能提取到坐标。

代码 代码
	                    					#以提取右手食指指尖坐标为例,由前述可知指尖序号为8
results = holistic.process(image)
if results.right_hand_landmarks:
    RHL = results.right_hand_landmarks
	coord_8=[RHL.landmark[8].x, RHL.landmark[8].y]
	#coord_8便是指尖的xy坐标

	                    				

接下来借助for循环来批量操作。

代码 代码
	                    					#食指、中指、无名指、小手指
joint_list = [[8, 7, 6], [12, 11, 10], [16, 15, 14], [20, 19, 18]]  # 手指关节序列
if results.right_hand_landmarks:
	RHL = results.right_hand_landmarks
	for joint in joint_list:
		a = np.array([RHL.landmark[joint[0]].x, RHL.landmark[joint[0]].y])
		b = np.array([RHL.landmark[joint[1]].x, RHL.landmark[joint[1]].y])
		c = np.array([RHL.landmark[joint[2]].x, RHL.landmark[joint[2]].y])

	                    				

这样,a, b, c就是三个点的xy坐标。再借助np.arctan2函数计算反正切,然后将计算出来的弧度转为角度。

代码 代码
	                    					# 计算弧度
radians_fingers = np.arctan2(c[1] - b[1], c[0] - b[0]) - np.arctan2(a[1] - b[1], a[0] - b[0])
angle = np.abs(radians_fingers * 180.0 / np.pi)  # 弧度转角度

	                    				

有必要说一下,arctan2函数和arctan函数有所不同。arctan的值域是[-π/2, π/2],arctan2的值域是[-π, π]。毕竟手指伸直可以达到180°,还是用arctan2函数好。
 有了角度,再利用cv2.putText()函数把角度数据实时渲染在手指旁边。
 

代码 代码
	                    					cv2.putText(image, str(round(angle, 2)), tuple(np.multiply(b, [640, 480]).astype(int)),
           cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)
#渲染的位置在三个点的中间也就是坐标b的位置

	                    				

三、硬件

DHT20是一款智能温湿度传感器模块,是DHT11的全新升级产品。采用完全校准的数字I2C接口,响应迅速,抗干扰能力强,性价比更高。

projectImage
projectImage

四、Mind+编程

projectImage

五、Python程序读取温湿度

代码 代码
	                    					import serial
import time
ser = serial.Serial('COM4', 115200, timeout=0)
Timestamp = str(int(time.time()))
if __name__ == '__main__':
     while True:
                time.sleep(2)
                count = ser.inWaiting() 
                if count > 0: 
                  data = ser.read(count) 
                  if data != b'':
                   data=str(data,"utf-8")
                   data=data.replace("\r\n","")
                   data=data.split(",")
                   print(data[0],data[1])

	                    				
projectImage

六、语音播报

使用pyttsx3的语音合成功能。

代码 代码
	                    					import pyttsx3

# 初始化一个朗读引擎
engine = pyttsx3.init()
# 阅读
engine.say("现在的温度是")
engine.say("现在的湿度是")
# 运行并且等到播放完毕
engine.runAndWait()
	                    				

七、Python完整代码

projectImage
projectImage

当食指与中指夹角超过50度时,播报语音播报“温湿度”。

代码 代码
	                    					import cv2
import numpy as np
import mediapipe as mp
import serial
import pyttsx3
engine = pyttsx3.init()
ser = serial.Serial('COM4', 115200, timeout=0)
mp_drawing = mp.solutions.drawing_utils
mp_holistic = mp.solutions.holistic

joint_list = [[6,9, 10],[4,3, 2],[8, 7, 6], [12, 11, 10], [16, 15, 14], [20, 19, 18]]  # 手指关节序列

cap = cv2.VideoCapture(0)
with mp_holistic.Holistic(
        min_detection_confidence=0.5,
        min_tracking_confidence=0.5) as holistic:
    while cap.isOpened():
        success, image = cap.read()
        if not success:
            print("Ignoring empty camera frame.")
            break
        image.flags.writeable = False
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        results = holistic.process(image)
        image.flags.writeable = True
        image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

        # 渲染
        #mp_drawing.draw_landmarks(image,results.face_landmarks)
        #mp_drawing.draw_landmarks(image,results.pose_landmarks)

        mp_drawing.draw_landmarks(image, results.left_hand_landmarks)
        mp_drawing.draw_landmarks(image, results.right_hand_landmarks)
        # 监测到右手,执行
        if results.right_hand_landmarks:
            RHL = results.right_hand_landmarks
            # 计算角度
            for joint in joint_list:
                a = np.array([RHL.landmark[joint[0]].x, RHL.landmark[joint[0]].y])
                b = np.array([RHL.landmark[joint[1]].x, RHL.landmark[joint[1]].y])
                c = np.array([RHL.landmark[joint[2]].x, RHL.landmark[joint[2]].y])
                # 计算弧度
                radians_fingers = np.arctan2(c[1] - b[1], c[0] - b[0]) - np.arctan2(a[1] - b[1], a[0] - b[0])
                angle = np.abs(radians_fingers * 180.0 / np.pi)  # 弧度转角度

                if angle > 180.0:
                    angle = 360 - angle

                cv2.putText(image, str(round(angle, 2)), tuple(np.multiply(b, [640, 480]).astype(int)),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)
                
        if results.left_hand_landmarks:
            RHL = results.left_hand_landmarks
            # 计算角度
            for joint in joint_list:
                a = np.array([RHL.landmark[joint[0]].x, RHL.landmark[joint[0]].y])
                b = np.array([RHL.landmark[joint[1]].x, RHL.landmark[joint[1]].y])
                c = np.array([RHL.landmark[joint[2]].x, RHL.landmark[joint[2]].y])
                # 计算弧度
                radians_fingers = np.arctan2(c[1] - b[1], c[0] - b[0]) - np.arctan2(a[1] - b[1], a[0] - b[0])
                angle = np.abs(radians_fingers * 180.0 / np.pi)  # 弧度转角度

                if angle > 180.0:
                    angle = 360 - angle

                cv2.putText(image, str(round(angle, 2)), tuple(np.multiply(b, [640, 480]).astype(int)),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)
                if joint[0]==6:
                    if angle>50:
                        count = ser.inWaiting() 
                        if count > 0: 
                            data = ser.read(count) 
                            if data != b'':
                              data=str(data,"utf-8")
                              data=data.replace("\r\n","")
                              data=data.split(",")
                              
                              engine.say("现在的温度是"+str(data[0]))
                              engine.say("现在的湿度是"+str(data[1]))
                              # 运行并且等到播放完毕
                              engine.runAndWait()
        # cv2.imshow('MediaPipe Holistic', cv2.flip(image, 1))
        cv2.imshow('Mediapipe Holistic', image)  # 取消镜面翻转
        if cv2.waitKey(5) == ord('q'):
            break
cap.release()

	                    				

八、演示视频

Makelog作者原创文章,未经授权禁止转载。
0
0
评论
[[c.user_name]] [[c.create_time]]
[[c.parent_comment.count]]
|
[[c.comment_content]]