关于一个增量式人脸跟踪云台

介绍

识别人脸的方法有很多,这里使用了HOG特征进行的识别,同时这里的识别仅画出人脸,而不会识别人脸是谁,如果需要聚类的话就需要使用CNN了,我这里不写只是说一下,对于人脸的识别我们可以使用目标检测方法,包括但不局限于FaceNet、Fast RCNN等。

设备清单:

  • Stm32F103C8T6
  • USB转TTL
  • 一枚摄像头,我用的是一个红外摄像头,视角为105°
  • 舵机(180°足够)
  • 供电部分

代码

首先看看stmcubemx的PWM配置:

1

同时USART设置速度为115200即可。我们看看单片机的代码:

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
#include "Method.h"
#include "tim.h"
#include "usart.h"

// ----
// 大小
uint16_t angle_memory=0;
// 数据结束标志
uint8_t OK=1;
// 角度缓冲区
uint8_t data[3];
// ----

// 传递角度返回占空比
uint16_t Servos(uint8_t angle){
uint16_t pwm;
pwm=500+angle*((2500-500)/180)+(angle*pro);
return pwm;
}

// TIM1进行旋转
void Turn(uint8_t angle){
while(Abs(angle,angle_memory)>=Acc){
if(angle>angle_memory){
__HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_1, Servos(angle_memory));
angle_memory+=turn_speed;
}else if(angle<angle_memory){
__HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_1, Servos(angle_memory));
angle_memory-=turn_speed;
} else{
angle_memory=angle;
}
HAL_Delay(8);
}
}

// 初次上电进行复位
void Start(){
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
__HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_1, Servos(90));
HAL_UART_Receive_IT(&huart1,data,LENGTH);
angle_memory=90;
HAL_Delay(1000);
}

// 取绝对值
uint16_t Abs(uint16_t a,uint16_t b){
uint16_t abs;
if(a>=b)abs=a-b;
else abs=b-a;
return abs;
}

// 循环体的函数
void Control(){
while (OK){}
Turn(data_hand(data));
OK=1;
}

// 中断处理,负责清理结束标志
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle){
OK=0;
HAL_UART_Receive_IT(UartHandle,data,LENGTH);
}

// 获取USART传递的数据并返回上位机传递的角度
uint8_t data_hand(uint8_t *Data){
uint8_t x=0;
x=((Data[0]-48)*100+(Data[1]-48)*10+(Data[2]-48));
return x;
}

这样我们就可以通过串口控制舵机了。接下来是人脸跟踪同时发送角度。

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
import cv2
import numpy as np
import serial
import time

# 参数传递需要的数据
# 初始角度
degree = 90
# 误差
e = 10


# 两点距离,目的是让摄像头跟随最近的人脸
def distance(x1, y1, x2, y2):
return np.sqrt(np.square(x1 - x2) + np.square(y1 - y2))


# 发送数据
def postData(x, name, bpm):
ser = serial.Serial(name, bpm)
data = str(int(x))
print(f'data = {data}')
# STM32内的数据暂存为3为,所以需要补0
if len(data) == 2:
data = '0' + data
elif len(data) == 1:
data = '00' + data
ser.write(data.encode('utf-8'))
time.sleep(0.1)
ser.close()
pass


# 发现人脸
def getFace(videoStream):
gary = cv2.cvtColor(videoStream, cv2.COLOR_RGB2GRAY)

cent_x = videoStream.shape[1] / 2
cent_y = videoStream.shape[0] / 2
dis = distance(cent_x, cent_y, 0, 0)
back_x = cent_x
back_y = cent_y

kernel = np.array([
[-1, 1, -1],
[1, 1, 1],
[-1, 1, -1]
])
# 锐化人脸
gary = cv2.filter2D(gary, -1, kernel)
preclass = cv2.CascadeClassifier("haarcascade_frontalface_alt.xml")

faceRects = preclass.detectMultiScale(gary, scaleFactor=1.3, minNeighbors=3, minSize=(12, 12))
if len(faceRects) > 0:
for faceRect in faceRects:
x, y, w, h = faceRect
if distance(x + w / 2, y + h / 2, cent_x, cent_y) < dis:
dis = distance(x + w / 2, y + h / 2, cent_x, cent_y)
back_x = x + w / 2
back_y = y + h / 2
pass
cv2.rectangle(videoStream, (x, y), (x + w, y + h), (0, 255, 255), 4)
pass
pass
# 最近的人脸的相对坐标
return back_x - cent_x, back_y - cent_y


# 发现行人
def getFooter(img):
cent_x = img.shape[1] / 2
cent_y = img.shape[0] / 2
dis = distance(cent_x, cent_y, 0, 0)
back_x = cent_x
back_y = cent_y
kernels = np.array([
[1, 0, 1],
[0, -3, 0],
[1, 0, 1]
])
gary_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
blur_img = cv2.filter2D(gary_img, -1, kernels)
hog = cv2.HOGDescriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
(rects, weights) = hog.detectMultiScale(blur_img, winStride=(4, 4), padding=(8, 8), scale=0.9)
print(rects)
for (x, y, w, h) in rects:
if distance(x + w / 2, y + h / 2, cent_x, cent_y) < dis:
dis = distance(x + w / 2, y + h / 2, cent_x, cent_y)
back_x = x + w / 2
back_y = y + h / 2
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 255, 0), 4)
pass
return cent_x - back_x, cent_y - back_y


if __name__ == "__main__":
video = cv2.VideoCapture(1)
if video.isOpened():
lastime = 0
while True:
ret, frame = video.read()
if not ret:
print("Video End!")
break
x, y = getFace(frame)
print(f'x:{x}')
cv2.imshow("Video", frame)
# 保证变化足够多
if not -1 * e < x < e and abs(lastime - x) > e+2:
if x> 0:
degree = degree - e/2
else:
degree = degree + e/2
if degree != 90:
# 发送角度数据
postData(degree, "COM11", 115200)
pass
else:
lastime = x
pass
if cv2.waitKey(1) & 0xFF == ord('q'):
break
pass
pass

最后的效果让人太满意啦!

总结

这次简单的做了一次项目,积累了一些视觉控制的技术经验,本次的项目分工明确,电控不是我做的。我只是做了视觉。

本期内容不算硬核,可以稍微看一看。


关于一个增量式人脸跟踪云台
https://blog.minloha.cn/posts/130833fd81258f2023030810.html
作者
Minloha
发布于
2023年3月8日
更新于
2024年9月15日
许可协议