内容简介:如何让机器人头部摄像头跟随识别到人脸位置变化而转动?一帧图像是 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
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
深入理解 Flask
[美]Jack Stouffer / 苏丹 / 电子工业出版社 / 2016-7-1 / 79.00
Flask 是一种具有平缓学习曲线和庞大社区支持的微框架,利用它可以构建大规模的web应用。学习上手Flask非常轻松,但要深入理解却并不容易。 本书从一个简单的Flask应用开始,通过解决若干实战中的问题,对一系列进阶的话题进行了探讨。书中使用MVC(模型-视图-控制器)架构对示例应用进行了转化重构,以演示如何正确地组织应用代码结构。有了可扩展性强的应用结构之后,接下来的章节使用Flask......一起来看看 《深入理解 Flask》 这本书的介绍吧!