
项目来源:第四届 3D 视觉创新应用竞赛 项目名称:无人机自动化巡检监测 团队/作者:莫泓乐,华南师范大学 深度相机:Orbbec Gemini 2 支持设备:NVIDIA Jetson Orin Nano、Ubuntu 20.04、CUAV V5+ 自动驾驶仪 源代码:https://github.com/3DCVdeveloper/RobotV_Drone-automation
一句话看懂项目
这不是一架只会“拍视频”的无人机,而是一套面向室内巡检的自动化闭环系统:它先在 NVIDIA Isaac Sim 中完成算法验证,再部署到自主设计组装的四旋翼无人机上,通过 Gemini 2 深度相机获取环境信息,由 ORB-SLAM3 完成无 GPS 定位与建图,由 EGO-Planner 生成安全轨迹,最后通过 PX4/CUAV V5+ 飞控执行飞行动作,并在测试场地完成巡检监测与三维重建。
换句话说,它解决的是一个很现实的问题:在厂房、机房、管廊这类 GPS 信号不可用、空间狭小、障碍密集的地方,如何让无人机自己知道“我在哪里、我要去哪、路上能不能飞、飞完之后留下什么数据”。
为什么这个项目值得看?
■ 它瞄准的是“室内巡检”这个更难、更刚需的场景
传统巡检无人机常依赖 GPS,到了厂房、车间、地下设施、管廊等空间,定位精度和可靠性都会明显下降。该项目把目标放在室内无 GPS 环境,难度更高,也更贴近工业巡检落地。
■ 它不是单一算法演示,而是完整系统闭环
代码和资料中可以看到:相机驱动、TF/里程计转换、ORB-SLAM3、EGO-Planner、状态机、MAVROS、PX4 飞控被串成一条工作链路。真正的挑战不只是“算法能跑”,而是多个模块能不能在真实飞行中稳定协同。
■ 它把巡检结果从“二维视频”推进到“三维空间复盘”
项目展示了场景三维重建能力。相比只留下照片或视频,三维场景能支持多角度查看、位置回溯和空间化记录,后续可用于设备异常复核、巡检路径优化和数字孪生资产沉淀。
项目背景:为什么室内巡检很难自动化?
电力、水务、燃气、厂房设备等基础设施数量庞大,传统人工巡检通常存在三类痛点:
- 成本高:重复性点检、抄表、巡视占用大量人力。
- 风险高:高压设备、狭窄管廊、燃气设施、复杂厂区都不适合人员频繁进入。
- 记录弱:人工巡检依赖经验和表格,难以形成连续、可复查、可量化的空间数据。
无人机看似是天然答案,但室内环境会立刻暴露新问题:没有 GPS、障碍物多、空间窄、光照和纹理不稳定、续航和载荷受限。项目的关键点就在于,它没有把无人机当作“遥控摄像头”,而是把它设计成一个能自主感知、定位、规划、执行和记录的移动机器人平台。
系统架构:从“看见”到“飞稳”的五层闭环
项目整体可以拆成五个层次:

代码中,orbbec_2-main 负责 Gemini 2 接入、TF/里程计发布和状态机桥接;orb_slam3_ros-master 提供 Orbbec RGB-D / RGB-D-Inertial 启动配置和 Gemini 2 标定参数;ego-planner-master 则负责局部规划、轨迹发布和无人机 waypoint 接收。三部分分工清晰,构成了一个 ROS 风格的模块化系统。
为了让读者更容易理解这套系统,可以先把它看成一条“传感器数据不断向飞行动作收敛”的数据管线:
Gemini 2
├─ /camera_orb/color/image_raw
├─ /camera_orb/depth/image_raw
└─ /camera_orb/gyro_accel/sample
↓
ORB-SLAM3
├─ 输出相机/机体位姿
├─ 发布地图点、关键帧与轨迹
└─ 通过 TF/里程计桥接为 /odom
↓
EGO-Planner
├─ 读取 /odom 与深度信息
├─ 根据 waypoint 生成局部轨迹
└─ 输出 /planning/pos_cmd_geo
↓
状态机 + MAVROS
├─ 等待定位与规划器就绪
├─ 将规划结果转为 /sm/pose
└─ 持续发送 PX4 OFFBOARD setpoint
↓
CUAV V5+ / PX4
└─ 执行起飞、悬停、巡检、返航、降落
核心硬件:为什么 Gemini 2 + Jetson Orin Nano 是合适组合?
Gemini 2 是项目中的关键传感器。它不是普通摄像头,而是双目结构光高精度深度相机,能够同时提供 RGB 图像、深度信息,并集成 6 轴 IMU。对于室内巡检,这类深度相机的价值很直接:
- 深度图提供真实尺度,帮助无人机判断障碍物距离。
- RGB-D 数据能增强 ORB-SLAM3 在室内定位和建图中的稳定性。
- 硬件 D2C 能降低上位机做深度和彩色图对齐的算力压力。
- IMU 数据可补充短时运动信息,缓解纯视觉在弱纹理、快速运动时的退化。
本地配置中还能看到更工程化的细节:Gemini 2 的配置分辨率为 640 x 480,帧率为 30 FPS,深度比例因子为 1000,双目基线约 0.0745m,ORB 特征数量设置为 1250。这些参数决定了系统对近距离障碍、特征点密度和运行负载的平衡。
# Gemini 2 在 ORB-SLAM3 中的关键配置摘录
Camera.width: 640
Camera.height: 480
Camera.fps: 30
RGBD.DepthMapFactor: 1000.0
Stereo.b: 0.0745
ORBextractor.nFeatures: 1250
这段配置说明了两个核心事实:第一,系统不是只用二维图像做定位,而是把深度图也纳入了 RGB-D SLAM;第二,深度值需要通过 DepthMapFactor 转换为真实尺度,后续规划器才能把“像素里的距离”变成“无人机可以避开的空间距离”。
计算平台选用 Jetson Orin Nano,配合 Ubuntu 20.04、ROS 与 Docker 容器化部署。这样做的好处是:SLAM、规划、相机驱动和飞控桥接可以被拆成独立运行环境,减少依赖冲突,也方便在仿真和实机之间迁移。
技术解说:从视觉定位到飞控执行
这部分是全文最关键的技术主线。建议按四步理解:传感器如何接入、SLAM 如何给出位姿、规划器如何生成轨迹、状态机如何把轨迹交给飞控。
1. 传感器接入:先把 Gemini 2 数据变成 ROS 世界里的标准输入
Gemini 2 的 RGB、深度和 IMU 数据会被映射成 ORB-SLAM3 可以订阅的话题。项目中的启动配置体现了这个关系:
<node name="orb_slam3" pkg="orb_slam3_ros" type="ros_rgbd_inertial">
<remap from="/camera/rgb/image_raw" to="/camera_orb/color/image_raw"/>
<remap from="/camera/depth_registered/image_raw" to="/camera_orb/depth/image_raw"/>
<remap from="/imu" to="/camera_orb/gyro_accel/sample"/>
<param name="voc_file" value="$(find orb_slam3_ros)/orb_slam3/Vocabulary/ORBvoc.txt.bin"/>
<param name="settings_file" value="$(find orb_slam3_ros)/config/RGB-D-Inertial/gemini2.yaml"/>
<param name="world_frame_id" value="odom"/>
<param name="cam_frame_id" value="camera"/>
<param name="imu_frame_id" value="imu"/>
</node>
这里的重点不是 XML 本身,而是它表达的工程关系:相机驱动负责产生数据,ORB-SLAM3 只关心“彩色图、深度图、IMU 和标定文件是否一致”。只要话题和坐标系对齐,SLAM 模块就可以相对独立地运行。
2. 无 GPS 定位:真正难在“连续可信”
室内无人机不能依赖卫星定位,只能从传感器中推断自己的位置。项目采用 ORB-SLAM3 作为核心定位建图方案,它通过图像中的 ORB 特征点建立帧间匹配,再结合局部地图、关键帧和后端优化估计相机轨迹。
这里的难点不只是“算出一个位置”,而是要让位置连续可信:
- 弱纹理墙面、重复结构、玻璃/反光表面会让视觉特征不稳定。
- 无人机旋转、加减速和震动会造成图像模糊或 IMU 噪声。
- 室内障碍近,定位小误差会直接影响避障安全距离。
- SLAM 输出的坐标系还需要和飞控、规划器使用的坐标系对齐。
项目中的 tf_listen.py 会监听 odom 到 base_link 的变换,并发布 /odom 以及 MAVROS 可用的里程计输出。这类看似不起眼的桥接层非常关键:规划器和飞控并不直接理解“相机看到的世界”,它们需要稳定、统一、实时的位姿数据。
# 将 SLAM/TF 中的位姿转换成规划器和飞控都能使用的里程计
trans = tf_buffer.lookup_transform("odom", "base_link", rospy.Time.now())
msg = tf_to_odom(trans)
odom_pub.publish(msg)
mavros_odom_pub.publish(msg)
这段逻辑的作用可以理解为“统一坐标语言”:SLAM 估计无人机在哪里,规划器和飞控则需要以 /odom 的形式持续收到这个结果。
3. 局部规划:EGO-Planner 为什么适合机载无人机?
室内巡检的路径规划不能太重。无人机机载算力有限,障碍物又可能很近,如果每次规划都构建复杂的 ESDF 距离场,计算成本会明显上升。
EGO-Planner 的亮点在于:它是基于梯度优化的 ESDF-free 局部路径规划器。简单理解,它不必先构建完整的欧氏距离场,而是围绕轨迹本身进行碰撞约束和光滑性优化,因此计算量更轻,适合部署在 Jetson 这类边缘计算平台上。
在项目代码中,规划器接收 /odom 作为当前状态,读取深度相机/点云形成局部地图,再通过 waypoint 生成轨迹;轨迹服务器进一步发布位置指令,状态机再把规划输出接到 MAVROS/PX4 执行链路里。这样一来,系统可以从“目标点”走向“可飞轨迹”,而不是让飞控盲目直线飞过去。
<!-- EGO-Planner 的实机关键输入:里程计、深度图、相机内参和飞行约束 -->
<arg name="odom_topic" value="/odom"/>
<arg name="camera_pose_topic" value="/pose_cam"/>
<arg name="depth_topic" value="/camera_orb/depth/image_raw"/>
<arg name="cx" value="319.859"/>
<arg name="cy" value="238.440"/>
<arg name="fx" value="512.717"/>
<arg name="fy" value="512.786"/>
<arg name="max_vel" value="1.0"/>
<arg name="max_acc" value="0.5"/>
<arg name="planning_horizon" value="7.5"/>
这几个参数可以分成三类理解:
/odom告诉规划器无人机现在在哪里。
- 深度图和相机内参告诉规划器周围障碍在哪里。
- 最大速度、最大加速度、规划视距告诉规划器“能飞多快、能拐多急、看多远”。
4. 状态机:把算法输出变成真实飞行动作
SLAM 和规划器只是给出“状态”和“轨迹”,真正让无人机按任务走完流程的是状态机。它负责等待关键模块就绪、发送目标点、判断是否到达、超时保护,并在任务结束时调用降落服务。
# 状态机的核心订阅与发布关系
rospy.Subscriber("/odom", Odometry, self.odomCb)
rospy.Subscriber("/planning/exec_state", ExecStatus, self.egoStateCb)
rospy.Subscriber("/planning/pos_cmd_geo", PoseStamped, self.egoPoseCb)
rospy.Subscriber("mavros/state", State, Cb=self.mavrosStateCb)
self.nav_pub = rospy.Publisher("/waypoint_generator/waypoints", Path, queue_size=5)
self.pose_pub = rospy.Publisher("/sm/pose", PoseStamped, queue_size=5)
self.land_client = rospy.ServiceProxy("land", Empty)
这段代码展示了系统的“中枢神经”:它一边听 SLAM 的 /odom,一边听 EGO-Planner 的规划状态和位置指令,同时把 waypoint 发给规划器,把最终姿态目标发给飞控桥接层。
# 真实任务中的等待顺序:先确认飞控、规划器、定位都在线
rospy.wait_for_service("land")
while sm.ego_state != ExecStatus.EXEC_STATUS_WAIT_TARGET:
rospy.sleep(2.0)
while sm.waitOdomUpdate():
pass
while sm.state.mode != "OFFBOARD":
rospy.sleep(2.0)
这段等待逻辑解释了为什么实机系统不能只关心算法:飞控没有连接、规划器没有就绪、SLAM 没有输出位姿、PX4 没有进入 OFFBOARD,任何一步缺失都会让无人机无法安全自主飞行。
5. 控制桥接:MAVROS 如何把 ROS 指令交给 PX4?
状态机最终发布的是 /sm/pose,但 PX4 飞控需要的是 MAVROS 的本地位置 setpoint。sm2px4.py 的作用就是做这层转换,并持续发布目标位姿:
rospy.Subscriber("/sm/pose", PoseStamped, Cb=self.poseCb)
self.pos_pub = rospy.Publisher(
"mavros/setpoint_position/local",
PoseStamped,
queue_size=10
)
def timerCb(self, event=None):
self.pos_pub.publish(self.pose)
这也解释了为什么无人机控制常常要“持续发布 setpoint”:飞控需要稳定、连续的目标输入,而不是偶尔收到一个目标点就长期执行。持续发布能让 PX4 在 OFFBOARD 模式下保持受控状态。
实机任务流程:一条巡检路线如何被执行?
测试任务中的巡检过程可以概括如下:

这条路线看起来不复杂,但它覆盖了室内自动巡检的基本能力单元:起飞、悬停、转向感知、定点飞行、目标点巡检、返航和降落。更重要的是,状态机中还设置了到达判断、超时保护、等待里程计、等待规划器等逻辑,这些都是实机飞行不可缺的安全机制。
从代码上看,这条任务路线可以简化为下面的伪代码:
# 起飞到安全高度
gotoTarget(0, 0, 0.25, 0)
# 原地环视,建立更充分的初始观测
gotoTarget(0, 0, 0.25, pi / 2)
gotoTarget(0, 0, 0.25, pi)
gotoTarget(0, 0, 0.25, -pi / 2)
gotoTarget(0, 0, 0.25, 0)
# 可扩展:交给规划器飞向巡检点
# plannertoTarget(0.5, 0, 0.35, 0)
# 返回与降落
gotoTarget(0, 0, 0.25, 0)
land()
这里的重点是“动作序列可编排”:如果后续要增加抄表、拍照、异常检测或更多检查点,不需要推翻底层架构,只需要扩展任务状态机和目标点列表。
三维重建:巡检数据不只“看过”,还要“留下空间”
传统巡检通常输出照片或视频,但这些数据有一个问题:它们被固定在拍摄视角里。要复查一个管线背面、设备侧面或空间结构关系时,视频并不方便。
项目将三维重建作为巡检结果的一部分,这一步的意义在于把无人机采集到的多视角信息组织成空间资产。其基本链路可以理解为:

NeRF 与 3D Gaussian Splatting(3D GS)方向代表了三维重建的一种新趋势:不只重建几何轮廓,还能更好表达纹理、光照和复杂材质。对于巡检而言,这意味着未来有机会把“视频留档”升级为“可漫游现场”,让管理人员在办公室里复看设备空间状态。
典型应用场景
- 电力/变电站:巡查柜体、仪表、热源、线路通道,降低人员进入高风险区域的频次

- 水务/泵站/管廊:对水泵、阀门、管道接口进行周期性图像采集和状态复盘

- 燃气/管道设施:辅助巡查阀组、储气设施和管线区域,未来可扩展气体泄漏传感

- 厂房/仓储/机房:在无 GPS 室内空间按固定点位巡检,输出视频、轨迹和三维场景

项目的关键价值
■ 从人工巡检到自动执行
无人机可以按预设路线完成重复任务,减少人工巡检的时间成本和安全风险。
■ 从单次拍摄到数字化留档
图像、视频、位姿、地图点、三维重建结果都可以成为后续分析资料,而不仅是一次性查看。
■ 从室外 GPS 依赖到室内自主定位
通过深度视觉和 SLAM,系统把无人机巡检推进到 GPS 缺失的室内空间。
■ 从算法验证到实机系统
Isaac Sim 仿真用于降低试错成本,真实无人机验证则说明项目已经跨过了“只在仿真里能跑”的阶段。
仍然值得继续攻克的难点
- 退化环境适应性:弱纹理、重复结构、反光材料仍会影响视觉 SLAM。
- 动态障碍物避让:室内人员、移动设备会增加规划不确定性。
- 续航与载荷平衡:小体积无人机要同时搭载计算平台、深度相机和电池,能耗压力明显。
- 巡检目标识别:拍照录像只是第一步,后续还可以加入仪表读数、异常检测、缺陷识别。
- 长期重复巡检:要真正落地,需要更强的路线记忆、地图更新和跨天稳定复现能力。
结语:真正有价值的不是“飞起来”,而是“飞得有目的”
这个项目最吸引人的地方,不是单独用了某个先进算法或某块高性能硬件,而是把室内巡检所需的关键能力串成了一个可运行系统:Gemini 2 负责看见空间,ORB-SLAM3 负责理解位置,EGO-Planner 负责规划安全路线,CUAV V5+/PX4 负责稳定执行,Jetson Orin Nano 则把这些能力放到无人机本体上。
当无人机可以在没有 GPS 的室内空间自主定位、按点巡检、记录现场并重建三维场景时,它就不再只是一个飞行摄像头,而是基础设施数字化管理中的移动感知节点。
这也是“无人机自动化巡检监测”的核心意义:让复杂空间被持续、低成本、可复查地看见。