无人机在室外靠GPS放飞自我,一进室内就彻底成“盲鸟”——这应该是很多玩PX4的朋友最头疼的问题。我自己调室内无人机踩了不少坑,最后稳定下来的方案就是用Intel RealSense T265做视觉惯性里程计(VIO),配合ROS把位姿喂给PX4飞控,让EKF2做状态估计。这篇直接把硬件连接、软件配置、PX4参数、调试避坑一次讲清楚,适合刚接触ROS和PX4、想在室内跑定点甚至自主飞行方向的新手。文中所有参数和步骤都是我实际跑通过的方案,你可以直接照着抄。
1. 方案选型:室内定位为什么选T265
1.1 室内定位的常见思路对比
室内没有GPS,无人机要稳住位置,必须自己想办法知道“我在哪”。目前主流方案无非这么几种:光流+测距模块、UWB超宽带、动捕系统,以及视觉SLAM/VIO。我一个个说下实际体验。
光流模块(比如PX4FLOW这类)的原理是拍摄地面纹理,通过图像运动推算水平位移,再配合激光测距或者气压计获得高度。这套方案胜在便宜、轻量、功耗低,小四轴上很常见。缺点也非常明显:它本质上是在“数像素”,对地面纹理要求高,地砖、地毯、纯色地面都会导致数据跳变,而且光流测的是相对位移,时间一长必然累积漂移,很难长时间悬停在一个点上。
UWB是近几年很火的室内定位方案,靠布置三四个基站解算标签位置,精度能做到10~30厘米左右。它不受光照影响,也不怕地面没纹理。但代价是你要先布置基站、标定基站坐标,移动性差,换一个场地就得重新布置,而且UWB输出的位置更新频率一般只有20~50Hz,快速机动时容易跟不上。
动捕系统(Vicon、OptiTrack等)精度确实最高,亚毫米级,但价格也最“劝退”,一套少说十几万,还要固定场地、贴反光点。除非是实验室做科研写论文,一般个人玩家和多数工程场景根本用不起。
剩下就是T265这种视觉惯性方案。它把双鱼眼相机和IMU集成在一个小盒子里,内部用VIO算法实时算出6自由度位姿,不需要外部基站,不依赖GPS,插上USB就能输出位置和姿态。因为双鱼眼视场角大,IMU频率又高,短时间内的定位精度和稳定性相当能打,特别适合无人机这种运动快的载体。T265虽然官方已经停产,但二手市场货源充足,资料和教程遍地都是,拿来入门视觉定位再合适不过。
1.2 T265的VIO定位原理
T265能定位,靠的是“视觉+惯性”融合。两个鱼眼镜头以190°左右的视场角同时采集图像,覆盖周围大范围环境;内置的IMU则以约200Hz频率测量加速度和角速度。视觉提供“我相对之前的位置移动了多少”,IMU提供“我在加速、在转”,两者互补,再用内部的融合算法输出一个完整的6自由度位姿,即XYZ位置加四元数姿态。
从原理上理解,视觉部分擅长感知环境的“不变特征”,能纠正IMU短时间积分带来的漂移;而IMU反应快,能在图像曝光和曝光之间提供高频运动预测,尤其在快速转弯、剧烈加减速时,纯视觉容易丢帧,IMU能顶住。这就像你闭着眼走直线容易偏离,但睁着眼一直盯着路边路灯就能走得很准。T265就是让“眼睛”和“内耳”互相校正。
它输出的位姿是相对于启动时刻建立的“局部世界坐标系”,在ROS里通常体现为camera_odom_frame到camera_pose_frame的变换。换句话说,每次上电开机,原点就是当时设备所在的位置和朝向,这决定了你每次起飞前要让无人机和T265保持静止,让它把“零点”记住。这个特性实际用起来很重要,下文调试部分我会再强调。
T265另一个优点是直接输出处理好的位姿,不需要你在机载电脑上再跑ORB-SLAM之类的算法。市面上很多无人机视觉方案要装一个沉重的AR标签板,或者用树莓派跑VINS-Fusion,算力压力大,延迟还高。T265等于把算法硬件化、集成化了,对入门者极其友好。
1.3 整体架构与数据流
这套方案的完整数据链路是这样的:T265通过USB3.0连接机载电脑(通常是jetson nano、树莓派4B或笔记本),realsense-ros驱动把VIO位姿和IMU数据发布到ROS话题;我们再写一个转发节点,把位姿转成MAVROS的vision_pose/pose话题;MAVROS包装成MAVLink消息发给PX4飞控;最后PX4的EKF2估计器把视觉位姿和飞控自身的IMU、气压计融合,输出稳定位置,供Position模式、Offboard模式使用。
看到这里你可能会问:T265已经自带IMU了,能不能把它的IMU数据也一起发给PX4?我的经验是不要这样干。PX4的EKF2有自己的陀螺仪和加速度计,你只需要喂给它外部视觉位姿就够了。把T265的IMU再灌进去,等于让飞控同时处理两套IMU,标定不一致时反而容易把状态估计搞乱。数据流里要理清的,只有“视觉位姿”这一路。
整体架构中还有个容易绕晕的概念——坐标系。ROS默认坐标系是ENU(X东、Y北、Z上),而PX4的机体坐标系是NED(X北、Y东、Z下)。这套方案里MAVROS会在底层自动完成ENU到NED的转换,所以你只需要保证ROS侧给MAVROS的数据是ENU下的准确位姿就行,千万别自己在转发节点里手动“转NED”,否则两边各转一次,结果必定歪掉。
2. 环境搭建:Ubuntu、ROS和T265驱动
2.1 系统与ROS版本选择
这个方案最省心的是Ubuntu 20.04 + ROS Noetic组合,因为realsense-ros和mavros都有非常成熟的预编译包,不用自己编译源码,能少踩一半的坑。如果你手头是Ubuntu 18.04,那用ROS Melodic也可以,但建议驱动相关依赖尽量跟着发行版的默认版本走,不要混装。
至于PX4固件,建议v1.11以上,本文的EKF2参数说明也是基于v1.11到v1.13这个区间的。老一点的v1.8、v1.9不是不能用,但参数位定义和现在有不少差异,照抄下面的值可能会出现“看起来设置了,但EKF根本没融合视觉数据”的情况。如果你用的是机载电脑和飞控分离的结构,飞控端固件建议直接用QGroundControl升级到最新稳定版,再用USB连上机载电脑做参数配置。
ROS本身安装没什么好说的,桌面完整版就行:
sudo apt update sudo apt install ros-noetic-desktop-full如果你刚配Ubuntu环境,嫌手动初始化rosdep麻烦,现在也有鱼香ROS这种一键安装脚本,对新手确实省时间。不过我个人建议至少手动跑一遍官方流程,了解工作空间、source环境这些基础概念,后面排查问题会顺手很多。
2.2 安装并验证T265驱动
T265的官方驱动是librealsense,ROS封装叫realsense-ros。Ubuntu 20.04 + Noetic直接用apt装预编译包最方便:
sudo apt install ros-noetic-realsense2-cameraT265需要USB3.0带宽,一定要插蓝色USB口,而且别用太长的延长线。USB3.0对线材质量很敏感,我之前用一根3米普通延长线,T265频繁掉线,换成带供电的主动延长线之后就稳定了。这个坑很隐蔽,排查半天才发现是供电问题。
装好后先插上T265,用工具确认设备枚举是否正常:
rs-enumerate-devices如果能看到设备并显示两个鱼眼镜头型号,说明驱动识别没问题。接下来启动ROS驱动验证数据:
roslaunch realsense2_camera rs_camera.launchT265默认会发布/camera/odom/sample(nav_msgs/Odometry,也就是VIO位姿)和/camera/imu(sensor_msgs/Imu),同时也会发布左右鱼眼图像话题。用下面命令确认:
rostopic list | grep camera rostopic echo /camera/odom/sampleecho位姿时,拿着T265在手里慢慢平移,观察position数值跟着变化,说明VIO已经正常工作了。这一部一定要在装机前完成,否则后面装到无人机上再调试,很难分清是硬件问题还是软件问题。
如果你发现rostopic里没有IMU话题或者没有pose话题,多半是launch里没开对应开关。我建议自己写一个launch文件,显式开启pose、gyro、accel:
<launch> <arg name="unite_imu_method" default="1"/> <include file="$(find realsense2_camera)/launch/rs_camera.launch"> <arg name="enable_pose" value="true"/> <arg name="enable_gyro" value="true"/> <arg name="enable_accel" value="true"/> <arg name="unite_imu_method" value="1"/> </include> </launch>unite_imu_method=1的作用是把gyro和accel合并到一个IMU话题里,避免两个话题在时间戳上不同步。T265的IMU数据虽然我们不直接喂给PX4,但realsense-ros内部融合位姿时可能会用到IMU话题的发布状态,所以建议一并开着。
2.3 驱动输出的话题类型和坐标系
这里稍微细说一下T265输出的数据形态。realsense-ros驱动主要发布两类位姿相关话题:一类是/camera/odom/sample,类型是nav_msgs/Odometry,包含位置、姿态四元数、以及对应的协方差;另一类可能是/camera/pose/sample,类型是geometry_msgs/PoseStamped。不同版本驱动略有差别,所以你在抄网上的转发脚本前,一定要先用rostopic info看准当前驱动实际发布的是哪个话题。
坐标系方面,T265的odometry话题通常以odom作为world frame,camera_pose_frame作为body frame。驱动还会发布odom到camera_pose_frame的TF变换。这里有个常见的理解误区:T265输出的“odom”并不是PX4里的odom,我们只需要把它的位置和姿态取出来,作为“外部视觉观测”送给PX4的EKF,不需要去和飞控的TF树强行对齐。等于说,T265是一个“能告诉你相对零点移动了多少”的传感器,而不是飞控导航系统的一部分。
要特别提醒的是,T265的两个鱼眼镜头怕强光,尤其是太阳直射。室内使用时尽量别让镜头正对窗外的强光或者大功率射灯,过曝会导致特征点丢失,位姿瞬间漂移甚至卡住。无人机在室内飞行时,最好给T265配一个遮阳罩,能显著提高稳定性。
3. 让PX4吃上T265数据:MAVROS桥接与参数配置
3.1 安装并启动MAVROS
MAVROS是ROS和PX4之间的官方桥接通道。在Noetic下直接apt安装即可:
sudo apt install ros-noetic-mavros ros-noetic-mavros-extras装完MAVROS后,按官方流程还要下载GeographicLib的数据集,主要用于磁偏角补偿,室内定位不依赖它,但如果以后要切换到室外GPS模式,建议还是补上,免得以后报一堆奇怪的警告。
连接飞控和机载电脑,一般有两种方式。如果你用的是飞控USB口直接连机载电脑,常见命令是:
roslaunch mavros px4.launch fcu_url:="/dev/ttyACM0:921600"如果飞控通过数传模块连机载电脑,fcu_url就要改成类似/dev/ttyUSB0:57600的串口配置,具体波特率看你的数传型号。启动后先验证MAVROS是否收到飞控数据:
rostopic echo /mavros/state如果能看到connected: true,说明MAVROS和飞控通信正常。飞控这边建议先在QGroundControl里把参数配置好,再启动MAVROS,顺序反了容易让EKF在启动早期错过视觉数据。
3.2 写一个转发节点,把T265位姿送入MAVROS
MAVROS提供/mavros/vision_pose/pose话题,专门接收外部视觉位姿,类型是geometry_msgs/PoseStamped。我们只需要写一个简单节点,把/camera/odom/sample里的pose转发过去即可。脚本我用python写,Noetic下面默认就是python3,兼容性没问题。
#!/usr/bin/env python3 import rospy from nav_msgs.msg import Odometry from geometry_msgs.msg import PoseStamped pub = rospy.Publisher('/mavros/vision_pose/pose', PoseStamped, queue_size=10) def callback(msg): pose = PoseStamped() pose.header.stamp = msg.header.stamp pose.header.frame_id = "odom" pose.pose = msg.pose.pose pub.publish(pose) if __name__ == "__main__": rospy.init_node("t265_to_mavros") rospy.Subscriber("/camera/odom/sample", Odometry, callback) rospy.spin()把脚本保存成t265_to_mavros.py,加上执行权限,然后rosrun运行。如果驱动发布的是/camera/pose/sample,就把Subscriber的类型改成PoseStamped,回调里直接取msg.pose即可。
关于frame_id,MAVROS其实不太在意这个字段,但我习惯填“odom”,避免后续用Rviz查看时TF树对不上。
启动顺序建议是:先启动T265驱动,再启动这个转发节点,最后启动MAVROS。这样MAVROS一启动就能立刻收到vision pose,EKF不会因为等待数据而进入保守模式。
3.3 PX4参数配置详解
这套方案的灵魂在这一节。PX4使用EKF2做状态估计,要让飞控认可用T265的视觉数据,需要配置几个关键参数。在QGroundControl的“参数”页面直接搜索修改即可。
我直接给一套我实测可用的参数组合,适用于PX4 v1.11至v1.13,T265作为室内唯一外部定位源:
| 参数名 | 推荐值 | 作用说明 |
|---|---|---|
| SYS_MC_EST_GROUP | 1 | 使用EKF2估计器,而不是旧的Q估计器 |
| EKF2_AID_MASK | 12 | 按位启用视觉位置融合和视觉偏航融合 |
| EKF2_EV_CTRL | 1 | 允许融合外部视觉水平位置与高度 |
| EKF2_HGT_MODE | 3 | 高度源选择视觉高度,室内气压计不稳时更可靠 |
| EKF2_EV_DELAY | 0 | 视觉数据延迟补偿,默认0即可 |
| EKF2_EV_POS_X | 0 | 视觉传感器相对飞控坐标系的前向偏移 |
| EKF2_EV_POS_Y | 0 | 视觉传感器相对飞控坐标系的右向偏移 |
| EKF2_EV_POS_Z | 0 | 视觉传感器相对飞控坐标系的向下偏移 |
EKF2_AID_MASK这个参数特别容易抄错。它是个位掩码,每个bit代表一类传感器融合选项,按官方位定义,bit2对应视觉位置,bit3对应视觉偏航,所以4+8=12。如果你用的PX4版本较老,或者QGC界面上显示的是可勾选的复选框,建议直接在界面上勾选“vision position fusion”和“vision yaw fusion”,比硬记数字更不容易错。网上很多教程写EKF2_AID_MASK=1,那是另一种位定义或者老版本的习惯,直接照抄容易翻车。
EKF2_EV_POS_X/Y/Z三个偏移参数,指的是T265安装位置相对于飞控机体坐标系原点的偏移。如果T265装在机架中心上方,三个值都可以保持0。如果相机装在机头前方、离飞控中心比较远,就必须量好距离填进去,否则EKF做坐标旋转时会产生杠杆臂误差,飞机在激烈机动时会明显感觉到位置估计“晃”。
高度源这里多说一句。EKF2_HGT_MODE=3表示用视觉高度作为主要高度来源,因为室内气压计会受空调气流、飞机旋翼气流干扰,数值经常飘。但视觉高度也有自身问题,VIO对Z轴的估计在垂直方向快速运动时偶尔会漂。我的经验是:如果悬停高度稳定,就用视觉高度;如果发现高度慢慢上浮下沉,可以改回EKF2_HGT_MODE=0(气压计高度),比较两种模式的实际表现再定。无人机试飞本来就是变量控制,一次只改一个参数,记住“改动最小原则”。
另外还有一个重要提示:室内没有GPS信号时,飞控可能会因为丢失GPS而报警甚至切换飞行模式。建议在QGC里确认GPS相关融合选项没有被强制启用,最好把EKF2_AID_MASK中的GPS位明确关闭,避免GPS偶发性飘星把位置估计带跑。有些版本有SYS_USE_GPS参数,室内纯视觉场景直接置0比较稳妥。
4. 实机调试与常见问题排查
4.1 上机前的地面测试和标准起飞流程
参数配好,别急着直接解锁起飞。我建议按下面流程走一遍,能提前发现80%的问题。
第一步,把T265固定到机架上。安装位置尽量靠近机架中心,镜头朝前或略带下压角度都可以。固定要刚性,不能有明显晃动,最好用3M胶加扎带双重固定。T265对震动其实还算耐受,但你的机架如果本身震动大,建议加一层减震泡棉。
第二步,上电后保持无人机静止不动5到10秒,让T265建立原点,同时让PX4的EKF收敛。此时用rostopic echo看一眼/camera/odom/sample,确认位置输出基本为零且有微小噪声,而不是持续朝一个方向漂移。如果一上电就漂,多半是T265初始化时设备本身还在移动,或者场景纹理太差。
第三步,观察QGroundControl里的EKF状态。点开MAVLink Inspector,查看VISION_POSITION_ESTIMATE消息是否以固定频率进来。也可以看EKF的innovation值,如果视觉位置数据的innovation一直为零,说明EKF压根没有使用视觉数据,优先检查EKF2_AID_MASK。
第四步,解锁并切到Position模式,缓慢推油让飞机离地半米左右。第一次试飞强烈建议系上安全绳或者装好螺旋桨保护罩,室内无GPS,新方案第一次放飞机大概率会出现位置抖动或突然漂移,安全措施不能省。如果飞机在Position模式下能悬停住但缓慢漂移,通常不是大问题,多数是T265原点建立时的轻微误差,可以在起飞后重新降落、再次上电重新建零点来消除。
4.2 典型问题速查表
下面这些坑是我自己踩过、以及帮别人排查时见过最多的,整理成表格,方便你在现场快速定位。
| 现象 | 可能原因 | 排查与解决 |
|---|---|---|
| rostopic里看不到/camera/odom/sample | T265供电不足或USB接触不良;驱动未开启pose输出 | 换USB3.0口、换线;检查rs-enumerate-devices;launch里确认enable_pose=true |
| T265运行一段时间后掉线 | USB线材质量差、供电不稳、设备过热 | 换带供电的主动延长线;避免阳光直晒;给T265加散热片或小风扇 |
| MAVROS没有收到vision_pose | 转发节点没启动;话题名或类型不匹配;rosrun前没source环境 | 用rostopic echo /mavros/vision_pose/pose验证;确认订阅的话题实际存在 |
| QGC里没有VISION_POSITION_ESTIMATE | MAVROS和飞控通信异常;转发节点没有成功publish | 先验证/mavros/state是否connected;再echo vision_pose话题 |
| EKF innovation里看不到视觉融合 | EKF2_AID_MASK配置错误;EKF2_EV_CTRL被设为关闭;GPS位干扰 | 按位检查AID_MASK;QGC参数页面勾选vision position和vision yaw |
| 悬停时飞机往一个方向缓慢漂移 | 起飞前T265建立原点时飞机有移动;场景纹理稀疏 | 重新启动T265并保持无人机静止10秒以上;在纹理丰富的区域起飞 |
| 快速机动时位置估计跳变 | T265安装不牢固、震动过大;EKF2_EV_DELAY设置不合理;安装偏移参数没填 | 加固摄像头;填写EKF2_EV_POS_X/Y/Z偏移;尝试调节EKF2_EV_DELAY |
| 高度缓慢上浮或下沉 | 视觉高度漂移;室内气压场不稳 | 切换EKF2_HGT_MODE到0或3对比测试;检查T265是否被旋翼气流遮挡 |
4.3 飞行安全与后续扩展建议
室内飞行最大的风险是“定位突然失效”。T265本质上是一个相对定位传感器,它不提供绝对坐标,一旦发生视觉丢失,位姿就只能靠内部IMU短时间猜,猜久了必然漂。所以我在实飞时永远保持手里捏着遥控器,切回Stabilized或者手动模式比什么都可靠。不要过度信任任何视觉定位系统,这个行业里“飞得好好的突然抽风”才是常态。
如果想把这套系统往自主方向扩展,几个方向你可以接着研究。第一,加一个激光雷达或者UWB做绝对定位的定期校正,VIO做高频短时估计,两者结合能长时间稳定。第二,把T265位姿和PX4的Offboard模式打通,后续可以做路径跟踪、自动巡检。第三,如果无人机载重要求低,T265这一套已经足够轻量,机载电脑选jetson nano甚至树莓派4B都能跑得动,整机重量能控制在1.5公斤以内。
最后再分享一个我个人的心得:T265这套方案最大的价值,是让新手绕过了“从零手写VIO算法”这个高门槛,直接用一套成熟硬件把视觉定位全链路跑通。调室内无人机,前几次失败基本都是坐标系、参数位、话题名这类细节问题。只要按“先地面验证驱动、再桥接话题、再配EKF参数、最后小高度试飞”的次序走,大多数坑都能提前规避。等你把这条链路跑顺了,再回头读PX4的EKF2源码、研究协方差和innovation的物理意义,会有一种“原来如此”的通透感。