引言
我是一名专注于机器学习和机器人技术自由者。我的热情始于大学期间的人工智能课程,这促使我探索人机交互的新方法。尤其对于机械臂的操作,我一直想要简化其复杂性,使之更加直观和易于使用。
这个项目的灵感源自于我对创新技术的热爱以及对改善人机互动方式的追求。我的目标是开发一个基于手势的机械臂控制系统,使非专业人士也能轻松操作。为此,我选择了Google的MediaPipe库进行手势识别,并以myCobot 320 m5作为实验平台。
技术概述
Google MediaPipe
MediaPipe是由Google开发的一个开源跨平台框架,专门用于构建各种感知管道。这个框架提供了丰富的工具和预先构建的模块,使得开发者能够轻松地构建和部署复杂的机器学习模型和算法,尤其在图像和视频分析方面。
MediaPipe的一个显著特点是它对实时手势和面部识别的支持。它能够高效地处理视频流,并实时识别和追踪人的手势、面部特征等。这种能力使其在交互式应用程序、增强现实(AR)、虚拟现实(VR)以及机器人技术中变得极其有用。
你可以尝试试用一下手势识别在线功能,无需安装。
https://mediapipe-studio.webapps.google.com/home
它的简单易用的API和丰富的文档使得更容易集成这个框架,非常适合使用在机器学习和计算机视觉领域当中。
pymycobot
pymycobot 是一个用于与 mycobot 机械臂进行串行通信和控制的 Python API。这个库是为了方便开发者使用 Python 语言控制 mycobot 机械臂而设计的。它提供了一系列的函数和命令,让用户可以通过编程方式控制机械臂的动作和行为。例如,用户可以使用该库获取机械臂的角度、发送角度指令来控制机械臂的移动,或者获取和发送机械臂的坐标信息。
使用这个库唯一的标准是,得使用mycobot 系列的机械臂,这是专门为mycobot进行适配的一款机械臂。
产品介绍
myCobot 320M5 stack
myCobot 320 M5 是大象机器人开发的一款面向用户的六轴协作机械臂。它具有350mm的工作半径和最大1000g的负载能力。该机械臂适用于开放的ROS仿真开发环境,并包含运动学正逆解算法。它支持多种编程语言,包括Python、C++、Arduino、C# 和 JavaScript,且兼容Android、Windows、Mac OSX和Linux平台。myCobot 320 M5的多功能性使其适用于多种开发和集成应用。
2D 相机
一个能够安装在mycobot320末端的2D相机,用USB数据线进行通信。能够呈现机械臂末端所看到的视野。
开发过程
项目架构
我将该项目主要分为三个木块功能:
Gesture Recognition: 主要用来处理手势的识别,能够返回信息当欠手势是什么,比如说竖大拇指等等。
Robotic Arm Control:主要功能用于设置机械臂的运动控制,例如坐标控制,角度控制等等。
Program logic:用来处理程序运行的逻辑,设置确认手势时间,重置识别时间等,后续将一一详细介绍。
编译环境
操作系统:windows 11
编程语言:Python3.9+
使用的库:opencv,mediapipe,pymycobot,time
手势的识别
做识别首先得获得到相机的一个画面,这里我们就用到了opencv库来获取相机的画面
import cv2# 获取相机流,默认的摄像头-0 外接的摄像头按照顺序往上- 1,2,3
cap = cv2.VideoCapture(1)# 持续获取相机画面
while cap.isOpened():
#获取当前图像画面
ret, frame = cap.read()
# 将BGR图像转换为RGB
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 在电脑中显示画面
cv2.imshow('gesture control',frame)
# 按下 'q' 键退出,避免死循环
if cv2.waitKey(1) & 0xFF == ord('q'):
break
到这里图像画面就获取成功了,接下来我们用mediapipe手势进行识别。
import mediapipe as mp# 初始化MediaPipe Hands模块
mp_hands = mp.solutions.hands
hands = mp_hands.Hands()
mp_draw = mp.solutions.drawing_utils# 处理图像并检测手部
result = hands.process(rgb_frame)if result.multi_hand_landmarks:
for hand_landmarks in result.multi_hand_landmarks:
mp_draw.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
这是识别手势之后的输出的结果,它能够精准的识别出手上的每个关节,并且将每个关节的点都命名。MediaPipe Hands 提供了21个手部关键点(landmarks),这些关键点共同描绘了手的结构,包括手腕、各个手指的各个关节。以大拇指举例子,一共有四个关节,从下往上分别是CMC,MCP,IP,TIP.
cmc: Carpometacarpal Joint
mcp:Metacarpophalangeal Joint
ip:Interphalangeal Joint
tip:tip
有了这些还不够,我们要让他识别特定的手势,要需要去设定一个方法,来确定这个手势,比如说我想要一个手势是竖大拇指,那么我们分析在竖大拇指的时候,拇指的指尖的位置是在整个手掌的最上方,这样就容易多了。只要确定在画面中大拇指的指尖是高于其他所有手指头的都指尖,那么这个手势就是竖大拇指。(也可以通过别的进行分析)
一般情况下,我们可以获取到某个关节的X,Y,Z的三个属性,表示改关节在图像中的位置。
#获取大拇指指尖的属性
thump_tip = hand_landmarks.landmark[mp.hands.HandLandmark.THUMB_TIP]#获取大拇指指尖的高度
thump_tip.y#判断竖大拇指手势
def is_thump_up(hand_landmarks):thumb_tip = hand_landmarks.landmark[mp_hands.HandLandmark.THUMB_TIP]
index_tip = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP]
# 判断那个关节比较高。
if thumb_tip.y < index_tip.y:
return True return False
如果想要别的手势的话,也可以根据手型的特点来设定一个专门的辨别方法。到这里手势的识别就完成了。
机械臂运动控制
我一开始的想法是,当相机识别到手势的时候就会给机械臂发送一条控制命令,这里我们先简单的设置一个让机械臂点头的动作。
pymycobot 这个功能库,开放了很多功能在控制机械臂的时候非常的方便。
from pymycobot.mycobot import Mycobot
import time
#链接机械臂
mc = Mycobot(port,baud)#以关节角度控制机械臂运动
mc.send_angles([angles_list],speed)#以坐标控制机械臂进行运动
mc.send_coords([coords_list],speed,mode)#点头动作
def ThumpUpAction(self): self.mc.send_angles([0.96, 86.22, -98.26, 10.54, 86.92, -2.37], 60)
time.sleep(1.5)
for count in range(3):
self.mc.send_angles([0.79, 2.46, (-8.17), 4.3, 88.94, 0.26], 70)
time.sleep(1)
self.mc.send_angles([(-3.6), 30.32, (-45.79), (-46.84), 97.38, 0.35], 70)
time.sleep(1)
self.mc.send_angles([0.79, 2.46, (-8.17), 4.3, 88.94, 0.26], 70)
time.sleep(1)
self.mc.send_angles([0.96, 86.22, -98.26, 10.54, 86.92, -2.37], 60)
为了让整体代码看起来可读性高,可修改性高,创建机械臂类方便进行调用和修改
class RobotArmController: def __init__(self,port):
#初始化链接
self.mc = MyCobot(port, 115200)
self.init_pose = [0.96, 86.22, -98.26, 10.54, 86.92, -2.37]
self.coords = [-40, -92.5, 392.7, -92.19, -1.91, -94.14]
self.speed = 60
self.mode = 0 def ThumpUpAction(self):...def OtherAction(self):...
程序逻辑的处理
在调试的过程中,出现了一些问题,在识别收拾的时候,它是一直识别,这就意味着如果在1s中内识别了10次的话,会给机械臂发送10个命令,这样肯定不是我一开始所设想的。
所以在逻辑上就要有所处理,下面是我处理的方式。
# 设置一个2S的时间来确定这个手势,当竖大拇指出现2s的时候才进行下发机械臂控制的命令,用控制变量的方式来进行。#初始化变量
#检测手势是否存在的变量
gesture_detected = False
#确定手势出现后计时的变量
gesture_start_time = None
# 设定手势出现2s后的变量
gesture_confirmation_time = 2后续的处理逻辑:
当特定手势出现的时候,gesture_start_time就开始计时,这个时候在不停的做判断如果时间到达了2S之后,确定手势接下来执行相对应手势的机械臂运动。current_time = time.time()
if current_gesture:
if not gesture_detected:gesture_detected = True
gesture_start_time = current_time
elif current_time - gesture_start_time > gesture_confirmation_time and not action_triggered:
# 根据手势执行相应动作
if current_gesture == "thumb_up":
robotic arm action()
但是这样还不够,因为手如果出现超过2s后也会持续发送机械臂的指令,这里我们需要在设置一个冷却的时间,有充足的时间让机械臂完成运动。
#冷却时间的变量
#机械臂是否完成动作的变量
action_triggered = False
#冷却时间计时的变量
cooldown_start_time = None
#固定2s冷却时间
cooldown_period = 2
这样就能够满足需求了。完整的逻辑代码处理如下
# 处理手势
current_time = time.time()if current_gesture:if not gesture_detected:
gesture_detected = True
gesture_start_time = current_timeelif current_time - gesture_start_time > gesture_confirmation_time and not action_triggered:# 根据手势执行相应动作if current_gesture == "thumb_up":print('good good')
mc.thum_up()elif current_gesture == "palm_open":print('forward')
mc.increment_x_and_send()# 可以添加更多手势和对应动作的判断
action_triggered = True
cooldown_start_time = current_timeelse:
gesture_detected = False
gesture_start_time = Noneif action_triggered and current_time - cooldown_start_time > cooldown_period:print('can continue')
action_triggered = False
cooldown_start_time = None
演示
https://youtu.be/9vOPKO_IG9M
总结
这个项目展示了使用手势识别控制myCobot 320的方法,开创了人机互动的新形式。尽管目前仅实现了有限的几个手势与机械臂动作的对应,但它为未来更广泛的机械臂应用奠定了基础。结合手势与机械臂的创新尝试不仅提升了我的编程技能,还锻炼了我的问题解决能力,为未来的相关项目提供了宝贵经验。