关于一个增量式人脸跟踪云台
介绍
识别人脸的方法有很多,这里使用了Haar特征加Cascade分类器进行检测。Haar特征其实就是拿一堆黑白小方块在图像上滑动,比较不同区域的明暗差值,比如眼睛一带比脸颊暗、鼻梁比两边亮,这些固定的明暗模式就是特征。单看一个特征很容易误判,所以还要用AdaBoost把一堆弱特征组合成强分类器,再把强分类器一级一级串起来:前面几级结构简单,很快就能把明显不是脸的窗口筛掉,只有活到最后的窗口才判定为人脸。OpenCV里直接带现成的分类器文件,省了自己训练的功夫,用起来也确实快,跑起来不怎么吃力。
不过这种检测只画出人脸,判断不了人脸是谁,如果需要识别具体是谁,那就得用CNN了。对于人脸识别我们可以使用目标检测方法,包括但不限于FaceNet、Fast RCNN等,这些模型的计算量就上来了而且我也没打算做,我这里只是说一下。
整套云台的思路很直接:摄像头对着前方,上位机检测到人脸之后,算出人脸框中心相对画面中心的偏移,再把偏移换算成舵机的角度增量,通过串口丢给STM32,STM32输出PWM让舵机往人脸的方向转一点,转完再看下一帧,人脸就慢慢被拉回画面中间了。因为是走一步看一步地递进调整,所以叫增量式,这么做的好处是不用提前知道人脸离多远、有多大,反馈一次修正一次就行。
设备清单:
- Stm32F103C8T6:主控,负责收串口数据、换算占空比、驱动舵机
- USB转TTL:给单片机下载程序,顺便看打印
- 一枚摄像头,我用的是一个红外摄像头,视角为105°:红外的好处是晚上没光也能拍到人脸;视角大一点,人脸偏到画面边缘也能框进来
- 舵机(180°足够):这里只用一个自由度,上下方向的跟踪没做
- 供电部分:单片机是3.3V,舵机得单独给5V,别从板子上取电,舵机堵转的电流能把单片机一起带崩
代码
首先看看STM32CubeMX的PWM配置:

这里把定时器配成PWM输出模式,让TIM1的通道1输出50Hz的方波,也就是周期20ms。舵机的控制信号看的是脉宽:一般0.5ms对应0°,2.5ms对应180°,中间线性对应。所以只要在代码里根据角度算出对应的比较值,再丢给__HAL_TIM_SetCompare就能控制角度。PWM的频率由预分频和自动重装值决定,CubeMX里填的时候按自己板子的时钟算一下,保证最后出来是50Hz就行。
USART设置速度为115200即可,接收长度定为3个字节。我们看看单片机的代码:
1 | |
这样我们就可以通过串口控制舵机了。控制流程是这样的:上电后Start()先把舵机复位到90°的中间位置,同时打开串口接收中断;电脑那边检测到人脸偏移够大就把角度转成3个字节发过来,每收满3个字节,中断回调就把结束标志OK清0,主循环里的while(OK){}这句就放行了,接着把ASCII码转成角度,调用Turn()一步一步转过去,转过以后再置回OK等下一帧数据。一次数据只处理一次,不会重复触发。
Turn()里没有直接把角度写进去,而是每次只挪turn_speed度,挪一步延时8ms,一直挪到目标和当前位置的差值小于Acc为止。这么做是为了让舵机转得平滑一点,要是直接跳过去,机械结构会抖,摄像头画面也会跟着晃,跟踪效果反而差。Abs()就是求两个角度差的绝对值,角度是有正反方向的,这一步不能省。
接下来是人脸跟踪同时发送角度。
1 | |
上位机这边的逻辑分几块来说。getFace()先把画面转成灰度,再用一个3×3的锐化核对图像做滤波,把边缘对比拉大一点,人脸轮廓更清楚,接着把灰度图丢给Haar分类器检测。detectMultiScale里scaleFactor=1.3 表示每轮把图像缩到约 1/1.3 再扫,倍数越大越快但小脸容易漏;minNeighbors=3是候选框的合并阈值,设小了会重叠出一堆框;minSize限制最小人脸尺寸,不然噪点也会被当成脸。如果一帧里检出好几张脸,就用distance()算每张脸中心到画面中心的距离,留最近的那张,这样摄像头优先跟住离得最近的人。
拿到人脸中心的相对偏移后,程序用e=10做了个死区:偏移小的时候不动,免得人脸细微晃动让舵机来回抖;lastime记录上一次的偏移,只有变化量超过e+2才发新角度,省得串口一直刷。偏移为正说明人脸在画面右边,就让角度减一点,为负就加一点,每次只调e/2,靠多帧慢慢逼近。postData()里把角度转成字符串,不够3位就在前面补0,因为STM32那边接收长度固定是3个字节,协议得对齐。发送的时候把串口打开、发完就关,每次开关串口都有开销,更好的做法是初始化时打开一次、全程复用。
代码里还顺手写了一个getFooter(),用的是HOG特征加SVM的行人检测,hog.detectMultiScale那一行就是,姿势和光照对它的影响比较大,算起来也比人脸检测慢,如果要装在车上跟随行人,这个思路可以直接拿来改。摄像头设备号填的是1,换台电脑得自己确认一下;分类器的xml文件要放在脚本同级目录,不然CascadeClassifier会加载失败。
最后的效果让人太满意啦!
总结
这次简单地做了一次项目,积累了一些视觉控制的技术经验,项目分工明确,电控不是我做的,我只负责视觉这块。整套东西不复杂,核心就是检测算偏移、串口发角度、舵机跟着转这么一条线,把死区和转速调好基本就稳了。本期内容不算硬核,可以稍微看一看,感兴趣的话把摄像头和舵机装到小车上,改一下行人检测那部分就能玩起来。