内容简介:如何让机器人头部摄像头跟随识别到人脸位置变化而转动?一帧图像是 2 维的,人脸位置用坐标 (x,y) 表示,要实现跟踪人脸则需要两个轴
基于 face_recognition 和 PID 的舵机云台人脸识别和跟踪
如何让机器人头部摄像头跟随识别到人脸位置变化而转动?一帧图像是 2 维的,人脸位置用坐标 (x,y) 表示,要实现跟踪人脸则需要两个轴 pan, tilt
- pan : 水平左右方向转头
- tilt : 竖直上下方向转头
人脸识别
首先需要在每一帧图像中识别到人脸, face_recognition 一个简单易用的人脸识别开源项目,并且配备了完整的开发文档和示例代码,还特别兼容了树莓派。face_recognition 基于 C++ 开源库 dlib 的深度学习模型,使用 Labeled Faces in the Wild 人脸数据集进行测试,有高达99.38%的准确率。
安装 face_recognition
安装步骤请参考: face_recognition#installation
识别人脸 Python 代码
import face_recognition
def face_location(frame, frame_center):
face_locations = face_recognition.face_locations(frame)
if len(face_locations) > 0:
y0, x0, y1, x1 = face_locations[0]
face_x = int((x0 + x1) / 2)
face_y = int((y0 + y1) / 2)
return face_x, face_y
return frame_center
PID 控制
使用 face_recognition 可以很容易地用 Python 代码实现人脸识别,计算出人脸在一帧图像中的像素坐标 (x, y) ,接下来就是需要控制舵机对人脸进行跟踪。
控制目标: 调整横向和纵向两个自由度的舵机,使得摄像头中的人脸中心与图像的中心重合。
这就需要引入 PID 控制了,先直接放公式
\[ u(t)=K_{\mathrm{p}} e(t)+K_{\mathrm{i}} \int_{0}^{t} e\left(t^{\prime}\right) d t^{\prime}+K_{\mathrm{d}} \frac{d e(t)}{d t} \]
- 时间 \(t\) ,在这里时间是离散的;
- 偏差 \(e(t)\) ,在人脸跟踪中指的是图像中心与人脸中心之间的距离(x 方向,y 方向);
- 系统输出 \(u(t)\) ,即输出的舵机角度(分水平和垂直方向两个舵机的角度);
接下来需要理解, \(K_p\) , \(K_i\) , \(K_d\) 三个参数的作用
比例(P)
比例控制的输出信号与输入偏差成比例关系。偏差一旦产生,控制器立即产生控制作用以减小偏差,是最基本的控制规律。当仅有比例控制时系统输出存在稳态误差。
- 当人脸与图像中心相距 较远 时,需要舵机 大幅度 运动对准人脸
- 当人脸与图像中心相距 较近 时,需要舵机 小幅度 靠近对准人脸
根据 kp 取值不同,摄像头都会去对准人脸,只是 kp 大了到达的快, kp 小了到达的慢一些。
积分(I)
防止系统进入稳定后存在的稳定误差,即有可能摄像头稳定后停下来了,但是没有对准人脸的中心。为了消除稳态误差,必须引入积分控制。积分作用是对历史的偏差进行积分,随着时间的增加,积分输出会增大,使稳态误差进一步减小,直到偏差为零,才不再继续增加,最后系统稳定下来,才可能正好中心对准人脸。
微分(D)
在微分控制中,控制器的输出与输入偏差信号的微分(即偏差的变化率)成正比关系。微分控制反映偏差的变化率,只有当偏差随时间变化时,微分控制才会对系统起作用,而对无变化或缓慢变化的对象不起作用。通俗来说,是为了在人脸追踪时,防止 追过劲了 ,在中心对准人脸后可以及时地停止,防止震荡。
PID 代码实现
在理解概念和公式后,就不难代码实现
# https://www.pyimagesearch.com/2019/04/01/pan-tilt-face-tracking-with-a-raspberry-pi-and-opencv/
import time
class PID:
def __init__(self, kP=1, kI=0, kD=0):
# initialize gains
self.kP = kP
self.kI = kI
self.kD = kD
def initialize(self):
# intialize the current and previous time
self.currTime = time.time()
self.prevTime = self.currTime
# initialize the previous error
self.prevError = 0
# initialize the term result variables
self.cP = 0
self.cI = 0
self.cD = 0
def update(self, error, sleep=0.2):
# pause for a bit
time.sleep(sleep)
# grab the current time and calculate delta time
self.currTime = time.time()
deltaTime = self.currTime - self.prevTime
# delta error
deltaError = error - self.prevError
# proportional term
self.cP = error
# integral term
self.cI += error * deltaTime
# derivative term and prevent divide by zero
self.cD = (deltaError / deltaTime) if deltaTime > 0 else 0
# save previous time and error for the next update
self.prevTime = self.currTime
self.prevError = error
# sum the terms and return
return sum([
self.kP * self.cP,
self.kI * self.cI,
self.kD * self.cD])
代码设计
由于云台有 2 个自由度(pan, tilt),所以需要用到 2 个 PID 控制器,来输出对应的角度
- \(x\) 轴偏差 对应 水平左右转动 pan
- \(y\) 轴偏差 对应 垂直上下转动 tilt
在代码设计时,需要考虑如下几点限制因素
- 受限于设备的性能,使用 face_recognition 识别一帧图像里的人脸可能会非常耗时
- 舵机控制时,不同的舵机响应时间不同
- PID 在代码中是通过循环累加来计算的;
def thread_face_center():
print('face_center ..')
process_this_frame = 0
while True:
time.sleep(0.01)
if not QUEUE_IMG.empty():
frame = QUEUE_IMG.get()
else:
continue
(h, w) = frame.shape[:2]
HEAD.center_x = w // 2
HEAD.center_y = h // 2
if process_this_frame > 8:
HEAD.obj_x, HEAD.obj_y = face_location(frame, (HEAD.center_x, HEAD.center_y))
print(HEAD.obj_x, HEAD.obj_y)
process_this_frame = 0
process_this_frame += 1
def thread_pid_pan():
p, i, d = 0.09, 0.08, 0.002
pid = PID(p, i, d)
pid.initialize()
while True:
error = HEAD.center_x - HEAD.obj_x
HEAD.pan = pid.update(error)
def thread_pid_tlt():
p, i, d = 0.11, 0.10, 0.002
pid = PID(p, i, d)
pid.initialize()
while True:
error = HEAD.center_y - HEAD.obj_y
HEAD.tlt = pid.update(error)
def thread_set_servos():
set_head_servo([0, 90])
while True:
time.sleep(0.01)
pan_angle = HEAD.pan + 0
tlt_angle = 90 - HEAD.tlt
print('[pan_angle, tlt_angle] = ', pan_angle, tlt_angle)
set_head_servo([pan_angle, tlt_angle])
需要将人脸识别、PID 过程、角度控制放到 单独的线程 处理。
async_do_job(thread_face_center) async_do_job(thread_pid_pan) async_do_job(thread_pid_tlt) async_do_job(thread_set_servos)
参考链接
以上所述就是小编给大家介绍的《基于 face_recognition 和 PID 的人脸识别和跟踪》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Firefox 65 计划推出新反跟踪方法,专门针对跨站点跟踪
- Thinkphp漏洞跟踪
- 跟踪 Component 的修改
- 路径跟踪 PathMeasure的简单使用
- 埋点:淘宝 SPM 流量跟踪体系
- 调用链跟踪系统之 Vaper
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Impractical Python Projects
Lee Vaughan / No Starch Press / 2018-11 / USD 29.95
Impractical Python Projects picks up where the complete beginner books leave off, expanding on existing concepts and introducing new tools that you’ll use every day. And to keep things interesting, ea......一起来看看 《Impractical Python Projects》 这本书的介绍吧!