好友
阅读权限25
听众
最后登录1970-1-1
|
本帖最后由 Lthero 于 2022-10-12 21:14 编辑
树莓派脸部追踪
硬件材料
树莓派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:[color=var(--a-color)]https://bcxiaobai.eu.org/post/383.html
第二种
参考:https://blog.csdn.net/rikeilong/article/details/126446567
当人脸矩阵左边或右边快要超出视频边界时再旋转,也是要尽量一度一度的转
请把代码中 ​ 去掉
代码
电脑上
电脑上client.py
[Python] 纯文本查看 复制代码 import socket
​
class connect_Raspberry():
def __init__(self,host,port):
print("客户端开启")
# 套接字接口
self.mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置ip和端口
​
try:
self.mySocket.connect((host, port)) #连接到服务器
print("连接到服务器")
except: #连接不成功,运行最初的ip
print('连接RASP不成功')
​
def send(self, words):
# 发送消息
msg = words
# 编码发送
self.mySocket.send(msg.encode("utf-8"))
# print("成功发送消息")
​
def close(self):
self.mySocket.close()
print("与树莓派丽连接中断\n")
exit()
​
​电脑上main.py
[Python] 纯文本查看 复制代码 import cv2 import mediapipe as mp
import numpy as np
import client
​
# 检测脸部
mp_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,cy,cw,ch=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.rectangle(frame, (int(cx*w) , int(cy*h)), (int((cx+cw)*w) , int((cy+ch)*h)),(0, 255, 0), 2)
​
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
[Python] 纯文本查看 复制代码 print("服务开启")
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
[Python] 纯文本查看 复制代码 import socket ​
import time
import sever
import RPi.GPIO as GPIO
from PCA9685 import PCA9685
import math
pwm=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])#左上角x
y0 = int(mess[1])#左上角y
x1 = int(mess[2])#右下角x
y1 = int(mess[3])#右下角y
​
​
# 方法1:超出中间就偏转
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)
​
# 误差大于100,要向左偏
if error_x<0 and abs(error_x)>100:
# temp_x=abs(error_x)/(wight/2)*45
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)
​
# 误差大于50,要向上偏
if error_y<0 and abs(error_y)>70:
# if abs(error_y)>=100:
# error_y=100
# temp_x=abs(error_x)/(wight/2)*45
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:
# if abs(error_y)>=100:
# error_y=100
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)
​
# 方法2:快超出屏幕时再旋转
# if x0 < changeFreqX:
# beangle += angleFreq
# if beangle >= 180:
# beangle = 180
# pwm.setRotationAngle(1,beangle)
# #set_servo_angle(channel1,beangle)
# if y0 < changeFreqY:
# beangle0 -= angleFreq
# if beangle0 <= 10:
# beangle0 = 10
# pwm.setRotationAngle(0,beangle0)
# #set_servo_angle(channel2,beangle0)
​
# if x1 > 640 - changeFreqX: #窗口宽为640
# beangle -= angleFreq
# if beangle <= 10:
# beangle = 10
# pwm.setRotationAngle(1,beangle)
# #set_servo_angle(channel1,beangle)
# if y1 > 480 - changeFreqY: #窗口高为480
# beangle0 += angleFreq
# if beangle0 >= 85:
# beangle0 = 85
# pwm.setRotationAngle(0,beangle0)
# set_servo_angle(channel2,beangle0)
# print("beangle",beangle,"beangle0:",beangle0)
except ValueError as e:
pwm.exit_PCA9685()
print("退出")
print(e)
exit()
[size=14px] ​
运行
1、树莓派上先运行main.py
2、电脑上再运行main.py,电脑上可见一个视频窗口,此时摄像头开始追踪人脸
|
免费评分
-
查看全部评分
|