树莓派脸部追踪 
硬件材料 
树莓派4B、云台、摄像头
思路 
1、电脑上显示摄像头拍摄的视频,并得到人脸坐标,将人脸坐标发给树莓派。
2、树莓派来控制舵机旋转
3、电脑和树莓派之间和socket通信
4、树莓派上使用motion将摄像头内容输出到“192.168.6.179:8081”,从而让电脑获取视频源【192.168.6.179是树莓派地址】
注意:
1、树莓派可能需要关掉防火墙:ufw disable
2、树莓派要先启动motion:sudo motion【只用启动一次即可,一直在后台运行】
人脸跟踪的算法 
第一种 
获得人脸矩阵中心点坐标【x,y】,再获得视频中心坐标,计算两者误差,从而让摄像头旋转相应角度,旋转时要尽量一度一度的转,不要过激,否则容易让抖动。
当然,我写的只是简单的计算两个中心误差再旋转,缺点是旋转不平滑,改进方式是用PID算法
PID算法参考1:https://pyimagesearch.com/2019/04/01/pan-tilt-face-tracking-with-a-raspberry-pi-and-opencv/ 
PID算法参考2:https://bcxiaobai.eu.org/post/383.html 
第二种 
参考:https://blog.csdn.net/rikeilong/article/details/126446567 
当人脸矩阵左边或右边快要超出视频边界时再旋转,也是要尽量一度一度的转
代码 
电脑上 
电脑上client.py 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import  socketclass  connect_Raspberry ():    def  __init__ (self,host,port ):         print ("客户端开启" )                  self.mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)                  try :             self.mySocket.connect((host, port))               print ("连接到服务器" )         except :               print ('连接RASP不成功' )     def  send (self, words ):                  msg = words                  self.mySocket.send(msg.encode("utf-8" ))              def  close (self ):         self.mySocket.close()         print ("与树莓派丽连接中断\n" )         exit() 
 
电脑上main.py 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import  cv2import  mediapipe as  mpimport  numpy as  npimport  clientmp_face_detection = mp.solutions.face_detection mp_drawing = mp.solutions.drawing_utils myRaspConnection = client.connect_Raspberry('192.168.6.179' , 8888 ) if  __name__ == "__main__" :    capture = cv2.VideoCapture("http://192.168.6.179:8081" )     ref, frame = capture.read()     fps = 0.0      while  (True ):         ref, frame = capture.read()         h, w, _ = np.shape(frame)         if  not  ref:             break          image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)                  with  mp_face_detection.FaceDetection(model_selection=0 , min_detection_confidence=0.8 ) as  face_detection:             results = face_detection.process(image)             if  results.detections:                 for  detection in  results.detections:                     box = detection.location_data.relative_bounding_box                                          cx = box.xmin                     cy = box.ymin                     cw = box.width                     ch = box.height                     cv2.rectangle(image, (int (cx * w), int (cy * h)), (int ((cx + cw) * w), int ((cy + ch) * h)),                                   (0 , 255 , 0 ), 2 )                                  msg = str (abs (int (cx * w))) + " "  + str (abs (int (cy * h))) + " "  + str (abs (int ((cx + cw) * w))) + " "  + str (                     abs (int ((cy + ch) * h)))                 print (msg)                 myRaspConnection.send(msg)         frame = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)                  cv2.imshow("video" , frame)         c = cv2.waitKey(1 ) & 0xff          if  c == 27 :             capture.release()             break      print ("Video Detection Done!" )     capture.release()     cv2.destroyAllWindows() 
 
树莓派上 
树莓派上sever.py 
1 2 3 4 5 6 7 8 9 import  socketprint ("服务开启" )mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) host = "192.168.6.179"  port = 8888   mySocket.bind((host, port)) mySocket.listen(10 ) 
 
树莓派上main.py 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 import  timeimport  severimport  RPi.GPIO as  GPIOfrom  PCA9685 import  PCA9685import  mathpwm=PCA9685() pwm.setPWMFreq(50 ) pwm.setRotationAngle(5 ,0 ) if  __name__ == '__main__' :    pid_X_P=0      pid_Y_P=0      print ("等待连接" )     client,address = sever.mySocket.accept()     print ("新连接" )     print ("IP is %s"  % address[0 ])     print ("port is %d\n"  % address[1 ])          beangle = 90       beangle0 = 45  	     channel1 = 4       channel2 = 8   	 	     angleFreq = 1  	     changeFreqX = 100      changeFreqY = 20      error_x=500                  last_error_x=100             error_y=250      last_error_y=50      wight=900      height=480      piv_x=90      piv_y=45      step=1      try :         print ("开始" )         while  True :             msg = client.recv(1024 )             msg = msg.decode("utf-8" )             if  msg != "" :                 mess = msg.split(' ' )                                  x0 = int (mess[0 ])                 y0 = int (mess[1 ])                 x1 = int (mess[2 ])                 y1 = int (mess[3 ]) 				                 x_mean=int ((x0+x1)/2 )                 y_mean=int ((y0+y1)/2 )                 print ("x_mean" ,x_mean,"y_mean" ,y_mean)                 error_x=int (x_mean-wight/2 )                 error_y=int (y_mean-height/2 )                 print ("error_x" ,error_x,"error_y" ,error_y)                                  if  error_x<0   and  abs (error_x)>100 :                                                              step_x=math.exp(abs (error_x)/(wight/2 ))                     print (step_x)                     beangle+=step                     if  beangle >= 180 :                         beangle = 180                      print ("向左偏" ,beangle)                     pwm.setRotationAngle(1 ,beangle)                                  if  error_x>0   and  abs (error_x)>100 :                     step_x=math.exp(abs (error_x)/(wight/2 ))                     print (step_x)                     beangle-=step                     if  beangle <=10 :                         beangle = 10                      print ("向右偏" ,beangle)                     pwm.setRotationAngle(1 ,beangle)                                  if  error_y<0   and  abs (error_y)>70 :                                                                                    try :                         step_y=math.exp(abs (error_y)/(height/2 ))                     except :                         step_y=2                      print (step_y)                     beangle0-=step                     if  beangle0 <=10 :                         beangle0 = 10                      print ("向上偏" ,beangle0)                     pwm.setRotationAngle(0 ,beangle0)                                  if  error_y>0   and  abs (error_y)>70 :                                                               try :                         step_y=math.exp(abs (error_y)/(height/2 ))                     except :                         step_y=2                      print (step_y)                     beangle0+=step                     if  beangle0 >= 85 :                         beangle0 = 95                      print ("向下偏" ,beangle0)                     pwm.setRotationAngle(0 ,beangle0)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   except  ValueError as  e:         pwm.exit_PCA9685()         print ("退出" )         print (e)         exit() 
 
运行 
1、树莓派上先运行main.py 
2、电脑上再运行main.py ,电脑上可见一个视频窗口,此时摄像头开始追踪人脸
参考 
PID算法:https://pyimagesearch.com/2019/04/01/pan-tilt-face-tracking-with-a-raspberry-pi-and-opencv/ 
思路:https://blog.csdn.net/rikeilong/article/details/126446567?spm=1001.2014.3001.5502