今天程序员的每一行代码都是未来高达身上的一颗螺丝
这是昨天收到的作者投稿,在iN的建议下就给大家改成了一个教程。
利用摄像头和舵机组建的一只人脸跟踪大魔。只要你出现在它的视野中,这只大摩头就会一只盯着你。
有一种 硝烟散尽 在废墟里找到了一个大魔 只剩下头 但眼睛还亮着的感觉……
其实要实现这个控制并不难,整体上仅仅需要200多行代码,在程序中都算作是小程序了。即便是未来真的出现高达,高达的目标追踪功能的代码其实也是可以从作者的项目中进行扩展的。
那么我们来看看这只大摩头是如何制作出来的:
作者的教程:
第一次写教程没有啥经验 有写的不好的地方欢迎指正
这个大魔源于一个我在写的人脸追踪的项目,做好了就像下面这个。
但是作为胶佬自然不会就这么止步,所以就想到了把他美化一下,就想到了买一个大魔头,然后把它装在里面。
接下来先教大家制作这个人脸追踪平台,成本不到100元。
需要的东西有:
- 舵机两个
- 二轴云台一个
- Arduino nano3开发板一个
- usb摄像头一个
- usb线一根
网上买的云台质量不会很好,舵机可能不能很好的卡进去,所以要先把舵机打磨好,然后像下图这样组装好。
舵机一共有三根线,黄色的是信号线,红色和褐色分别是正负极。如下图所示。
我们将正负极的线剪断,然后将两个舵机的正极接在一起,负极接在一起,再剪断一根没用的usb线,usb线里面也会有正负极线,对应的将正极接在一起,负极接在一起。这样就可以通过usb向舵机供电了。
再将x轴的舵机信号线接到arduino nano开发板的D8接口接到Y轴的舵机信号线上,D9接口接到X轴的舵机信号线上。
这样硬件部分的连接就算完成了。
接下来就是向arduino nano烧录程序了。
下载arduino的IDE
将arduino nano通过usb连接到电脑上。做如下配置,在工具中选择开发板。
配置端口,我这里是COM5,大家根据自己的实际情况选择。
然后点击编译,编译无错误,上传。
等待上传完成。
最后将摄像头连接到电脑上,打开pycharm软件,运行python人脸识别代码。
Python会将识别到的人脸的坐标通过串口发送给arduino nano开发板。开发板根据接收到的坐标对舵机进行控制。
以上关于人脸追踪的介绍就完成了。
下面就是大魔头的做旧了。
首先把大魔头的眼睛以下的部分全部掏空,然后打磨平整,保证大魔头可以正好套在云台上。
然后在外甲加上伤痕,我没有电动锯,所以就用了三角形的锉刀,一点一点搓出来的伤口。随后就是对动力管等上漆,这里用的田宫的喷灌,金属浅枪铁色。
先在表面上一层消光,以免裂件。然后就是渍洗。我用了郡仕的铁锈色渗线液进行渍洗。
等渍洗液干透,就可以用棉签蘸取稀释剂擦去多余的渍洗液了。
然后干扫、用面相笔蘸取黑色补充细节掉漆。
完成!
后面则是代码了:
Arduino 舵机驱动代码:
#include <Servo.h> // 声明调用Servo.h库Servo myservo; // 创建一个舵机对象Servo myservo1; // 创建一个舵机对象int posx = 60; // 变量pos用来存储舵机位置int posy = 60; // 变量pos用来存储舵机位置int c = 0;void setup() { Serial.begin(9600); myservo.attach(9); // 将引脚9上的舵机与声明的舵机对象连接起来 myservo1.attach(8); // 将引脚9上的8机与声明的舵机对象连接起来 myservo.write(posx); myservo1.write(posy);// 给舵机写入角度}
void loop() { while(Serial.available()>0)//当有信号的时候 { char val=Serial.read(); Serial.println(val); if(val=='0'){ //左上 if(posx -3>=60){ posx = posx -3; c = 1; } if(posy -2>=0){ posy = posy -2; c = 1; } }else if(val=='1'){ //右上 if(posx +3<=120){ posx = posx +3; c = 1; } if(posy -2>=60){ posy = posy -2; c = 1; } }else if(val=='2'){ //左下 if(posx -3>=0){ posx = posx -3; c = 1; } if(posy +2<=120){ posy = posy +2; c = 1; } }else if(val=='3'){ //右下 if(posx +3<=120){ posx = posx +3; c = 1; } if(posy +2<=120){ posy = posy +2; c = 1; } }else if(val=='4'){ //上 if(posy -2>=0){ posy = posy -2; c = 1; } }else if(val=='5'){ //左 if(posx -3>=0){ posx = posx -3; c = 1; } }else if(val=='6'){ //右 if(posx +3<=120){ posx = posx +3; c = 1; } }else if(val=='7'){ //下 if(posy +2<=120){ posy = posy +2; c = 1; } } if(c ==1){ Serial.println("dong"); myservo.write(posx); myservo1.write(posy);// 给舵机写入角度 } c = 0; Serial.println(posx); Serial.println(val); } }
上位机(也就是我们的电脑)人脸识别代码:
#!Anaconda/anaconda/python
import dlib #人脸识别的库dlibimport numpy as np #数据处理的库numpyimport cv2 #图像处理的库OpenCvfrom pyfirmata import Arduino, utilimport timeimport serial
class face_emotion():
def __init__(self): # 使用特征提取器get_frontal_face_detector self.detector =
dlib.get_frontal_face_detector() # dlib的68点模型,使用作者训练好的特征预测器 self.predictor = dlib.shape_predictor("
./shape_predictor_68_face_landmarks.dat")
# 建cv2摄像头对象,这里使用电脑自带摄像头,如果接了外部摄像头,则自动切换到外部摄像头 self.cap = cv2.VideoCapture(0) # 设置视频参数,propId设置的视频参数,value设置的参数值 self.cap.set(3, 480) # 截图screenshoot的计数器 self.cnt = 0 self.ser = serial.Serial() self.ser.baudrate = 9600 # 设置波特率 self.ser.port = "COM5" # 端口是COM3 self.ser.open() # 打开串口
def learning_face(self): # cap.isOpened() 返回true/false 检查初始化是否成功 while(self.cap.isOpened()):
# cap.read() # 返回两个值: # 一个布尔值true/false,用来判断读取视频是否成功/是否到视频末尾 # 图像对象,图像的三维矩阵 flag, im_rd = self.cap.read()
# 每帧数据延时1ms,延时为0读取的是静态帧 k = cv2.waitKey(1)
# 取灰度 img_gray = cv2.cvtColor(im_rd, cv2.COLOR_RGB2GRAY)
# 使用人脸检测器检测每一帧图像中的人脸。并返回人脸数rects faces = self.detector(img_gray, 0)
# 待会要显示在屏幕上的字体 font = cv2.FONT_HERSHEY_SIMPLEX
# 如果检测到人脸 if len(faces) != 0:
# 对每个人脸都标出68个特征点 for i in range(len(faces)): # enumerate方法同时返回数据对象的索引和数据,k为索引,d为faces中的对象 for k, d in enumerate(faces):
cv2.rectangle(im_rd, (d.left(), d.top()), (d.right(), d.bottom()), (0, 0, 255)) fw(self, d.top(), d.bottom(), d.left(), d.right())
# 计算人脸热别框边长 self.face_width = d.right() - d.left()
# 标出人脸数 cv2.putText(im_rd, "Faces: "+str(len(faces)), (20,50), font, 1, (0, 0, 255), 1, cv2.LINE_AA) else: # 没有检测到人脸 cv2.putText(im_rd, "No Face", (20, 50), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
# 窗口显示 cv2.imshow("camera", im_rd)
# 释放摄像头 self.cap.release()
# 删除建立的窗口 cv2.destroyAllWindows()
def fw(self, t, b, l, r):
y = (b - t) / 2 + t x = (r - l) / 2 + l print(str(t)+" "+str(b)+" "+str(l)+" "+str(r)) print(" " +str(x) + " " + str(y))
if x-280>=50 and abs(y-300)<=40: print("右") self.ser.write(b"5") elif x - 280 >= 50 and y - 300 >= 40: print("右下") self.ser.write(b"2") elif x - 280 >= 50 and y - 300<=-40: print("右上") self.ser.write(b"0") elif x - 280 <= -50 and abs(y-300)<=40: print("左") self.ser.write(b"6") elif x - 280 <= -50 and y-300<=-40: print("左上") self.ser.write(b"1") elif x - 280 <= -50 and y-300 >= 40: print("左下") self.ser.write(b"3") elif abs(x - 280) <= 50 and y-300>=40: print("下") self.ser.write(b"7") elif abs(x - 280) <= 50 and y-300<=-40: print("上") self.ser.write(b"4")
if __name__ == "__main__": my_face = face_emotion() my_face.learning_face()