本文从硬件开始再到控制、算法层面一步一步搭建一个猜拳用的机械手,当然本次实现的是之前做的一个小玩意,可能在实时性上并不算特别好,仅做学习和参考用途。当然基本的玩耍功能还是有的啦~
一、准备
软件环境
Arduino IDE安装
Python(Pycharm可选)
OpenCV-Python
TensorFlow(版本为1)
Keras
硬件
机械手(大伙自己淘宝吧,省的被说打广告。当然也可以自己DIY,看这篇:我的diy之自制体感机械手篇)
Arduino UNO开发板
舵机
PCA9685舵机控制板
电压转换模块
电源及开关等
摄像头(当然用电脑自带的也行)
参考资料
以下都是收集的GitHub上面相关的手势识别或者人机交互的项目源码,可用作参考
https://github.com/HiminDong/gesture-vrep
https://github.com/Oranges520/2019-HMI-ISP-06
https://github.com/AravinthPanch/gesture-recognition-for-human-robot-interaction
https://github.com/Dhivin/Gesture-control-for-robotics-in-Matlab
二、硬件及底层控制
组装
发几张自己组装的流程图,组装的过程注意要让每根手指的动作至少不会有太大的阻力,防止卡顿
接线
电路接线如下,将Arduino开发板的GPIO与舵机控制板PCA9685的I2C接口相接,电池电源连通开关(此处兼具电压转换功能)接到PCA9685的供电端口(一定要接电源,否则难以驱动多个电机)。然后,选择PCA9685上的输出引脚对与机械手上的舵机相连接(16路舵机控制板,编号从左至右为0~15)。此处选择为:
- 大拇指 0号位
- 食指 2号位
- 中指 4号位
- 无名指 6号位
- 小拇指 7号位
Arduino控制源码
底层控制流程如下:
Arduino源码如下,注意大拇指与其他手指的弯曲角度有所区别。通过串口获取从电脑上发送来的控制信号
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
char var;
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); // 获取舵机地址
#define SERVOMIN 150 // this is the 'minimum' pulse length count (out of 4096)
//这是“最小”脉冲长度计数(在4096)中
#define SERVOMAX 600 // this is the 'maximum' pulse length count (out of 4096)
//这是“最大”脉冲长度计数(在4096中)
#define SERVOMED 320 // this is the 'medium' pulse length count (out of 4096)
//这是手指默认位置的脉冲长度计数(在4096中)
//给定各个手指在舵机控制板上的位
#define thumb 0 //大拇指
#define index_finger 2 //食指
#define middle_finger 4 //中指
#define ring_finger 6 //无名指
#define little_finger 7 //小拇指
void setup() {
Serial.begin(9600);
pwm.begin();
pwm.setPWMFreq(50);
}
void scissors(){
pwm.setPWM(thumb, 0, SERVOMED);
pwm.setPWM(index_finger, 0, SERVOMAX);
pwm.setPWM(middle_finger, 0, SERVOMAX);
pwm.setPWM(ring_finger, 0, SERVOMED);
pwm.setPWM(little_finger, 0, SERVOMED);
}
void rock(){
pwm.setPWM(thumb, 0, SERVOMED);
pwm.setPWM(index_finger, 0, SERVOMED);
pwm.setPWM(middle_finger, 0, SERVOMED);
pwm.setPWM(ring_finger, 0, SERVOMED);
pwm.setPWM(little_finger, 0, SERVOMED);
}
void paper(){
pwm.setPWM(thumb, 0, SERVOMED+180);
pwm.setPWM(index_finger, 0, SERVOMAX);
pwm.setPWM(middle_finger, 0, SERVOMAX);
pwm.setPWM(ring_finger, 0, SERVOMAX);
pwm.setPWM(little_finger, 0, SERVOMAX);
}
void loop() {
while(Serial.available()>0)
{
var = Serial.read();
if(var == '1')
{
//delay(1000);
scissors();
Serial.write("scissors");
delay(50);
}
if(var == '2')
{
//delay(1000);
rock();
Serial.write("rock");
delay(50);
}
if(var == '3')
{
//delay(1000);
paper();
Serial.write("paper");
delay(50);
}
}
}
调试界面
为了方便调试,这边提供了python编写的调试界面
import tkinter as tk
import serial
serialPort = "COM5" # 注意此处需要查找你电脑上的连接端口并且进行替换
baudRate = 9600 # 注意波特率配置
ser = serial.Serial(serialPort,baudRate,timeout=1)
print("参数设置:串口 = %s, 波特率 = %d" % (serialPort, baudRate))
demo1 = b'1' # case scissors
demo2 = b'2' # case rock
demo3 = b'3' # case paper
def scissors():
ser.write(demo1)
def rock():
ser.write(demo2)
def paper():
ser.write(demo3)
window = tk.Tk()
window.title('机械手测试')
window.geometry('300x200')
l = tk.Label(window,text = '机械手测试').pack()
b1 = tk.Button(window, text = '剪刀', command = scissors)
b1.pack()
b2 = tk.Button(window, text = '石头', command = rock)
b2.pack()
b3 = tk.Button(window, text = '布', command = paper)
b3.pack()
window.mainloop()
调试无误之后,可以进行模型的训练及图像识别过程
三、手势识别
接来下的实现过程参考了GitHub上的源码项目:
https://github.com/Oranges520/2019-HMI-ISP-06
希望大家多多支持原开发者。也可参考对应的源码进行自己的修改。
实现过程是在卷积神经网络中添加了SE-Block。该算法模式是在2017年由自动驾驶公司Momenta的团队所提出,他通过对特征通道之间的相关性进行建模,把重要的特征进行强化啊,对无用的特征进行抑制来提高识别准确率。
论文:Squeeze-and-Excitation Networks
网络架构如下:
源码及训练好的文件:图像识别训练结果及程序
注意在内部需要在完成手势识别之后,将对应的识别结果通过串口发送给Arduino,然后通过Arduino内部的控制程序再控制机械手做出相应的反应。程序最后面通过添加多个if判断语句实现对信号的限幅及滤波,防止识别结果跳变时导致的机械手的抖动,以避免损坏舵机。
以下为电脑上显示的识别结果。当然在单一颜色背景下效果是最好的,这边也只是记录和分享一下自己的制作过程,如果能对大家有所帮助也再好不过。大佬就请无视本菜鸡吧。
以上内容仅做学习和分享用途