作者:Hydra.CasterDamon工作室
這是昨天收到的作者投稿,在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)
# 取灰度 gray = cv2.cvtColor(im_rd, cv2.COLOR_RGB2GRAY)
# 使用人臉檢測器檢測每一幀圖像中的人臉。并返回人臉數rects faces = self.detector(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()