思路
视觉实现需要使用Google的mediapipe这个model,碍于本作者手中没有派派或者英伟达的板子,只能使用PC去实现这个功能。五自由度舵机机械臂,这没啥好说的。
理论成立,开始实践!!!
硬件准备
五自由度机械臂:
电脑:
arduino
电源加电源控制电路,这个可以得到5V就可以
软件准备
PyCharm或者其他编译器也可以,只要能够编译运行python代码
arduino IDE
思路分析
通过mediapipe的关键点检测,并通过各个关键点之间的angle来确定手势,通过串口跟下位机(arduino)进行通信,从而操控舵机的转动。
实现
python代码
import cv2
import mediapipe as mp
import math
import time
import serial
def vector_2d_angle(v1, v2):
"""
求解二维向量的角度
"""
v1_x = v1[0]
v1_y = v1[1]
v2_x = v2[0]
v2_y = v2[1]
try:
angle_ = math.degrees(math.acos(
(v1_x * v2_x + v1_y * v2_y) / (((v1_x ** 2 + v1_y ** 2) ** 0.5) * ((v2_x ** 2 + v2_y ** 2) ** 0.5))))
except:
angle_ = 65535.
if angle_ > 180.:
angle_ = 65535.
return angle_
def hand_angle(hand_):
'''
获取对应手相关向量的二维角度,根据角度确定手势
'''
angle_list = []
# ---------------------------- thumb 大拇指角度
angle_ = vector_2d_angle(
((int(hand_[0][0]) - int(hand_[2][0])), (int(hand_[0][1]) - int(hand_[2][1]))),
((int(hand_[3][0]) - int(hand_[4][0])), (int(hand_[3][1]) - int(hand_[4][1])))
)
angle_list.append(angle_)
# ---------------------------- index 食指角度
angle_ = vector_2d_angle(
((int(hand_[0][0]) - int(hand_[6][0])), (int(hand_[0][1]) - int(hand_[6][1]))),
((int(hand_[7][0]) - int(hand_[8][0])), (int(hand_[7][1]) - int(hand_[8][1])))
)
angle_list.append(angle_)
# ---------------------------- middle 中指角度
angle_ = vector_2d_angle(
((int(hand_[0][0]) - int(hand_[10][0])), (int(hand_[0][1]) - int(hand_[10][1]))),
((int(hand_[11][0]) - int(hand_[12][0])), (int(hand_[11][1]) - int(hand_[12][1])))
)
angle_list.append(angle_)
# ---------------------------- ring 无名指角度
angle_ = vector_2d_angle(
((int(hand_[0][0]) - int(hand_[14][0])), (int(hand_[0][1]) - int(hand_[14][1]))),
((int(hand_[15][0]) - int(hand_[16][0])), (int(hand_[15][1]) - int(hand_[16][1])))
)
angle_list.append(angle_)
# ---------------------------- pink 小拇指角度
angle_ = vector_2d_angle(
((int(hand_[0][0]) - int(hand_[18][0])), (int(hand_[0][1]) - int(hand_[18][1]))),
((int(hand_[19][0]) - int(hand_[20][0])), (int(hand_[19][1]) - int(hand_[20][1])))
)
angle_list.append(angle_)
return angle_list
def h_gesture(angle_list):
"""
# 二维约束的方法定义手势
# fist five gun love one six three thumbup yeah
"""
thr_angle = 65.
thr_angle_thumb = 53.
thr_angle_s = 49.
gesture_str = None
if 65535. not in angle_list:
# if (angle_list[0] > thr_angle_thumb) and (angle_list[1] > thr_angle) and (angle_list[2] > thr_angle) and (
# angle_list[3] > thr_angle) and (angle_list[4] > thr_angle):
# gesture_str = "fist"
if (angle_list[0] < thr_angle_s) and (angle_list[1] < thr_angle_s) and (angle_list[2] < thr_angle_s) and (
angle_list[3] < thr_angle_s) and (angle_list[4] < thr_angle_s):
gesture_str = "five"
# elif (angle_list[0] < thr_angle_s) and (angle_list[1] < thr_angle_s) and (angle_list[2] > thr_angle) and (
# angle_list[3] > thr_angle) and (angle_list[4] > thr_angle):
# gesture_str = "gun"
elif (angle_list[0] < thr_angle_s) and (angle_list[1] < thr_angle_s) and (angle_list[2] > thr_angle) and (
angle_list[3] > thr_angle) and (angle_list[4] < thr_angle_s):
gesture_str = "love"
elif (angle_list[0] > 5) and (angle_list[1] < thr_angle_s) and (angle_list[2] > thr_angle) and (
angle_list[3] > thr_angle) and (angle_list[4] > thr_angle):
gesture_str = "one"
elif (angle_list[0] < thr_angle_s) and (angle_list[1] > thr_angle) and (angle_list[2] > thr_angle) and (
angle_list[3] > thr_angle) and (angle_list[4] < thr_angle_s):
gesture_str = "six"
elif (angle_list[0] > thr_angle_thumb) and (angle_list[1] < thr_angle_s) and (angle_list[2] < thr_angle_s) and (
angle_list[3] < thr_angle_s) and (angle_list[4] > thr_angle):
gesture_str = "three"
elif (angle_list[0] > thr_angle_thumb) and (angle_list[1] < thr_angle_s) and (angle_list[2] < thr_angle_s) and (
angle_list[3] < thr_angle_s) and (angle_list[4] < thr_angle):
gesture_str = "four"
# elif (angle_list[0] < thr_angle_s) and (angle_list[1] > thr_angle) and (angle_list[2] > thr_angle) and (
# angle_list[3] > thr_angle) and (angle_list[4] > thr_angle):
# gesture_str = "thumbUp"
elif (angle_list[0] > thr_angle_thumb) and (angle_list[1] < thr_angle_s) and (angle_list[2] < thr_angle_s) and (
angle_list[3] > thr_angle) and (angle_list[4] > thr_angle):
gesture_str = "two"
elif (angle_list[0] < thr_angle_s) and (angle_list[1] > thr_angle) and (angle_list[2] > thr_angle) and (
angle_list[3] > thr_angle) and (angle_list[4] > thr_angle_s):
gesture_str = "Good"
elif (angle_list[0] > thr_angle_thumb) and (angle_list[1] > thr_angle_s) and (angle_list[2] < thr_angle_s) and (
angle_list[3] > thr_angle) and (angle_list[4] > thr_angle):
gesture_str = "Fuck!!!"
return gesture_str
def detect():
serialPort = "COM5" # 串口
baudRate = 115200 # 波特率
ser = serial.Serial(serialPort, baudRate, timeout=0.5)
time.sleep(5)
print("参数设置:串口=%s ,波特率=%d" % (serialPort, baudRate))
mp_drawing = mp.solutions.drawing_utils
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
static_image_mode=False,
max_num_hands=1,
min_detection_confidence=0.75,
min_tracking_confidence=0.75)
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = cv2.flip(frame, 1)
results = hands.process(frame)
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
hand_local = []
for i in range(21):
x = hand_landmarks.landmark[i].x * frame.shape[1]
y = hand_landmarks.landmark[i].y * frame.shape[0]
hand_local.append((x, y))
if hand_local:
angle_list = hand_angle(hand_local)
gesture_str = h_gesture(angle_list)
if gesture_str == 'one':
ser.write(b"1") # ser.write在于向串口中写入数据
print(1)
elif gesture_str == 'two':
ser.write(b"2") # ser.write在于向串口中写入数据
print(2)
elif gesture_str == 'three':
ser.write(b"3") # ser.write在于向串口中写入数据
print(3)
elif gesture_str == 'four':
ser.write(b"4") # ser.write在于向串口中写入数据
print(4)
elif gesture_str == 'five':
ser.write(b"5") # ser.write在于向串口中写入数据
print(5)
cv2.putText(frame, gesture_str, (0, 100), 0, 1.3, (0, 0, 255), 3)
cv2.imshow('MediaPipe Hands', frame)
if cv2.waitKey(1) & 0xFF == 27:
break
cap.release()
ser.close()
if __name__ == '__main__':
detect()
arduino代码:
/*
Uart output and input
*/
#include <Servo.h> //加载文件库
int pos = 0;
Servo myservo1;
Servo myservo2;
Servo myservo3;
Servo myservo4;
// 初始化串口波特率为11520:
void setup() {
// 使用Serial.begin()函数来初始化串口波特率,参数为要设置的波特率
Serial.begin(115200);
pinMode(13, OUTPUT);//设置13号端口作为输出端口
digitalWrite(13,HIGH);//让灯开始时亮
myservo1.attach(3, 500, 2500); //修正脉冲宽度
myservo2.attach(5, 500, 2500); //修正脉冲宽度
myservo3.attach(6, 500, 2500); //修正脉冲宽度
myservo4.attach(9, 500, 2500); //修正脉冲宽度
}
char var;
// the loop routine runs over and over again forever:
void loop() {
while(Serial.available()>0)//当有信号的时候
{
var=Serial.read();
if(var=='1')//传过来的是1
{
// digitalWrite(13,HIGH);
// myservo1.write(90);
// myservo2.write(90);
// myservo3.write(90);
myservo4.write(80);
}
else if(var=='2')//传过来的是2
{
// digitalWrite(13,HIGH);
// myservo1.write(180);
// myservo2.write(180);
// myservo3.write(180);
myservo4.write(145);
}
}
}
这样就做好了,舵机控制那可以自行设计。