
📌 项目概述
> "如果让机器代替人类走进火场、直面入侵者,工厂安全将变得多么不同?"
这是一个基于四旋翼无人机的智能巡检安防系统——它能在深夜自主巡航工厂、在火灾警报响起时冲向火源、在入侵发生时锁定可疑人员,并将一切实时回传给人类操作员。没有冒险、没有伤亡、只有高效。
本项目由杭州电子科技大学陈东韧团队开发,面向复杂工业环境下的自主巡逻、火情识别、入侵检测三大核心场景,构建了一套完整的"感知—决策—执行"闭环系统。
🛠️ 硬件清单
| 组件 | 型号/规格 |
|---|---|
| 飞控板 | Pixhawk 2.4.8 |
| 处理器 | NVIDIA Jetson Nano |
| 深度相机 | Intel RealSense / Astra Pro |
| 定位传感器 | GPS + 超声波测距仪 + 光流传感器 |
| 平台 | 四旋翼无人机(自组) |
🔗 开源代码
项目完整源代码已开源在 GitHub:RobotV_Security-inspection-drone
🏗️ 系统架构一览
架构分层
整个系统以Pixhawk 2.4.8 飞控板为核心,搭载 GPS、超声波传感器、光流传感器与 Intel RealSense 3D 深度相机,在 Jetson Nano 边缘计算平台上完成全部视觉处理任务。

ORB-SLAM2 多线程架构
系统的"大脑"是运行在 Jetson Nano 上的 ORB-SLAM2 视觉 SLAM 系统。为了保证实时性,整个系统采用四线程并行架构,各线程职责清晰、互不阻塞:
// System.cpp —— 四大线程的创建与绑定
//Initialize the Tracking thread (主线程)
mpTracker = new Tracking(this, mpVocabulary, mpFrameDrawer, mpMapDrawer,
mpMap, mpKeyFrameDatabase, strSettingsFile, mSensor);
//Initialize the Local Mapping thread and launch (后台线程)
mpLocalMapper = new LocalMapping(mpMap, mSensor==MONOCULAR);
mptLocalMapping = new thread(&ORB_SLAM2::LocalMapping::Run, mpLocalMapper);
//Initialize the Loop Closing thread and launch (后台线程)
mpLoopCloser = new LoopClosing(mpMap, mpKeyFrameDatabase, mpVocabulary, mSensor!=MONOCULAR);
mptLoopClosing = new thread(&ORB_SLAM2::LoopClosing::Run, mpLoopCloser);
//Initialize the Viewer thread and launch (可视化线程)
mpViewer = new Viewer(this, mpFrameDrawer, mpMapDrawer, mpTracker, strSettingsFile);
mptViewer = new thread(&Viewer::Run, mpViewer);
> 💡 架构解读: 这种架构设计是系统在 Jetson Nano 上实现 30+ FPS 实时运行 的根本保障。Tracking 主线程专注于每一帧的实时位姿估计;Local Mapping 后台异步处理新关键帧,构建局部地图;Loop Closing 后台检测回环、消除全局漂移;Viewer 独立线程负责 OpenGL 渲染,不影响实时性。各线程通过互斥锁(mutex)安全通信,确保数据一致性。
🧠 核心技术深度解读
1️⃣ 实时建图:ORB-SLAM2 的多线程加速

传统的 SLAM 算法在实时性上往往力不从心,而本项目的创新在于:
– 多线程并行架构:特征提取、特征匹配、位姿估计、局部建图分别运行在独立线程中,互不阻塞
– 运动模型辅助的特征点筛选:利用无人机前后帧的运动先验,剔除动态物体产生的错误特征点
– 高帧率保障:在 Jetson Nano 的有限算力下,仍能维持 30+ FPS 的实时建图能力
1.特征提取的并行化加速
在 Frame.cpp 的构造函数中,左右目图像的特征提取被显式分配到两个线程中并行执行:
// Frame.cpp —— 双目ORB特征提取的并行化
// ORB extraction
thread threadLeft(&Frame::ExtractORB, this, 0, imLeft);
thread threadRight(&Frame::ExtractORB, this, 1, imRight);
threadLeft.join();
threadRight.join();
> 💡 解读: 在无人机高速飞行时,每一毫秒的延迟都可能导致建图漂移。通过显式开启双线程并行提取 ORB 特征,左右目图像同时处理而非串行等待,有效榨干了 Jetson Nano 四核 CPU 的性能,保证了高帧率输入。
2.四叉树特征均匀化
为了避免特征点"扎堆"在纹理丰富的区域,ORB-SLAM2 引入了四叉树(OctTree)均匀化算法,将图像递归分割为网格,每个网格仅保留响应最强的特征点:
// ORBextractor.cpp —— 四叉树特征均匀分布
vector<cv::KeyPoint> ORBextractor::DistributeOctTree(
const vector<cv::KeyPoint>& vToDistributeKeys, ...)
{
// Divide node into four children
ExtractorNode n1, n2, n3, n4;
lit->DivideNode(n1, n2, n3, n4);
// Retain the best point in each node
vector<cv::KeyPoint> vResultKeys;
for(list<ExtractorNode>::iterator lit = lNodes.begin(); lit != lNodes.end(); lit++)
{
vector<cv::KeyPoint>& vNodeKeys = lit->vKeys;
cv::KeyPoint* pKP = &vNodeKeys[0];
float maxResponse = pKP->response;
for(size_t k = 1; k < vNodeKeys.size(); k++)
if(vNodeKeys[k].response > maxResponse)
pKP = &vNodeKeys[k];
vResultKeys.push_back(*pKP);
}
return vResultKeys;
}
> 💡 解读: 普通的 FAST 特征提取往往集中在纹理丰富的角落,导致特征点分布不均。这在无人机倾斜飞行或快速转向时会导致算法失效。四叉树均匀化保证了特征点在图像中均匀分布,即使无人机剧烈晃动,也能提供足够的视差信息进行位姿解算。
3.特征点网格化(动态物体剔除的基础)
为了应对工厂中的移动工人或设备,系统将特征点进行空间分区存储:
// Frame.cpp —— 特征点网格化
void Frame::AssignFeaturesToGrid()
{
int nReserve = 0.5f * N / (FRAME_GRID_COLS * FRAME_GRID_ROWS);
for(unsigned int i = 0; i < FRAME_GRID_COLS; i++)
for(unsigned int j = 0; j < FRAME_GRID_ROWS; j++)
mGrid[i][j].reserve(nReserve);
for(int i = 0; i < N; i++)
{
const cv::KeyPoint& kp = mvKeysUn[i];
int nGridPosX, nGridPosY;
if(PosInGrid(kp, nGridPosX, nGridPosY))
mGrid[nGridPosX][nGridPosY].push_back(i);
}
}
> 💡 解读: 这段代码建立的 4×4 图像网格(Grid) 是动态物体剔除的关键基础。它将特征点空间分区存储,使得系统能快速定位并剔除位于动态物体运动区域内的异常点,将建图集中在静态墙壁和管线上,极大提升了动态环境下的鲁棒性。
2️⃣ 位姿跟踪:运动模型与重定位
1.运动模型跟踪 —— 高帧率的核心策略
无人机在工厂中飞行速度快、易出现运动模糊。相比耗时的"参考关键帧匹配",系统优先采用运动模型跟踪,利用上一帧的速度预测当前帧位姿:
// Tracking.cpp —— 基于运动模型的位姿跟踪
bool Tracking::TrackWithMotionModel()
{
ORBmatcher matcher(0.9, true);
UpdateLastFrame();
// 利用上一帧的速度(Velocity)预测当前帧位姿
mCurrentFrame.SetPose(mVelocity * mLastFrame.mTcw);
// 在预测位置附近进行特征点投影搜索
int nmatches = matcher.SearchByProjection(mCurrentFrame, mLastFrame, th, ...);
// 如果匹配数不足,扩大搜索窗口
if(nmatches < 20)
nmatches = matcher.SearchByProjection(mCurrentFrame, mLastFrame, 2 * th, ...);
// 优化帧位姿
Optimizer::PoseOptimization(&mCurrentFrame);
// 统计有效内点数量
int nmatchesMap = 0;
for(int i = 0; i < mCurrentFrame.N; i++)
if(mCurrentFrame.mvpMapPoints[i] && !mCurrentFrame.mvbOutlier[i])
nmatchesMap++;
return nmatchesMap >= 10;
}
> 💡 解读: 运动模型的核心思想是 "用速度推位姿"。通过上一帧的位姿变换(Velocity = Tcw_current × Twc_last)预测当前帧的相机位置,然后只需要在预测位置附近进行特征点投影搜索。这大大减少了搜索范围,降低了计算量,是 Jetson Nano 上实现高帧率跟踪的核心策略。
2.重定位机制 —— 鲁棒性的最后防线
当跟踪丢失时(如无人机穿越狭窄管道或被工人遮挡),系统会立即触发基于词袋模型(BoW)的重定位:
// Tracking.cpp —— 重定位恢复
bool Tracking::Relocalization()
{
// 计算当前帧的词袋向量
mCurrentFrame.ComputeBoW();
// 在关键帧数据库中检索候选帧
vector<KeyFrame*> vpCandidateKFs =
mpKeyFrameDB->DetectRelocalizationCandidates(&mCurrentFrame);
// 使用 PnP Solver 恢复位姿
PnPsolver* pSolver = new PnPsolver(mCurrentFrame, vvpMapPointMatches[i]);
cv::Mat Tcw = pSolver->iterate(5, bNoMore, vbInliers, nInliers);
return !Tcw.empty();
}
> 💡 解读: 重定位的本质是 "用历史地图找回自己"。一旦跟踪丢失,系统检索历史关键帧数据库,利用 PnP(Perspective-n-Point)算法 计算当前帧与历史地图的匹配关系,快速恢复位姿。这使得无人机在受到干扰后能迅速"找回自我",极大提升了巡检任务的可靠性。
3️⃣ 位姿解算:EPnP 算法
当无人机重定位或初始化时,需要从已知的 3D 地图点和 2D 图像点中反推相机位姿,这就是 PnP 问题。系统采用的是 EPnP(Efficient PnP)算法:
// PnPsolver.cpp —— EPnP 算法核心
double PnPsolver::compute_pose(double R[3][3], double t[3])
{
// Step 1: 选择4个虚拟控制点(从点云的PCA中得到)
choose_control_points();
// Step 2: 计算重心坐标
compute_barycentric_coordinates();
// Step 3: 构建线性系统矩阵 M
CvMat* M = cvCreateMat(2 * number_of_correspondences, 12, CV_64F);
fill_M(M, ...);
// Step 4: SVD分解求解
cvSVD(&MtM, &D, &Ut, 0, CV_SVD_MODIFY_A | CV_SVD_U_T);
// Step 5-7: 高斯牛顿优化(分别尝试1个、2个、3个控制点的情况)
find_betas_approx_1(&L_6x10, &Rho, Betas[1]);
gauss_newton(&L_6x10, &Rho, Betas[1]);
rep_errors[1] = compute_R_and_t(ut, Betas[1], Rs[1], ts[1]);
// 选择重投影误差最小的方案
int N = 1;
if(rep_errors[2] < rep_errors[1]) N = 2;
if(rep_errors[3] < rep_errors[N]) N = 3;
copy_R_and_t(Rs[N], ts[N], R, t);
return rep_errors[N];
}
> 💡 解读: EPnP 的核心创新在于将非线性 PnP 问题转化为线性最小二乘问题。它通过将 3D 点表示为四个虚拟控制点的加权和,构建线性方程组,再通过高斯牛顿法优化。相比传统的 P3P,EPnP 速度更快且精度更高,是实时 SLAM 的首选位姿解算方案。代码中尝试了 1/2/3 个控制点三种配置,自动选择最优解,兼顾了速度与精度。
4️⃣ 回环检测与 Sim3 尺度修正
单目 SLAM 最大的问题是尺度漂移(Scale Drift)——无人机飞一圈回来,发现地图"缩水"或"膨胀"了。系统通过 Sim3 相似变换 一次性修正旋转、平移和尺度误差:
// Sim3Solver.cpp —— Sim3 相似变换计算
void Sim3Solver::ComputeSim3(cv::Mat& P1, cv::Mat& P2)
{
// Step 1-2: 计算质心和相对坐标
ComputeCentroid(P1, Pr1, O1);
ComputeCentroid(P2, Pr2, O2);
// Step 3-4: 构建并分解 M 矩阵,通过特征向量求解旋转
cv::Mat M = Pr2 * Pr1.t();
cv::eigen(N, eval, evec);
cv::Rodrigues(vec, mR12i); // 从角轴表示转换为旋转矩阵
// Step 5: 计算尺度因子 s
if(!mbFixScale)
{
double nom = Pr1.dot(P3);
double den = norm(P3)^2;
ms12i = nom / den; // 尺度 = 两组点云的距离比值
}
// Step 6-7: 计算平移 + 组装变换矩阵
mt12i = O1 - ms12i * mR12i * O2;
mT12i = [sR | t]; // 4×4 的 Sim3 变换矩阵
}
> 💡 解读: 普通的 SE3 变换(旋转+平移)无法修正尺度漂移。Sim3 额外引入了一个尺度因子 s,在回环矫正时一次性修正轨迹的旋转、平移和尺度误差。这段代码通过对两组对应点云进行质心对齐和 SVD 分解,优雅地求解出完整的 Sim3 变换,确保地图的全局一致性。
回环边的可视化
在 Pangolin 可视化界面中,回环边以特殊颜色标注,是证明系统消除了累计误差的最有力证据:
// MapDrawer.cpp —— 绘制回环边
void MapDrawer::DrawKeyFrames(const bool bDrawKF, const bool bDrawGraph)
{
// 共视图(绿色线):关键帧之间共享的特征点数量
glColor4f(0.0f, 1.0f, 0.0f, 0.6f);
// 生成树(蓝色/默认):保证地图连通性的主干
KeyFrame* pParent = vpKFs[i]->GetParent();
// 回环边(Loop Edges):当无人机回到原点时的特殊连线
set<KeyFrame*> sLoopKFs = vpKFs[i]->GetLoopEdges();
}
5️⃣ 关键帧管理:地图裁剪与后端优化
1.自适应关键帧策略
无人机沿固定航线巡航时,连续帧之间的重叠度极高。如果不剔除冗余关键帧,地图数据量会暴涨:
// LocalMapping.cpp —— 关键帧剔除
void LocalMapping::KeyFrameCulling()
{
vector<KeyFrame*> vpLocalKeyFrames = mpCurrentKeyFrame->GetVectorCovisibleKeyFrames();
for(vector<KeyFrame*>::iterator vit = vpLocalKeyFrames.begin();
vit != vpLocalKeyFrames.end(); vit++)
{
KeyFrame* pKF = *vit;
int nRedundantObservations = 0;
int nMPs = 0;
const vector<MapPoint*> vpMapPoints = pKF->GetMapPointMatches();
for(size_t i = 0, iend = vpMapPoints.size(); i < iend; i++)
{
MapPoint* pMP = vpMapPoints[i];
if(pMP && !pMP->isBad())
{
nMPs++;
if(pMP->Observations() > 3) // 被至少3个其他关键帧观测到
nRedundantObservations++;
}
}
// 如果90%的点都被冗余观测,则剔除该关键帧
if(nRedundantObservations > 0.9 * nMPs)
pKF->SetBadFlag();
}
}
> 💡 解读: 这段代码实现了 "90% 冗余度剔除策略"。如果一个关键帧看到的地图点,90% 都能在其他关键帧中找到,那么这个关键帧就是多余的。这确保了地图库的轻量化,是系统能长时间连续作业的保障。
2.地图点生命周期管理
// LocalMapping.cpp —— 地图点剔除
void LocalMapping::MapPointCulling()
{
list<MapPoint*>::iterator lit = mlpRecentAddedMapPoints.begin();
while(lit != mlpRecentAddedMapPoints.end())
{
MapPoint* pMP = *lit;
if(pMP->isBad())
{
lit = mlpRecentAddedMapPoints.erase(lit);
}
// 1. 观测率过低(<25%),说明跟踪不稳定
else if(pMP->GetFoundRatio() < 0.25f)
{
pMP->SetBadFlag();
lit = mlpRecentAddedMapPoints.erase(lit);
}
// 2. 创建时间较长但仍缺乏足够的观测支持
else if(((int)nCurrentKFid - (int)pMP->mnFirstKFid) >= 2
&& pMP->Observations() <= cnThObs)
{
pMP->SetBadFlag();
lit = mlpRecentAddedMapPoints.erase(lit);
}
else
lit++;
}
}
> 💡 解读: 在工厂巡检中,动态物体(如移动的叉车、工人)会产生大量错误的临时地图点。这段代码通过三重过滤机制(低观测率、少观测次数、超时淘汰)及时清理这些"坏点"。特别是 GetFoundRatio() < 0.25f 这一条件,能有效剔除那些只在少数几帧中出现又消失的动态物体特征。
3.最具区分度的描述子选取
同一个地图点可能被几十个关键帧观测到,每个关键帧提取的描述子都有微小差异。系统会选择"中值距离最小"的那一个:
// MapPoint.cpp —— 计算最具代表性的描述子
void MapPoint::ComputeDistinctiveDescriptors()
{
vector<cv::Mat> vDescriptors;
// ... 收集所有观测到此点的描述子
// 计算两两之间的距离矩阵
for(size_t i = 0; i < N; i++)
for(size_t j = i + 1; j < N; j++)
Distances[i][j] = ORBmatcher::DescriptorDistance(vDescriptors[i], vDescriptors[j]);
// 选择与其他描述子平均距离最小的那个
int BestMedian = INT_MAX;
for(size_t i = 0; i < N; i++)
{
vector<int> vDists(Distances[i], Distances[i] + N);
sort(vDists.begin(), vDists.end());
int median = vDists[0.5 * (N - 1)];
if(median < BestMedian)
{
BestMedian = median;
BestIdx = i;
}
}
mDescriptor = vDescriptors[BestIdx].clone();
}
> 💡 解读: 选取最具代表性的描述子相当于选取了该点最"标准"的样貌,极大地提高了无人机在视角变化时(如倾斜飞行)的特征匹配成功率。
**6️⃣ 全局航迹规划:BIT* 算法**
为什么选择 BIT 而非传统的 A 或 RRT?
| 算法 | 搜索效率 | 路径质量 | 适合动态环境 |
|---|---|---|---|
| A* | 中等 | 高 | ❌ |
| RRT | 低 | 低 | ✅ |
| **BIT*航迹规划** | 高 | 高 | ✅ |
BIT* 的核心思想是分批采样 + 启发式引导:每次迭代生成一批随机采样点,利用启发函数引导搜索方向,避免无效探索。同时,算法会对稀疏点云地图中的障碍物进行膨胀处理,确保规划出的路径与障碍物保持安全距离。
7️⃣ 动态避障:3DVFH+ 算法
VFH(Vector Field Histogram)是一种经典的局部避障算法,而本项目采用的 3DVFH+ 是其三维增强版本,专为无人机设计。
工作原理

创新点: 算法不仅考虑静态障碍物,还能根据实时点云信息预测动态障碍物的运动趋势,提前规划绕行策略。相比传统 VFH,3DVFH+ 增加了高度维度的决策能力,使无人机能够"向上飞"绕过障碍。
8️⃣ 火情识别:HSV 颜色空间 + 形态学处理
这是整个系统中最具"轻量化智慧"的模块——不依赖深度学习,仅凭传统计算机视觉即可实现高精度火焰检测。
完整识别流程

关键参数解读
| 参数 | 作用 | 典型值 |
|---|---|---|
| HSV 红色色域 | 过滤火焰颜色 | H: 0-6, S: 150-255, V: 150-255 |
| 形态学核大小 | 控制区域闭合程度 | 7×7 椭圆核 |
| 轮廓面积阈值 | 过滤小面积误检 | > 5000 像素 |
| 帧差阈值 | 判断火焰动态变化 | > 10000 像素差异 |
> 💡 为什么不用深度学习? 在工厂巡检场景中,深度学习模型需要大量标注数据且推理延迟较高。而基于 HSV + 形态学的方法零训练成本、毫秒级响应、CPU 即可运行,配合帧差法对火焰"跳动"特性的判断,准确率完全满足工业应用需求。
9️⃣ 入侵检测:Haar + CNN 双级过滤
入侵检测模块采用两阶段策略,兼顾实时性与准确性:

– Haar Cascade:OpenCV 内置的 Haar 特征分类器,检测速度极快
– CNN 分类器:使用仅包含员工人脸的数据集训练
– 为什么不用端到端的目标检测? 工厂环境固定、人员身份明确,专用 CNN 的误检率远低于通用目标检测模型
🧪 功能测试场景

场景一:夜间自主巡航 🌙
> "深夜的工厂,无人机关灯巡逻,像一只不会疲倦的电子眼。"
无人机在巡航模式下自动扫描厂房环境,实时建图并回传视频画面。操作员可在监控端实时查看厂房状态,及时发现未关闭的阀门、异常堆放的货物等安全隐患。
测试结果: ✅ 无人机成功完成自主巡航,实时建图稳定,航迹规划高效避障,视频回传流畅。
场景二:火情应急响应 🔥
> "烟雾报警器响了,但没人知道火有多大。无人机第一个冲进现场。"
当烟雾/温度传感器触发警报后,无人机自动飞往报警点,利用 HSV 火焰识别模块确认是否真的发生火灾,并将现场画面实时回传。
测试结果: ✅ 无人机快速抵达报警位置,准确识别火情并触发警报,全程无需人员靠近危险区域。
场景三:入侵检测与预警 🚨
> "半夜有人翻墙进入工厂?无人机在安保人员到场之前,已经锁定了入侵者的位置。"
警报响起时,无人机自动飞往目标区域。若检测到非员工人员,立即在画面中标记红色框并发出警报,同时持续追踪目标位置。
测试结果: ✅ 无人机成功识别入侵者并发出警报,对工厂员工的识别准确率达 100%,入侵者检测无漏报。
💡 创新亮点总结
应用创新
| 亮点 | 说明 |
|---|---|
| 🚁 地形无忧 | 四旋翼平台突破地面机器人受限于地形和障碍物的瓶颈 |
| 🎯 全自动化闭环 | 报警→自动响应→实时回传,无需人工介入 |
| 🔄 多工作模式 | 支持巡航模式、遥控模式、定点导航多种工作方式 |
算法创新
| 亮点 | 说明 |
|---|---|
| 📐 ORB-SLAM2 加速 | 多线程 + 动态物体剔除,30+ FPS 实时建图 |
| 🗺️ **BIT*航迹规划** | 融合图论与采样规划优势,生成高质量全局航迹 |
| 🛡️ 3DVFH+ 避障 | 三维动态避障,支持高度维度决策 |
| ⚡ 轻量火焰识别 | HSV + 形态学,零训练成本、毫秒级响应 |
| 👤 双级入侵检测 | Haar + CNN,低误检率 |
> "工业安全不应以人的生命为代价。当无人机代替人类走进危险,我们不仅获得了效率,更守护了每一个平凡而珍贵的生命。"