Funpack4-1
——板卡三 自命题:基于毫米波雷达的哨兵摄像头
一、项目介绍
本项目依托于Funpack4-1活动,使用Maix-Dock和毫米波雷达,实现了一个哨兵摄像头。基于Maix-Dock的摄像头和板载的wifi模块,实现了图片拍摄、处理和发送,基于毫米波雷达,实现了动物检测,当有移动物体时,就启动摄像头拍摄并传输。
项目内容:
1、驱动Maix-Dock拍摄并处理照片。
2、连接无线网络并发送照片。
3、基于毫米波雷达检测移动物体。
应用场景
现实生活中有大量安保等级较低,不需要连续监控,但又需要摄像头的场景。如,智能仓储、智能锁和门铃、智能农场。对于这些场景,大部分时候,摄像头的视场角内都没有需要监控的物体,只有有人出现后,才需要监控。并且一般都没有有线网络覆盖,但是可以以较低成本覆盖无线网。本项目哨兵摄像头的出现,可以免于架设有线网,降低闲时功耗,节省电力资源,低成本满足大面积区域的安防需求。
二、总体架构
图2 总体结构图
系统的总体结构如图2所示。Maix-Dock通过串口WIFI模块连接至网络,与上位机进行通信。Maix-Dock根据毫米波雷达的输出控制摄像头是否拍摄图片,然后对图片进行识别,对于有人脸的图片,发送至上位机,上位机接收数据,存储图片,并将图片调整至适宜尺寸进行显示。
三、受控端设计
3.1 硬件介绍
本项目用到的主要板卡为Sipeed M1w Dock Suit K210 开发板,如图3.1.1,该开发板板载M1W AI模块为核心单元,辅以摄像头、LCD、TF卡插槽等外设。对于M1w AI模块,主控为嘉楠堪智科技的边缘智能计算芯片K210(RISC-v架构64位双核),并辅以串口WIFI模块。主控芯片内置64位双核高性能低功耗处理器,每个核都有浮点运算单元(FPU) ,具备卷积人工神经网络硬件加速器(KPU)和快速傅里叶变换加速器(FFT)、搭载现场可编程IO阵列(FPIOA),支持多种主流Al编程框架。M1W模块内置标准802.11 b/g/n协议的ESP8285 WiFi芯片,可让模块轻松联网。
图3.1.1
同时,如图3.1.2本项目还用到了来自于萤火工厂的24GHz毫米波雷达-CEM5826-M11。这是一款高性价比、高灵敏度、高准确度的 24GHz 毫米波人体微动存在检测雷达模块。其通过多普勒效应检测物体的移动,并在传统感应雷达的功能基础上,同时具备检测积累物体微动幅度的运动,以提高识别准确率。
图3.1.2
3.2 软件流程
本项目中受控端软件设计分为两部分,初始化和循环,没用使用中断的操作。
初始化部分包含各个功能模块的初始化,包含GPIO的初始化,初始化并连接WIFI,连接服务端,摄像头的初始化,最后初始化KPU,并载入人脸探测模型。
进入主循环,首先循环读取毫米波雷达的输出。若毫米波雷达未探测到移动物体,置板载LED为绿色,延时50ms,重复该循环。毫米波雷达探测到移动物体时,置板载LED为红色,并进入下一层循环。在下一层循环中,首先拍摄照片,载入KPU进行人脸探测。若无人脸,不延时连续拍摄图片。若有人脸,对图片进行处理、分块发送至上位机。如果发送失败,检查并连接wifi和上位机。
3.3 软件设计
Maix-Dock有C SDK和移植Micro Python的Maix-Py V1。由于Maix-Py V1开发较为容易,且官网给出的样例均基于Maix-Py V1,因而采用Maix-Py V1作为本项目受控端的开发框架。
软件中的库引用及IO初始化等请参见源码,此处不赘述。
3.3.1 wifi连接
WIFI未连接时,开发板会利用try-catch语句持续尝试连接WIFI,包含复位WIFI模块,以及根据指定SSID和密码连接WIFI。WIFI连接成功后,利用try-catch语句持续尝试连接服务器。
def enable_espat():
while( wifi.isconnected() == False):
try:
wifi.reset()
print('try AT connect wifi...')
wifi.connect(SSID, PASW)
except Exception as e :
print(e)
print('network state:', wifi.isconnected(), wifi.ifconfig())
enable_espat()
while 1:
try:
sock.connect((server_ip, server_port))
break
except:
print("连接失败\n")
3.3.2 kpu编程
在进行软件编程前,首先需要将模型烧写进硬件存储器内,可以参照官网教程。注意地址不要填错,可能会损坏固件。然后,在软件编程部分,我们首先要设定模型地址,再将模型加载进kpu,并初始化。再主循环中只要循环调用行6的语句进行识别即可,会返回所有识别到的物体。
model_addr=0x300000
task = None
task = kpu.load(model_addr)
kpu.init_yolo2(task, 0.5, 0.3, 5, anchors) # threshold:[0,1], nms_value: [0, 1]
objects = kpu.run_yolo2(task, img)
3.3.3 图片处理与发送
图片处理部分首先是对识别到的人脸进行标识。然后,因为未压缩的图片体积极大,需要3-5秒时间进行发送,会极大的降低图片更新速率。为了提高这一速度,要在保证人脸特证可辨认的情况下,尽可能的压缩图片体积。显然,我们可以将彩色图片转为黑白图片。对于大多数人来说,由于皮肤颜色,一定程度上甚至提升了信噪比。然后,经多次实验,选取了20这个压缩指标。经过处理的图片体积可以由5KB左右压缩至2KB左右。
然后将图片转换为数据,将数据进行分块,最后进行发送。此处函数使用write和send都可以,笔者测试send会较为稳定。
for obj in objects:
img.draw_rectangle(obj.rect())
img = img.to_grayscale(copy=False)
img = img.compress(quality=20)
img_bytes = img.to_bytes()
block = int(len(img_bytes)/2048)
for i in range(block):
send_len = sock.send(img_bytes[i*2048:(i+1)*2048])
send_len2 = sock.send(img_bytes[block*2048:])
3.3.4 主循环
在主循环中,根据毫米波雷达的输出,决定是否进行图片拍摄和处理。对于拍摄的图片,进行识别,如果存在人脸,就将图片黑白化,调整尺寸并压缩,发送至服务端。如果发送失败,就反复尝试重连wifi和服务端。
while 1:
while led_g.value():
led_r.value(0)
img = sensor.snapshot()
objects = kpu.run_yolo2(task, img)
if objects:
for obj in objects:
img.draw_rectangle(obj.rect())
#img = img.resize(160,120)
img = img.to_grayscale(copy=False)
img = img.compress(quality=20)
img_bytes = img.to_bytes()
print("send len: ", len(img_bytes))
try :
block = int(len(img_bytes)/2048)
for i in range(block):
send_len = sock.send(img_bytes[i*2048:(i+1)*2048])
send_len2 = sock.send(img_bytes[block*2048:])
print("send1\n")
except :
print("发送失败\n")
while (wifi.isconnected()==0):
try:
wifi.reset()
print('try AT connect wifi...')
wifi.connect(SSID, PASW)
if wifi.isconnected():
break
except Exception as e:
print(e)
while 1:
try:
sock.connect((server_ip, server_port))
break
except:
print("连接失败\n")
time.sleep_ms(50)
led_r.value(1)
四、上位机软件
上位机采用QT开发,界面包含两部分,一个用于输出服务器端口号,一个显示接收到的图片。
数据接收基于QT特色的槽与信号机制,将图片接收、存储放置在TCP的接收槽函数中,代码如下。这段代码以虚函数嵌套的方式在有客户端连接的时候,初始化socket,并设置可读信号的槽函数。对于TCP连接,读全部数据时,一般是读取一整个包,不会出现图片的结尾在某一段数据中间,因此直接判断数据结尾是否是jpg格式的图片结尾标志,是的话就关闭当前图片,打开下一个即可。
QObject::connect(mServer,&QTcpServer::newConnection,this,[&](){
mSocket=mServer->nextPendingConnection();
QHostAddress c_addr=mSocket->peerAddress();
QObject::connect(mSocket,&QTcpSocket::readyRead,this,[&](){
QByteArray arr =mSocket->readAll();
m_file.write(arr);
if(arr.endsWith(suffix))
{
fil_l=fil;
fil=QString("C:/tem/%1-%2.jpg").arg(QDate::currentDate().toString("yyyy-MM-dd")).arg(QTime::currentTime().toString("HH-mm-ss-zzz"));
m_file.close();
m_file.setFileName(fil);
m_file.open(QIODevice::ReadWrite);
}
});
});
图片的显示使用了一个定时器超时信号的槽,以将图片的接收与显示解耦,以提高图像接收速度,代码如下。首先读取最近一个接收完成的图片,然后调整至合适的尺寸,最后显示该图片。
QObject::connect(tim1,&QTimer::timeout,this,[&](){
QPixmap pixmap(fil_l);
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap);
if (pixmap.isNull()) {
qDebug() << "Failed to load image:" << fil_l;
}
QGraphicsScene *scene = new QGraphicsScene(this);
scene->addItem(new QGraphicsPixmapItem(pixmap));
ui->graphicsView->setScene(scene);
ui->graphicsView->fitInView(item, Qt::KeepAspectRatio);
});
同时,为了美化QT界面,在比较了各个样式后,采用了QT内置的mac风格。
a.setStyle(QStyleFactory::create("mac"));
五、最终效果
实物如图,效果请参见视频
六、展望
本项目实现了一种哨兵摄像头,能检测移动物体,并实现人脸探测,智能发送数据至上位机。不过受限于串口网络模块的通信波特率等因素,上位机仅能收到单色图片,效果较为一般。