FastDeploy+英特尔NUC+大疆无人机梦幻联动!推动智慧城市巡检应用落地


智慧城市旨在利用大数据、物联网(IoT)、人工智能和5G等数字技术,提高政府公共服务水平、社会治理效能,推动经济增长,不断增强人民群众的获得感、安全感和幸福感。自十四五规划以来,国家和各大主要城市一直加速推进新型智慧城市分级分类建设,但在实施的过程中也遇到了一些问题和困难。
在智慧城市AI部署落地中,常见的问题有 边缘设备硬件不统一、跨平台开发成本高和模型推理速度优化难度大 等。聚焦到AI系统的使用者本身,智慧城市的落地也有很多阻碍,比如环境工程师在户外采集数据时,如果要做到实时采集实时分析存储入库, 必须要背上配有显卡的工作站到户外,同时还要带上XXXXL号移动电源解决供电问题。其重量对环境工程师而言无疑是超负荷的。
为了解决软件工程师开发难度大的问题,本方案演示了 FastDeploy (OpenVINO) + 轻量化模型的AI工作流模式 ,希望能为使用者提供更轻便、简单和高效的解决方案。
本项目利用无人机控制端自带的实时消息传输协议(RTMP)将低空无人机的实时图像传输到推理硬件设备,通过模型推理计算绿化覆盖率、建筑率、车辆数量、人群数量等指标,可用于 城市大规模环境监测、国土低空遥感、道路交通巡检和无人机低空安防 等领域。
本任务是对无人机图像数据流的实时推理,对推理速度有极高的要求。同时为了减少开发难度和迁移成本,我们采用了X86 CPU架构的英特尔NUC迷你电脑套件作为推理硬件,软件选择了FastDeploy推理部署工具箱快速开发后端OpenVINO推理引擎,加速AI模型推理。此外,在AI模型选择上,我们分别选择了飞桨PP-LiteSeg和飞桨PP-YOLO Tiny两个轻量化模型来完成语义分割和目标检测任务。

项目整体流程
如上图所示,首先需要在NUC上架设RTMP推流服务,无人机APP客户端连接内网RTMP服务器,实现无人机的图像实时通过路由器向NUC设备传输。NUC设备端获得图像后,渲染至PySide前端的显示窗口,同时FastDeploy线程执行语义分割和目标检测的推理任务,并把结果和可视化图像实时渲染至前端窗口。
开发环境准备
核心软硬件
英特尔NUC迷你电脑套件
(型号:NUC8i5BEH)
FastDeploy >= 0.2.1
PaddleDetection >= 2.5
PaddleSeg >= 2.6
PySide6 >= 6.3.2
可选软硬件(户外实时采集时用)
大疆系列无人机及DJI Fly APP
路由器(用于内网RTMP传输)

英特尔NUC迷你电脑硬件
无人机实时图传
RTMP (Real-Time Messaging Protocol,实时消息传送协议)是一个设计用来实时数据通信的网络协议,现多用于直播设备与支持RTMP协议的服务器之间进行音视频和数据通信。本项目无人机和推理硬件设备之间的实时图传环节利用的是大疆控制端APP的RTMP推流服务,为了最大限度地减少图传时间延迟,我们直接在英特尔NUC上利用Nginx搭建RTMP服务。
英特尔NUC预装的操作系统是Windows10,所以可以直接下载带RTMP模块的Nginx。
- 下载链接
http://nginx-win.ecsds.eu/download/nginx%201.7.11.3%20Gryphon.zip
下载到本地后解压,在nginx/conf目录下新建一个文件,命名为nginx.conf,输入内容如下。
worker_processes 1;
events {
worker_connections 1024;
rtmp {
server {
listen 8899;
chunk_size 4000;
application live {
live on;
allow publish all;
allow play all;
}
然后打开cmd命令行窗口,进入到Nginx的目录下,输入nginx启动Nginx服务,如图所示即为启动成功。

此时,我们需要在cmd命令行输入ipconfig命令查询NUC设备的内网IPv4地址。

打开DJI Fly APP,找到RTMP直播推流,填入推流地址:rtmp://192.168.31.246:8899/live

APP推流配置完成之后,可以在NUC设备端调用OpenCV的API进行拉流操作以获取无人机实时画面,Python实现代码如下。
import cv2
rtmpUrl = 'rtmp://192.168.31.246:8899/live'
vid = cv2.VideoCapture(rtmpUrl)
while vid.isOpened():
ret,frame = vid.read()
cv2.imshow('RTMP Test',frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
vid.release()
cv2.destroyAllWindows()
运行这段代码后,Python程序会实时获取无人机APP实时推送的RTMP流数据并更新到OpenCV窗口显示,运行效果如下图。

FastDeploy
模型推理部署
打通了无人机图像实时传输的环节之后,我们进入核心的推理部署环节。由以上Python代码可以看出,RTMP数据流经过OpenCV解码后得到图像帧 (frame),因此推理环节的工作本质就是将每一帧图像输入模型然后得到结果并将输出结果可视化。本环节主要分为三个步骤: 模型动静转换、推理脚本编写和前端集成。
模型动静转换
首先,我们需要把使用PaddleSeg和PaddleDetection开发套件训练好的动态图模型转换成静态图模型,这一步利用两个套件分别提供的脚本即可简单完成。已经训练好的动态图模型可以在AI Studio项目中获取。
- 链接
https://aistudio.baidu.com/aistudio/projectdetail/4535658
语义分割目标检测动态图模型和语义分割分别在AI Studio项目中ppyolo_inference_model和output_ppliteseg_stdc1/best_model/目录下,分别调用对应的动静转换脚本将两个动态图模型转换成静态图。
- PaddleSeg
python PaddleSeg/export.py \
--config ppliteseg_stdc1.yml \ #配置文件路径,根据实际情况修改
--model_path output_ppliteseg_stdc1/best_model/model.pdparams \
# 动态图模型路径,根据实际情况修改
--save_dir inference_ppliteseg_stdc1 \ # 导出目录
--input_shape 1 3 512 1024 #模型输入尺寸
- PaddleDetection
python PaddleDetection/tools/export_model.py \
-c PaddleDetection/configs/ppyolo/ppyolo_tiny_650e_coco.yml \ # 配置文件路径,根据实际情况修改
--output_dir=./ppyolo_inference_model \ # 保存模型的路径
-o weights=ppyolo_tiny_650e_coco.pdparams # 动态图模型路径
注:模型转换的详细教程可参考
https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.6/docs/model_export_cn.md
https://github.com/PaddlePaddle/PaddleClas/blob/release/2.4/README_ch.md
完成转换之后,我们能分别得到两套model.pdmodel和model.pdiparams文件以及对应的yaml配置文件。
基于FastDeploy开发
OpenVINO推理模块
本项目使用的是英特尔NUC 迷你电脑套件,选择FastDeploy的OpenVINO后端作为推理部署解决方案。以往使用OpenVINO需要下载套件、安装和配置,过程比较繁琐,因此我采用了FastDeploy的部署方案,调用其内置的OpenVINO推理后端进行快速开发、部署。FastDeploy的预编译库安装和使用教程可参考官方Github文档。
- 链接
https://github.com/PaddlePaddle/FastDeploy
因为考虑到程序的适用性和多种硬件环境兼容,我首先写了Option配置,根据不同的硬件选择不同的推理后端,在CPU环境中,默认使用OpenVINO作为推理后端。
import cv2
import numpy as np
import fastdeploy as fd
from PIL import Image
from collections import Counter
def FastdeployOption(device=0):
option = fd.RuntimeOption()
if device == 0:
option.use_gpu()
else:
# 使用OpenVino推理
option.use_openvino_backend()
option.use_cpu()
return option
然后,将语义分割模型推理代码封装成一个类,方便前端快速调用。在init方法中,直接调用了SegModel()函数进行模型初始化(热加载),通过SegModel.predict()完成结果的推理,得到推理结果之后执行postprocess()对结果进行解析,提取建筑和绿地的像素数量,统计图像占比,得到环境要素的占比结果。最后调用FastDeploy内置的vis_segmentation()可视化函数,对推理结果进行可视化。
class SegModel(object):
def __init__(self, device=0) -> None:
self.segModel = fd.vision.segmentation.ppseg.PaddleSegModel(
model_file = 'inference/ppliteseg/model.pdmodel',
params_file = 'inference/ppliteseg/model.pdiparams',
config_file = 'inference/ppliteseg/deploy.yaml',
runtime_option=FastdeployOption(device)
def predict(self, img):
segResult = self.segModel.predict(img)
result = self.postprocess(segResult)
visImg = fd.vision.vis_segmentation(img, segResult)
return result, visImg
def postprocess(self, result):
resultShape = result.shape
labelmap = result.label_map
labelmapCount = dict(Counter(labelmap))
pixelTotal = int(resultShape[0] * resultShape[1])
# 统计建筑率和绿地率
buildingRate, greenRate = 0, 0
if 8 in labelmapCount:
buildingRate = round(labelmapCount[8] / pixelTotal* 100, 3)
if 9 in labelmapCount:
greenRate = round(labelmapCount[9] / pixelTotal * 100 , 3)
return {"building": buildingRate, "green": greenRate}
同理,直接调用FastDeploy的PPYOLO()方法完成模型的推理,经过后处理格式化数据之后调用对应的可视化函数vis_detection()进行渲染。
class DetModel(object):
def __init__(self, device=0) -> None:
self.detModel = fd.vision.detection.PPYOLO(
model_file = 'inference/ppyolo/model.pdmodel',
params_file = 'inference/ppyolo/model.pdiparams',
config_file = 'inference/ppyolo/infer_cfg.yml',
runtime_option=FastdeployOption(device)
# 阈值设置
self.threshold = 0.3
def predict(self, img):
detResult = self.detModel.predict(img.copy())
result = self.postprocess(detResult)
visImg = fd.vision.vis_detection(img, detResult, self.threshold, 2)
return result, visImg
def postprocess(self, result):
# 得到结果
detIds = result.label_ids
detScores = result.scores
# 统计数量
humanNum, CarNum = 0, 0
for i in range(len(detIds)):