1. 方案选型:为什么室内定位我选了T265
1.1 T265到底是个什么硬件
第一次接触Intel RealSense T265的人,很容易把它误当成普通的深度相机。它确实属于Realsense系列,但跟D435那种测深度的摄像头完全不同,T265的任务只有一个:算清楚“自己”在哪、朝哪个方向运动。它正面带了两颗鱼眼镜头,内部还有一颗IMU,也就是惯性测量单元,这个组合就是典型的视觉惯性里程计,英文缩写VIO。
T265内部专门有一颗Movidius Myriad X VPU,这颗芯片在相机里直接完成图像特征提取、特征跟踪和位姿解算,不需要额外接电脑去跑算法。换句话说,它输出的不是“画面”,而是已经算好的6自由度位姿数据:X轴、Y轴、Z轴坐标,以及翻滚、俯仰、偏航角。这个数据通过USB线传给上位机,上位机再转给飞控,整个过程里无人机本体的PX4飞控不需要自己改算法。
很多新手会问:既然T265自己就能算位姿,那为什么还要ROS和PX4参与?这里需要分清两个概念:T265算的是相对起点的一个轨迹,它是一套“感知”系统;而无人机要稳定悬停,还需要飞控把姿态环、速度环、位置环串起来,用这个位姿去做控制。ROS在这里的角色是数据搬运工和坐标系翻译官,PX4的角色是把视觉位姿和机载IMU、气压计、磁力计统一融合,最终输出一个可用的飞行控制状态。三者的分工,实际上就是感知、通信、决策控制三层结构。
1.2 几条室内定位路线的对比
室内定位的方案并不少,我在T265之前也折腾过光流、UWB、动作捕捉,各有各的坑。放在一张表里对比会更直观:
| 方案 | 典型硬件 | 定位精度 | 成本范围 | 明显限制 |
|---|---|---|---|---|
| 光流+测距 | Pixhawk + 光流传感器 | 厘米级短时,长时间会漂 | 几百元 | 依赖地面纹理,灯光暗或者地面纯色就失效 |
| UWB | Tag/Anchor几件套 | 10-30厘米 | 数千元 | 要布置基站,需要开坐标系标定,运动速度高时丢包 |
| 动捕 | OptiTrack/Vicon | 毫米级 | 数万起步 | 价格劝退,还需要空旷场地和反光球 |
| 视觉惯性里程计 | T265 / 双目相机+IMU | 厘米到分米级 | 一千到几千 | 不能对着纯白墙或强反光面飞,长时间有偏航漂移 |
从平衡角度看,T265是性价比最高的室内定位方案。它不需要外部基站,不依赖GPS,通网通电就能自建坐标系,在光线正常的室内走廊、实验室、办公室环境下都很稳定。虽然动捕的精度可以吊打它,但动捕一套下来够买好几台T265。
除了硬件成本,我更喜欢T265的一点是它把视觉里程计里最头疼的“多传感器时间同步”问题从源头简化了。鱼眼相机和IMU数据在内部就被同步起来,不像自己拿双目相机配IMU还要做时间戳对齐,哪怕不精通卡尔曼滤波也能把整套系统跑起来。
1.3 视觉惯性里程计的原理,尽量说人话
VIO的原理用一句话概括:摄像头看环境变化,IMU感知自身加速度和角速度,两者互相“纠错”,最后输出一个可信的位姿。
具体展开一点。鱼眼镜头拍摄到的图像中有大量角点、边缘、纹理特征,算法会提取这些特征点,并在连续两帧里匹配它们的位置变化。如果画面中特征点向右移动,相机大概率是向左转了或者向右平移了。但单个相机的位姿解算存在尺度模糊和快速运动模糊,这时候IMU就派上用场。IMU能给出比较精准的加速度和角速度变化,短时间内数值可靠,但长时间积分会漂移到离谱。图像和IMU一结合,相当于一个靠直觉短时记忆,一个靠眼睛做长期校正,EKF或者非线性优化再把两者缝合起来,就得到了一条相对稳定、带尺度信息的运动轨迹。
T265内部的Myriad X芯片干的就是这件事。需要注意的是,T265输出的是“相对起始点”的位姿,没有绝对意义。每次上电,它的起点就是坐标系原点。所以无人机飞控端做视觉定位融合时,也只是把视觉里程计当作位置传感器来用,而不是当作绝对坐标来源。这个特性决定了后续参数配置里有一些辅助开关必须关掉,否则飞控会把视觉位姿和GPS位置混在一起算,直接打架。
2. 准备工作:ROS装好、固件刷好、驱动配好
2.1 硬件清单和连接拓扑
我手里的这套配置是这样的,大家可以参考:
- 飞控:Pixhawk 6C,运行PX4 v1.13或者更新的稳定固件
- 上位机:树莓派4B,4G内存版本,跑Ubuntu 20.04和ROS Noetic
- 定位传感器:Intel RealSense T265
- 数传/地面站:Zed-F9P RTK只用于户外测试,室内一律拔掉GPS模块
- 机架:500mm四轴,电机选择在300-500g拉力范围,桨带保护罩
连接方式非常简单:T265通过USB3.0线接到树莓派,树莓派通过USB或者串口和Pixhawk连接。在实际调试时我是用USB连接飞控的,方便省事,量产再改串口。需要提醒的是,树莓派的USB供电能力不算强,T265峰值电流可能到800毫安,建议用带独立供电的Y型USB线或加一个USB hub供电,不然容易出现USB设备随机掉线的问题。
这里有一个新手经常踩的坑:T265开机后需要一定时间让内部算法收敛初始的零偏,直接上电马上启动飞行会导致前一两分钟的数据有较大漂移。我的习惯是给无人机通电后,先让T265和飞控静置至少一分钟,确保系统完成初始化。
2.2 安装ROS,尽量用省心办法
ROS的安装对新手来说最大痛点是依赖关系太乱。官方教程是一步一步添加软件源、设置密钥、安装一大堆包,光环境变量就要配好几次。国内的话,我推荐用鱼香ROS的一键安装脚本,它能自动配置源、装ROS本体、装常用依赖,省下大量折腾时间。它的本质是帮你把官方步骤封装成脚本,里面涉及的关键内容一样都不少,只是更省心。
如果你坚持手动装ROS Noetic,核心步骤也无非是这几条:
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list' sudo apt install curl curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add - sudo apt update sudo apt install ros-noetic-desktop-full echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc source ~/.bashrc装完之后还要把rosdep、catkin_tools这些常用工具补齐。说实话,除非你在编译包时遇到奇奇怪怪的依赖问题,手动装和脚本装效果基本一样。我自己是在台式机上手动装、在树莓派上用脚本装,这样两边环境还能互相验证。
2.3 安装realsense-ros驱动
T265要跑在ROS里,必须要装Intell Realsense官方提供的SDK和ROS驱动。核心是三个包:librealsense2、realsense2-camera包和realsense-ros包。
安装SDK最省事的是用官方发布源:
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-key F6E65AC044F831AC80A06379C8B3A5A0276F7FBB sudo add-apt-repository "deb https://librealsense.intel.com/Debian/apt-repo $(lsb_release -cs) main" -u sudo apt-get install librealsense2-dkms sudo apt-get install librealsense2-utils装好之后先插上T265,执行rs-enumerate-devices,能看到设备信息就说明驱动OK。然后编译ROS功能包,这一步需要先用catkin_make或者catkin build把realsense-ros源码编进工作空间里。
mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone https://github.com/IntelRealSense/realsense-ros.git cd ~/catkin_ws catkin_make如果编译过程中缺了ddynamic-reconfigure之类的依赖,直接sudo apt install ros-noetic-ddynamic-reconfigure装掉就行。在树莓派上首次编译时间会比较长,建议挂个代理或者把Swap空间调大一点,不然内存不够会直接OOM。
2.4 PX4开发环境准备
PX4固件的编译配置,我这里不展开全部内容,只说跟本方案相关的部分。如果你只是需要刷写固件,最简单的办法是用QGroundControl地面站。打开QGC,选择飞控型号,固件版本选择稳定版PX4,会提示是否保留当前参数。刷完固件以后,进入参数界面检查SYS_MC_EST_GROUP,确认当前估计器是EKF2,因为稍后要用的视觉融合参数都挂在EKF2下面。
如果你要改PX4源码做二次开发,那就需要Linux上的PX4工具链,一般是用官方脚本安装:
git clone https://github.com/PX4/PX4-Autopilot.git --recursive cd PX4-Autopilot ./Tools/setup/ubuntu.sh --no-nuttx make px4_fmu-v6c_default这里提醒一句:PX4的版本迭代很快,EKF2相关参数在不同版本有增删,下面所有参数值都是基于PX4 v1.13和v1.14常见稳定版本写的。如果刷了新版本,最好对照官方参数文档再确认一遍。
3. 让T265在ROS里正常运行起来
3.1 启动T265节点
在ROS里启动T265最常用的命令是:
roslaunch realsense2_camera rs_t265.launch也可以自己写一个更精简的launch文件,把需要打开的功能都显式声明出来,方便后续调试。我习惯这样写:
<launch> <node pkg="realsense2_camera" type="realsense2_camera" name="rs_t265" output="screen"> <param name="enable_gyro" value="true"/> <param name="enable_accel" value="true"/> <param name="enable_pose" value="true"/> <param name="unite_imu_method" value="linear_interpolation"/> <param name="odom_frame" value="camera_odom_frame"/> <param name="pose_frame" value="camera_pose_frame"/> </node> </launch>unite_imu_method设成linear_interpolation是为了纠正IMU时间戳,把加速度计和陀螺仪的数据统一成同一频率输出,这一步会让后续数据融合稳定很多。odom_frame和pose_frame是T265的坐标系名称,默认就是camera_odom_frame和camera_pose_frame,建议保持默认,减少后续TF配置的混乱。
启动完成后,用rostopic list确认话题存在,重点看这几个:
/camera/odom/sample:视觉里程计数据,类型为nav_msgs/Odometry/camera/imu:IMU数据/camera/pose/sample:6自由度位姿数据/tf:T265内部发布的坐标变换
3.2 确认数据不是一堆零
很多新手遇到的情况是话题存在,但里面全是零,或者数据变化异常。排查第一步先看频率:
rostopic hz /camera/odom/sample正常情况odom频率在30Hz左右,IMU频率在200Hz左右。如果频率在1Hz以下,说明USB带宽不够或者被其他进程占用。如果频率正常但位移始终是零,多半是场景特征太少,把T265对着白墙或者空旷的天空,它也找不到特征匹配,自然会输出零位姿。这时候调整一下无人机朝向,让它对着有纹理的地面、桌椅、海报等明显特征的地方,再观察数据。
数据内容检查也很重要:
rostopic echo /camera/odom/sample --noarr主要看pose.pose.position和pose.pose.orientation两个字段。手动拿起无人机,上下左右移动,数值应该跟着变化。注意:这里“拿起无人机”时,T265看到的要在静止环境,不要尝试把相机和场景一起转动,这样会让VIO失去参照物。
3.3 TF坐标系:别小看这条base_link链条
T265虽然输出位姿,但PX4和MAVROS并不认识camera_pose_frame这个名字。飞控期望的是机体坐标系下的位姿,所以需要告诉ROS:T265的坐标系和飞机机体坐标系是什么关系。
最简单的办法是发一条静态坐标变换。如果T265安装方向和机头一致,而且就在飞机重心附近,那变换矩阵基本接近原点重合:
rosrun tf2_ros static_transform_publisher 0 0 0 0 0 0 1 base_link camera_pose_frame如果T265是装在机头朝前、偏上位置,那就得把偏移量写进去。例如装在机体前方15厘米、上方5厘米,并向右偏2厘米:
rosrun tf2_ros static_transform_publisher 0.15 0.02 0.05 0 0 0 1 base_link camera_pose_frame注意这里的坐标轴顺序和单位,前三位是xyz,单位米,后三位是滚动、俯仰、偏航角,单位弧度。很多人会忽略偏航角,如果T265装歪了,飞到后面就会发现视觉定位的轨迹整体旋转了,怎么调参数都救不回来。
4. 把视觉里程计喂给PX4和EKF2
4.1 数据流为什么要过MAVROS这一层
T265算出的位姿是ROS的nav_msgs/Odometry消息,而PX4飞控不认识这种消息,它只认MAVLink协议。MAVROS的作用就是桥接两边:它把ROS话题转成MAVLink的VISION_POSITION_ESTIMATE消息,再通过串口或USB发给飞控;同时飞控返回的IMU、状态、遥控信号又会变成MAVROS话题。
如果你用的是Pixhawk和树莓派USB直连,MAVROS启动命令是:
roslaunch mavros px4.launch fcu_url:=/dev/ttyACM0:921600如果树莓派用的是串口,那就把fcu_url改成类似/dev/serial0:921600。启动后MAVROS默认会有很多话题,但核心只需关注/mavros/state、/mavros/local_position/odom和/mavros/vision_pose_estimate/odom。
关键一步是让T265的里程计进入MAVROS。我用的方法是写一个launch文件,在MAVROS节点前把topic重映射:
<remap from="/camera/odom/sample" to="/mavros/vision_pose_estimate/odom"/>这样MAVROS收到视觉里程计后,会自动转换坐标系,生成PX4能理解的VISION_POSITION_ESTIMATE并发送飞控。
4.2 PX4参数配置逐项说明
接下来是这篇文章最值钱的部分。打开QGC地面站,进入参数界面,逐项修改:
EKF2_AID_MASK
这个参数用来决定EKF2使用哪些辅助传感器。没有GPS的室内场景,必须关闭GPS融合的位,打开视觉位置、视觉速度、视觉偏航融合。常用值是7,表示视觉位置、视觉速度、视觉偏航三项全部开启。不同的PX4版本里位定义略有区别,但视觉融合对应的位基本在bit0到bit2,设置为7或直接参考固件参数说明设置即可。
EKF2_EV_CTRL
这个参数是较新PX4版本加入的,用于更精细控制视觉数据在EKF2里怎么用。位0是水平视觉位置融合,位1是垂直视觉位置融合,位2是视觉速度融合,位3是视觉偏航融合。如果只做室内定位,我建议设置7,也就是开启水平位置、垂直位置、偏航三项。这里要注意,如果EKF2_EV_CTRL非零,它会覆盖EKF2_AID_MASK里关于视觉的部分,所以两个参数要保持语义一致。
EKF2_HGT_MODE
高度源选择。室内没有GPS,我一般选4,也就是视觉+气压计融合。单独用视觉高度的话,T265的Z方向在小范围悬停时还算稳,但如果整个垮层飞行或者遇到轻微漂移,视觉高度就容易失真。气压计虽然在高动态下有延迟和滞后,但能提供一个长期稳定的基准。视觉+气压计融合,等于用气压计兜底,用视觉细节做修正,实际表现比单用视觉高度舒服很多。
EKF2_GPS_CHECK
既然没有GPS,必须把GPS检查关掉,否则EKF2会因为收不到GPS而疯狂报警,甚至拒绝切换融合模式。设成0,表示不检查GPS状态。
SYS_HAS_GPS
如果硬件上确实没有GPS模块,就把这个参数设成0。这样系统知道机载环境没有GPS,很多依赖GPS的航点和设定就不会报错。
EKF2_IMU_POS_X/Y/Z
这个参数描述飞控IMU相对飞机重心的位置。如果飞控装在飞机正中央,三个值都是0也没问题;如果偏了,把实际偏移量以米为单位填进去。它对于EKF2融合后的动态控制响应有明显影响,不能忽略。
把上面的参数调整完之后,最好重启一下飞控让参数生效。
4.3 验证飞控是否真的收到了视觉数据
参数配好,MAVROS起来,T265也在跑,最怕的是飞控根本没收到视觉数据。我建议用两种方式确认。
第一种是在QGC的MAVLink控制台里输入listener vision_position_estimate,如果能看到一个不断刷新的几十Hz数据流,说明MAVLink消息已经到达飞控。第二种是直接用MAVROS回读,执行:
rostopic echo /mavros/vision_pose_estimate/odom --noarr如果这个topic有稳定数据且数值跟着相机运动变化,说明链路已经通了。
更直观地验证是看QGC右上角的EKF状态。正常情况下会看到“视觉位置”这个辅助源被激活。如果看到“GPS”或者“没有位置源”,说明参数配置还没生效,或者飞控还在寻找可用的传感器。
5. 实操过程:从地面站到空中悬停
5.1 先在地面站里做一次“伪起飞”验证
我强烈建议第一次飞行之前,不要通电推油,而是先把整机放在桌上,拿着飞机在房间里走动,观察QGC里的位置估计是否在实时更新。具体做法是:上电后不解锁,在QGC的飞行界面看到机头朝向、位置轨迹随走动而移动,这就说明整套数据链路已经打通。如果不动,那就别飞,先排查数据问题,不然解锁之后飞控会立刻发现定位丢失,直接闪避或者切自稳模式。
这一步还有一个容易忽略的细节:房间里的光线和纹理。T265是视觉设备,对光照敏感。开着明亮白炽灯或自然采光的房间效果最好,环境光太暗或房间大白墙密布会让特征点数量骤减。我有一条经验:把手放在T265视野前晃一晃,如果QGC里的位置数值能随之变化,说明环境特征足够。
5.2 用自稳模式试悬停
确认数据没问题后,不要一上来就切位置模式。先用自稳模式(Stabilized)解锁,轻轻推油让飞机离地10-20厘米悬停。这一步的目的是验证视觉定位在真实电机振动环境下是否依然稳定。T265虽然内部有减震设计,但电机高频振动会给IMU带来额外噪声,如果悬停过程中位置估计开始剧烈跳动,多半是安装减震不够。
建议在T265下方加一层4mm厚的泡棉双面胶或专用减震垫,避免它的IMU直接吸收机架振动。如果嗡嗡声传得太清晰,那就要考虑调整安装位置,把摄像头往机架中心靠,减少力臂效应。
在自稳模式下,飞控不使用视觉定位做位置控制,所以即使视觉数据异常也不会炸机。等飞机能稳定悬停,再切到位置模式,感受一下有没有缓慢漂移。位置模式下面,飞控会尝试通过视觉数据把飞机锁定在一个位置。
5.3 调整EKF2接收视觉数据的阈值
有些情况下数据链路是通的,但EKF2仍然不信任视觉数据,原因在于PX4有一组EKF2_REQ_*参数,用来限制数据更新的质量和步数。比如EKF2_REQ_VEL、EKF2_REQ_POS等,这些阈值如果太高,视觉数据轻微跳变就会触发“数据超时”,EKF2直接放弃使用视觉。
我的经验是把这个阈值调低一些。例如在PX4里设置EKF2_REQ_VEL=0.1,EKF2_REQ_POS=0.05,具体看固件文档说明。这样即使偶尔有一次异常数据,EKF2也不会立刻把整个视觉源踢出去。
还有一个比较隐蔽的问题是偏航漂移。T265长时间运行后,偏航会缓慢漂移,室内没有罗盘校正的话,无人机可能会在空中慢慢绕圈。解决办法有几个:一是让T265初始对准一个明确特征方向再起飞;二是用EKF2的磁罗盘辅助偏航,但室内磁场干扰大,反而可能更糟;三是定期让飞控用遥控器的偏航杆“强制重置”视觉偏航基准。这个方法的效果取决于固件,但至少能保证短时间的定位持续可用。
6. 常见问题与避坑实录
6.1 现象:视觉位姿一直是零值
可能原因排查顺序
先看T265看到的环境是否有足够纹理,再看rostopic hz /camera/odom/sample有没有数据,最后看USB是不是跑在2.0模式下。尤其要注意,有些树莓派的USB口供电不足,T265会降速运行,数据率直接掉到几赫兹。
处理办法
给T265换一个带独立供电的USB3.0 hub,或者用高质量数据线。飞行时别让T265对准无纹理的白墙,多让它看到地面和周围物体。
6.2 现象:飞控端的视觉位姿超时
可能原因
数据已发布,但MAVROS没把数据发到飞控,或者EKF2因为参数阈值太高完全不采信视觉数据。
处理办法
在QGC的MAVLink控制台输入listener vision_position_estimate,如果消息频率正常,说明MAVROS到飞控这半段没问题,那就去改EKF2_REQ_*的阈值和下发的消息频率。如果消息频率几乎为零,停下来检查MAVROS的重映射是否生效,话题名有没有拼错。
6.3 现象:位置模式悬停时飞机缓慢漂移
可能原因
T265的偏航漂移在积分累积后会体现为位置漂移,尤其在没有GPS校正、没有罗盘的场景下。另一个原因是TF变换未校准,相机和机体的坐标系偏移有误差。
处理办法
起飞时让T265朝向与机头完全一致,避免机头对准大块无纹理区域。如果漂移方向固定,可以在TF静态变换里补偿一个偏航角。实测来看,T265在10分钟内的偏航漂移通常小于2度,对单次飞行来说完全可以接受,但如果飞完一个起降还想保持高精度,最好回原点重新初始化。
6.4 现象:悬停一段时间后飞控突然切换到了自稳模式
可能原因
EKF2判定视觉定位失效,自动降级。最常见原因是视觉更新中断,或者T265在上电后内部算法崩溃。其次是电机振动噪声污染了IMU数据,导致EKF2的imu数据异常。
处理办法
检查T265的温度,如果过热它会自动降低输出频率或暂停。长期飞行时要注意给T265加散热,我见过不少新手把它塞进封闭的机舱里,结果飞了五分钟就过热报警。另外安装减震垫也是必须的,尤其是电机直接焊在小机架的情况下。
6.5 避坑清单
说了这么多,最后把这套方案里最值得记住的经验汇总一下,方便大家复现时少踩坑:
- 上电后静置1分钟再起飞,让T265和EKF2完成初始化。
- 室内灯光要是正常亮度,避免逆光和纯白墙。
- 没有GPS的场合,把
EKF2_GPS_CHECK设成0,SYS_HAS_GPS设成0。 - 视觉高度建议加上气压计兜底,
EKF2_HGT_MODE选4。 - 用EKF2_EV_CTRL精细控制视觉融合时,检查它和EKF2_AID_MASK是否是同一个语义,别互相覆盖。
- T265的USB线要固定好,空中振动导致接口松动是排查起来最痛苦的问题。
- 每次更换安装位置或拍摄角度后,重新做一次静态TF变换校准。
- 先在地面上通过QGC观察位置更新,再解锁起飞,不要跳过验证。
我在实际调试中最深刻的体会是,T265这套方案并不难,难的是数据链路上任何一个环节都可能静默失效。你看着ROS话题数据正常,飞控参数也设置了,但松杆悬停时它还是飘,这种问题通常不是算法问题,而是安装细节问题。把相机固定牢、把USB线捆好、把视角调整好,往往比调参数更能解决问题。真希望当初有人把这些都写在教程开头,能省下我一个月的折腾时间。