FEATURED · 精选文章

机器人定位实战:基于ROS的GNSS与IMU/里程计融合(EKF与因子图优化对比)

发布时间 / 2026/9/3 8:49:37
来源 / 创域科博编辑部
栏目 / 资讯中心
机器人定位实战:基于ROS的GNSS与IMU/里程计融合(EKF与因子图优化对比) 简介本资源是一套面向机器人定位方向毕业设计与科研实践的完整ROS工程方案聚焦GNSS多源数据融合定位问题适用于自动驾驶、高精度导航等场景下的本科生与研究生项目开发。压缩包共2000个文件总大小833.54MB涵盖112个C核心算法实现如FGO因子图构建与EKF状态更新、119个头文件、75个ROS launch配置、375个实测GNSS原始观测CSV数据含GPS/北斗双模05o/05n/09g等格式、以及PDF项目文档、RVIZ可视化配置与KML轨迹导出工具等支撑从数据解析、滤波建模到结果评估的全流程复现。已有373人学习下载提供可直接编译运行的Ceres-solverRTKLIB集成环境、多场景驾驶/步行/高层遮挡测试数据集及对比分析说明助力读者快速掌握因子图优化与扩展卡尔曼滤波在实际GNSS定位中的工程落地方法。1. 项目概述从零构建一个高精度的ROS-GNSS融合定位系统最近在做一个户外移动机器人的项目定位是核心需求之一。虽然激光雷达和视觉SLAM在室内和结构化环境表现不错但一到开阔的室外尤其是天空视野良好的场景GNSS全球导航卫星系统就成了不可或缺的绝对定位源。然而单纯的GNSS数据尤其是单点定位精度在米级还容易受多路径、信号遮挡等影响直接拿来用肯定不行。于是我花了不少时间折腾出了一个基于ROS的GNSS定位系统核心目标就是把原始的、带噪声的GNSS数据与我们机器人自身的运动传感器如IMU、轮速计的数据融合起来得到一个更平滑、更可靠、精度更高的位姿估计。这个项目我尝试了两种主流的融合方法一种是经典的扩展卡尔曼滤波EKF另一种是近年来在SLAM和状态估计领域越来越火的因子图优化FGO。两种方法各有千秋EKF轻量、实时性好适合在线运行FGO则能利用历史信息进行全局优化精度通常更高但计算开销也大。我把整个系统的搭建过程、代码实现、参数调试心得以及两种方法的对比测试结果都整理成了详细的项目文档。今天我就把这个“基于ROS的GNSS定位系统”的完整实现思路和踩坑经验分享出来希望能给同样在做机器人定位特别是需要融合绝对位置信息的同行一些参考。2. 系统整体设计与核心思路拆解2.1 为什么选择ROS作为框架ROSRobot Operating System现在几乎是机器人研发的“标准配置”了。它提供的节点通信、消息传递、坐标变换TF和可视化Rviz等工具链能极大简化多传感器数据同步、处理和展示的复杂度。对于GNSS定位系统我们需要处理来自GNSS接收机的NMEA数据流、来自IMU的角速度和加速度数据、以及可能的轮式里程计数据。ROS的serial或socket包可以轻松接入串口/UDP的GNSS数据robot_localization等包提供了EKF的现成实现而gtsam或ceres-solver这类优化库也能很好地集成到ROS节点中。更重要的是整个数据流可以在Rviz里实时可视化看到定位轨迹、协方差椭圆调试起来非常直观。因此基于ROS来搭建这个系统是效率最高、生态最成熟的选择。2.2 核心传感器与数据流分析一个完整的融合定位系统其精度上限很大程度上取决于传感器配置。在这个项目中我主要考虑了以下数据源GNSS接收机这是我们的绝对位置源。我使用的是支持RTK实时动态定位的GNSS模块在开阔环境下能提供厘米级精度的navsatfix消息包含经纬高、协方差矩阵和定位状态。即使在没有RTK固定解的情况下单点定位的navsatfix或原始的NMEA语句如GGA也能提供米级的参考。关键是要解析出有效的位姿和可信度通过协方差或position_covariance_type来体现。惯性测量单元IMU提供机体的角速度和线性加速度。IMU数据频率高通常100Hz以上能很好地刻画机器人的高频运动弥补GNSS更新率低通常1-10Hz的不足。但IMU存在零偏和漂移需要与其他传感器融合来校正。轮式里程计对于地面移动机器人通过编码器积分得到的里程计能提供短时精度很高的相对位移估计是融合模型中非常重要的观测。它主要贡献于平面内的位移和航向。系统的数据流可以这样描述GNSS节点将原始的NMEA语句解析为ROS标准的sensor_msgs/NavSatFix消息IMU和里程计节点发布各自的sensor_msgs/Imu和nav_msgs/Odometry消息。这些消息通过一个融合节点EKF或FGO进行订阅。融合节点内部维护一个机器人的状态通常是位置、速度、姿态并利用这些异步、异频率的观测数据通过概率滤波或优化方法估计出最优的机器人位姿最终发布一个融合后的、更平滑可靠的nav_msgs/Odometry消息并广播到TF树。2.3 两种融合方法的选型考量EKF vs. FGO这是本项目的核心对比部分。选择哪种方法取决于你的应用场景、对精度和实时性的要求以及计算资源的限制。扩展卡尔曼滤波EKF核心思想一种递归的贝叶斯滤波器。它维护一个高斯分布来描述机器人状态均值和协方差。在每个时刻它先根据运动模型进行“预测”然后当新的观测如GNSS位置到来时进行“更新”修正预测的状态。EKF通过线性化非线性运动/观测模型来处理机器人中的非线性问题。优势计算高效状态更新只涉及当前时刻和上一个时刻计算复杂度低非常适合在线、实时的应用。内存占用小只需要保存当前的状态向量和协方差矩阵。成熟稳定在机器人领域应用了数十年有大量现成的库如ROS的robot_localization和调试经验。劣势线性化误差对于高度非线性的系统如剧烈运动线性化会引入误差可能导致滤波器发散。马尔可夫假设只依赖前一时刻状态无法利用“未来”的观测信息来修正“过去”的状态。这意味着如果一个GNSS观测因为遮挡而出现巨大跳变EKF会立刻被带偏且这个错误会影响后续所有状态无法回溯修正。对初始值和噪声模型敏感需要较准确地设置过程噪声和观测噪声的协方差矩阵Q和R调参需要经验。因子图优化FGO核心思想将状态估计问题建模为一个概率图模型。图中的节点是我们要估计的机器人状态在不同时间点的位姿边则是连接这些节点的约束包括运动模型约束相邻状态间如何变化和观测模型约束某个状态应满足的GNSS/IMU观测。通过最大化所有约束联合概率一次性优化所有节点的状态。优势全局一致性可以优化一个时间窗口内滑动窗口甚至全部历史状态利用未来的观测来修正过去的误差对GNSS的偶然跳变有很强的鲁棒性。精度更高避免了EKF的线性化误差累积通常能获得更精确、更平滑的轨迹。灵活性高可以方便地添加各种类型的约束闭环检测、点云匹配等很容易与视觉/激光SLAM系统结合。劣势计算开销大优化问题涉及求解大规模稀疏线性系统虽然有效率高的求解器如gtsam使用的iSAM2但相比EKF仍更耗时。延迟为了利用未来信息通常会引入一定的处理延迟例如优化一个包含最近几秒状态的滑动窗口不是严格的“逐帧”输出。实现更复杂需要构建因子图、选择优化器、管理滑动窗口等比配置一个EKF滤波器要复杂。我的选型建议如果你的机器人计算资源有限且对实时性要求极高控制环路需要EKF是更稳妥的选择。如果你追求更高的定位精度和轨迹平滑性能够接受几十到几百毫秒的延迟并且有足够的计算能力如机载工控机那么FGO是更好的选择。对于处理GNSS数据中的“野值”Outliers问题FGO的表现远优于EKF。在实际项目中我两者都实现了你可以根据场景切换。例如在实时导航时使用EKF在事后轨迹分析或建图时使用FGO进行后优化。3. 核心模块解析与实操要点3.1 GNSS数据接入与坐标转换这是整个项目的地基如果没处理好后面的融合再高级也没用。GNSS模块输出的是WGS-84坐标系下的经纬高LLH而我们的机器人通常在局部平面坐标系如UTM或自定义的ENU坐标系下运动。因此第一步是进行正确的坐标转换。1. 解析NMEA或自定义协议 大多数GNSS模块通过串口输出NMEA-0183格式的语句。你需要一个解析节点。可以使用现成的ROS包如nmea_navsat_driver或者自己写一个简单的解析器。关键是要解析出$GPGGA或$GNGGA语句获取时间、纬度、经度、海拔、定位状态和卫星数。如果使用RTK还要关注$GPGST或RTCM消息相关的状态字以确定是单点解、浮点解还是固定解。2. 发布NavSatFix消息 将解析出的数据填充到sensor_msgs/NavSatFix消息中。以下几个字段至关重要latitude,longitude,altitude: 直接的经纬高。position_covariance: 一个3x3的协方差矩阵表示定位精度。对角线元素通常为东、北、天方向的方差。RTK固定解时这个值可以很小如0.01-0.04 m²单点解时则很大如25-100 m²。务必根据GNSS模块输出的实际定位精度或DOP值来动态设置这个协方差这是融合滤波器衡量GNSS观测可信度的关键依据position_covariance_type: 协方差类型一般设为COVARIANCE_TYPE_APPROXIMATED或COVARIANCE_TYPE_DIAGONAL_KNOWN。status.status: 表示定位状态如STATUS_FIX有定位、STATUS_GBAS_FIX增强定位等。3. 坐标转换到局部平面 这是最容易出错的一步。你不能直接将经纬高当作XY坐标来用。通常的流程是确定原点选择一个合适的点作为局部坐标系的原点例如机器人启动点。记录下这个点的经纬高(lat0, lon0, alt0)。LLH转ECEF将原点(lat0, lon0, alt0)和当前点(lat, lon, alt)都从WGS-84经纬高转换到地心地固直角坐标系ECEF。有标准的公式或库函数如geographiclib或PROJ库可以完成。ECEF转ENU以原点ECEF坐标为参考将当前点的ECEF坐标转换为以原点为基准的东-北-天ENU坐标。这样得到的(e, n, u)就是机器人在局部水平坐标系下的位置。选择UTM对于大范围移动更推荐使用UTM通用横轴墨卡托投影坐标系。它能把经纬度直接投影成平面直角坐标(easting, northing)并给出所在UTM分区。使用proj或GeographicLib库可以一键转换。UTM坐标在几十公里范围内尺度变形极小非常适合机器人导航。注意坐标转换的代码一定要反复验证。一个简单的检查方法是让机器人静止连续输出转换后的ENU/UTM坐标看其波动是否在预期范围内例如RTK固定解时波动应在厘米级。同时要确保转换后的Z轴高程处理正确有些应用可能只关心平面位置。3.2 IMU与里程计的数据预处理传感器数据在送入融合器之前必须进行必要的预处理和标定。IMU预处理零偏标定IMU静止时采集一段时间的加速度计和陀螺仪数据计算其均值这就是零偏。在启动时或定期在线估计并扣除零偏对抑制漂移至关重要。坐标系对齐确保IMU的坐标系与机器人本体坐标系通常是ROS中的base_link对齐。如果不一致需要通过一个固定的旋转矩阵进行转换。这个旋转矩阵可以通过手持IMU做特定旋转运动来标定例如使用imu_filter_madgwick或imu_complementary_filter包中的自动标定工具或者更专业的工具如kalibr。时间同步确保IMU数据的时间戳尽可能准确。如果IMU有自己的时钟可能需要与系统时钟做软同步。里程计预处理标定轮式里程计需要标定轮子半径和轮距对于差分驱动。一个常见的方法是让机器人走一个精确的正方形或圆形通过实际移动距离与编码器读数计算标定参数。误差模型里程计的主要误差来源是滑动和打滑。在融合时需要为里程计的速度或位移观测设置一个合理的噪声协方差这个噪声通常会随着速度增大而增大。3.3 扩展卡尔曼滤波EKF实现细节我使用了ROS中非常强大的robot_localization包来实现EKF融合。它支持融合任意多个传感器输入并输出在odom或map坐标系下的位姿。1. 状态向量与模型robot_localization中的EKF节点ekf_localization_node默认状态向量是15维位置(x,y,z)速度(vx,vy,vz)姿态用四元数表示(qw,qx,qy,qz)以及加速度和角速度的零偏。它使用3D刚体运动模型。2. 配置文件与参数调试 核心在于编写一个正确的.yaml配置文件。你需要为每个输入话题指定其提供哪些观测值pose和twist的哪些维度以及这些观测值的噪声协方差。# ekf_config.yaml 示例片段 ekf_filter_node: frequency: 50.0 # 滤波器预测频率 sensor_timeout: 0.1 # 传感器超时时间 two_d_mode: false # 是否为2D模式如果只关心平面可以设为true简化计算 # 里程计输入配置 odom0: /wheel_odom odom0_config: [true, true, false, # X, Y, Z 位置 false, false, false, # 滚转、俯仰、偏航角 true, true, false, # X, Y, Z 线速度 false, false, true] # 滚转、俯仰、偏航角速度 odom0_queue_size: 10 odom0_nodelay: true odom0_differential: false odom0_relative: false # GNSS输入配置 (从NavSatFix转换而来通常作为绝对位置观测) pose0: /gps_enu # 这是一个由自定义节点将NavSatFix转换到局部ENU坐标系后发布的PoseWithCovarianceStamped消息 pose0_config: [true, true, true, # 绝对位置X, Y, Z false, false, false] # 绝对姿态GNSS通常不提供可靠姿态 pose0_queue_size: 10 # IMU输入配置 imu0: /imu/data imu0_config: [false, false, false, # 位置 false, false, true, # 姿态通常只用IMU的偏航角因为加速度计受重力影响俯仰滚转需要其他观测修正 false, false, false, # 速度 true, true, true] # 角速度 imu0_queue_size: 10 # 过程噪声Q矩阵和初始估计误差协方差P矩阵需要仔细调整 process_noise_covariance: [0.05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... ] # 这是一个15x15对角矩阵的扁平化表示需要根据机器人动态特性设置 initial_estimate_covariance: [0.1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... ] # 初始状态的不确定度3. 关键调试经验pose0_config对于GNSS通常只融合其位置(x,y,z)不融合姿态因为单天线GNSS不提供可靠的航向。姿态主要依赖IMU的陀螺仪积分和里程计。协方差动态设置这是提升性能的关键。不要给GNSS观测一个固定的噪声协方差。应该根据NavSatFix消息中的position_covariance和status来动态设置。例如当状态是RTK固定解时给一个很小的噪声值如0.01当状态是单点解时给一个很大的噪声值如100这样滤波器就会“信任”RTK观测“怀疑”单点观测。坐标系管理务必理清odom、map、base_link坐标系的关系。robot_localization可以输出odom-base_link的TF基于里程计和IMU的高频相对定位也可以输出map-odom的TF将GNSS的绝对位置作为map系的原点修正。常见的设置是让EKF输出odom-base_link然后发布一个根据融合后的绝对位姿计算得到的map-odom静态变换或缓慢更新的变换。3.4 因子图优化FGO实现细节对于FGO我选择了GTSAMGeorgia Tech Smoothing and Mapping库它提供了强大的因子图和iSAM2增量求解器非常适合在线滑动窗口优化。1. 因子图构建 在滑动窗口内我们维护一系列状态节点X_t位姿可能还包括速度、零偏等。然后添加三种主要因子Between因子运动模型连接相邻状态节点X_t和X_{t1}。这个因子描述了从t时刻到t1时刻机器人应该运动了多少由IMU预积分或里程计给出。它有一个协方差矩阵表示这个运动模型的不确定性。GPS因子观测模型在接收到GNSS观测的时刻添加一个连接对应状态节点X_k的因子。这个因子表示该状态节点的位置部分应该与GNSS观测到的ENU位置一致。其协方差同样来自GNSS消息中的position_covariance。Prior因子为第一个状态节点添加一个先验因子将其固定在初始位置例如(0,0,0)为整个优化问题提供一个锚点。2. 使用GTSAM与iSAM2iSAM2是GTSAM提供的增量平滑和建图算法它支持在线、增量式地更新因子图并重新优化而不是每次都从头优化效率很高。// 简化示例代码结构 #include gtsam/navigation/CombinedImuFactor.h #include gtsam/navigation/GPSFactor.h #include gtsam/slam/BetweenFactor.h #include gtsam/nonlinear/ISAM2.h #include gtsam/geometry/Pose3.h // 1. 定义因子图 gtsam::NonlinearFactorGraph graph; gtsam::Values initialEstimate; // 2. 创建iSAM2实例 gtsam::ISAM2Params parameters; parameters.relinearizeThreshold 0.01; parameters.relinearizeSkip 1; gtsam::ISAM2 isam(parameters); // 3. 添加先验因子对第一个位姿 gtsam::Pose3 first_pose gtsam::Pose3(gtsam::Rot3::Ypr(yaw0, pitch0, roll0), gtsam::Point3(x0, y0, z0)); initialEstimate.insert(pose_key1, first_pose); graph.addPrior(pose_key1, first_pose, prior_noise); // 4. 主循环 while (ros::ok()) { // 接收到新的里程计或IMU预积分数据 if (new_odom_arrived) { // 添加一个新的状态节点 X_{t1} pose_key_next pose_key_current 1; // 根据运动模型预测新位姿作为初始估计 gtsam::Pose3 predicted_pose ...; initialEstimate.insert(pose_key_next, predicted_pose); // 添加一个Between因子连接 X_t 和 X_{t1} gtsam::Pose3 odometry ...; // 从里程计得到的相对变换 graph.add(BetweenFactorPose3(pose_key_current, pose_key_next, odometry, odometry_noise)); } // 接收到新的GNSS数据 if (new_gps_arrived) { // 找到GNSS时间戳对应的状态节点 key_k // 添加一个GPS因子 gtsam::Point3 gps_position(enu_x, enu_y, enu_z); graph.add(GPSFactor(pose_key_k, gps_position, gps_noise)); } // 5. 增量更新iSAM2 isam.update(graph, initialEstimate); // 获取当前最优估计 gtsam::Values currentEstimate isam.calculateEstimate(); // 发布最新的优化后位姿例如滑动窗口中最新的那个 // 6. 清空临时图和初始估计为下一次迭代准备 graph.resize(0); initialEstimate.clear(); // 7. 滑动窗口管理如果节点数超过窗口大小移除最老的节点和与之相连的因子 if (pose_key_next - oldest_key window_size) { // 标记最老的变量为边缘化iSAM2会将其从优化中移除但保留其信息 // 具体操作涉及iSAM2的标记和部分重新线性化 } }3. 滑动窗口管理 为了控制计算复杂度我们不能让因子图无限增长。需要维护一个固定大小的滑动窗口。当新的状态节点加入时如果窗口已满就需要将最老的状态节点边缘化Marginalization。在iSAM2中这可以通过将旧变量标记为“移除”来实现iSAM2会将其从当前优化变量中剔除但其携带的信息会以先验因子的形式保留下来影响窗口内的其他变量。这是保持全局一致性的关键。4. IMU预积分 为了更精确地利用高频IMU数据最佳实践是进行IMU预积分。即在两个关键帧例如GNSS观测时刻或固定的时间间隔之间对IMU的角速度和加速度进行积分得到一个相对的运动约束Delta位置、速度、旋转这个约束只依赖于这两个关键帧之间的IMU数据与零偏估计解耦计算效率更高。GTSAM提供了PreintegratedCombinedMeasurements类来方便地实现IMU预积分并生成CombinedImuFactor。4. 系统集成与部署实操4.1 ROS包结构与启动文件一个清晰的ROS包结构能让项目易于管理和复用。我的项目结构大致如下gnss_fusion_localization/ ├── CMakeLists.txt ├── package.xml ├── launch/ │ ├── start_gnss_driver.launch # 启动GNSS驱动节点 │ ├── start_ekf_fusion.launch # 启动EKF融合节点 │ ├── start_fgo_fusion.launch # 启动FGO融合节点 │ └── start_all.launch # 一键启动所有节点 ├── config/ │ ├── ekf_params.yaml # EKF滤波器参数 │ ├── fgo_params.yaml # FGO优化器参数 │ └── transforms.yaml # 静态TF配置 ├── src/ │ ├── gnss_driver_node.cpp # GNSS数据解析与坐标转换节点 │ ├── ekf_fusion_node.cpp # (可选)自定义EKF节点或直接调用robot_localization │ ├── fgo_fusion_node.cpp # FGO融合节点使用GTSAM │ └── utils.cpp # 坐标转换等工具函数 ├── scripts/ │ └── calibrate_imu.py # IMU标定脚本 └── rviz/ └── localization.rviz # Rviz配置文件在start_all.launch文件中我会按顺序启动所有节点并加载相应的参数文件。同时使用node pkgtf typestatic_transform_publisher ... /发布必要的静态坐标变换例如从base_link到gnss_antennaGNSS天线相位中心的变换。这个变换非常重要因为GNSS观测的是天线中心的位置而我们需要的是机器人中心base_link的位置。4.2 多传感器时间同步策略传感器数据的时间戳如果不一致融合效果会大打折扣。ROS提供了几种同步策略近似时间同步使用message_filters库中的ApproximateTime策略。当你有多个话题如/imu/data和/gps/fixed需要同步处理时可以创建一个ApproximateTimeSynchronizer设置一个合适的时间容差例如0.01秒。当来自不同话题的消息时间戳之差在容差范围内时就会触发回调函数。这对于EKF或FGO节点处理异步数据非常有用。硬件时间戳如果传感器支持如一些高端IMU和GNSS接收机尽量使用其硬件产生的时间戳并通过NTP或PTP协议与主机同步这能获得最精确的时间对齐。插值对于高频传感器如IMU当低频传感器如GNSS数据到达时可以通过插值获得对应时刻的高频传感器数据。在我的FGO实现中我采用了基于消息时间戳的异步因子添加方式。每个传感器数据到来时都以其时间戳为索引将其对应的因子添加到因子图中对应时间戳的状态节点上。iSAM2能够处理这种异步的、非等间隔的因子添加。4.3 可视化与调试技巧Rviz是调试机器人系统的神器。我通常会配置以下几个重要的显示项轨迹分别显示原始的GNSS轨迹/gps/fixed-path、EKF输出的轨迹/odometry/filtered-path和FGO输出的轨迹。用不同颜色区分直观对比平滑度和延迟。位姿显示融合后的机器人模型/odometry/filtered-RobotModel观察其运动是否自然。协方差椭圆在Odometry显示属性中勾选“Covariance”可以显示位置和方向的不确定度椭圆。一个健康运行的滤波器其协方差椭圆应该大小合理且稳定。如果椭圆突然变得巨大说明观测可能出了问题或滤波器即将发散。TF坐标系查看map、odom、base_link、gps等坐标系之间的关系是否正确。确保map-odom的变换是缓慢更新的由GNSS绝对观测驱动而odom-base_link是高频平滑变化的由IMU/里程计驱动。除了Rviz用rqt_plot绘制关键状态值随时间的变化也很有帮助比如X、Y位置、速度、协方差对角线元素等可以帮你发现数据异常或参数设置不当。5. 性能评估、对比与常见问题排查5.1 测试场景设计与评估指标为了公平对比EKF和FGO我设计了几个测试场景开阔场地直线往返在RTK固定解可用的开阔场地让机器人沿直线匀速往返。评估指标轨迹的直线度、终点闭合误差回到起点的位置偏差。GNSS信号遮挡/多路径环境让机器人经过高楼旁、树下或桥洞。评估指标在信号丢失或劣化期间轨迹的平滑程度和漂移量。这是检验算法鲁棒性的关键。动态性能测试让机器人做加速、减速、转弯等机动动作。评估指标融合后位姿的延迟情况以及在高动态下是否出现明显滞后或振荡。定量评估时如果条件允许可以使用更高精度的定位系统如全站仪、激光跟踪仪的测量值作为“真值”Ground Truth计算融合轨迹的均方根误差RMSE。如果没有真值可以定性分析轨迹的平滑性、对GNSS跳变的抑制能力以及计算轨迹的闭合误差。5.2 EKF与FGO实测结果对比在我进行的测试中两种方法表现出了预期的差异精度与平滑性在GNSS信号良好的开阔区域两者都能输出厘米级精度的轨迹。但FGO的轨迹在视觉上明显更平滑尤其是在机器人转弯时EKF的轨迹会有轻微的“阶梯感”而FGO则是一条光滑的曲线。事后计算闭合误差FGO通常比EKF小20%-50%。抗干扰能力当机器人短暂经过信号遮挡区GNSS出现几米跳变时EKF的轨迹会立刻产生一个明显的“毛刺”并且这个毛刺会影响后续一段时间的估计。而FGO滑动窗口优化则能很好地“吸收”这个跳变在滑动窗口内将其平滑掉输出的轨迹几乎看不到这个干扰。这是FGO最大的优势。计算资源与实时性EKF运行在树莓派4B上也能轻松达到50Hz以上CPU占用率很低。而FGO滑动窗口大小为50使用iSAM2在Intel NUC上运行优化一步需要10-50毫秒取决于窗口内因子数量输出频率约10-20Hz。对于实时控制要求极高的场景EKF是唯一选择对于导航、建图等可以接受一定延迟的场景FGO更具吸引力。调参复杂度EKF需要精心调整process_noise_covariance和每个传感器的观测噪声。调参过程更像一门“艺术”需要反复试验。FGO也需要设置运动模型和观测模型的噪声协方差但由于其优化特性对噪声模型的敏感性似乎略低于EKF但滑动窗口大小、边缘化策略等又带来了新的参数。5.3 常见问题与排查技巧实录在实际部署中我遇到了不少坑这里总结一下问题1融合轨迹在GNSS信号良好时也频繁跳动。可能原因GNSS观测的协方差设置过小。滤波器过于“信任”GNSS而GNSS本身的噪声即使是RTK也有厘米级波动被直接反映到了输出中。排查检查NavSatFix消息中的position_covariance是否合理。在Rviz中观察GNSS原始数据点的抖动情况。适当增大EKF配置文件中GNSS观测的噪声协方差pose0_*的噪声参数或是在FGO中增大GPS因子的噪声矩阵。技巧实现一个简单的协方差自适应模块。根据GNSS的定位状态单点/浮点/固定、卫星数、HDOP值来动态缩放观测噪声。固定解时用低噪声单点解时用高噪声。问题2机器人静止时融合位置仍有缓慢漂移。可能原因IMU零偏估计不准或者里程计存在微小的零速输出。排查让机器人长时间静止记录EKF估计的速度是否为零以及IMU的角速度和加速度零偏估计值是否收敛。检查里程计在零速时是否有微小脉冲输出。解决进行更严格的IMU静止标定。在EKF中确保过程噪声设置合理让滤波器在静止时能“相信”零速观测如果有的话。对于FGO可以在静止时添加零速因子Zero Velocity Factor, ZUPT强制某些时刻的速度为零这能有效抑制漂移。问题3启动时滤波器发散轨迹“飞”掉。可能原因初始状态或协方差设置错误第一个GNSS观测到来前纯积分误差过大。排查检查EKF的initial_estimate_covariance是否设置得足够大以表示初始状态的高度不确定性。确保在收到第一个可靠的GNSS绝对定位之前不要发布不可信的融合结果。解决实现一个初始化阶段。在启动后的头几秒只进行IMU/里程计积分并等待一个质量较高的GNSS定位如RTK固定解到来后再用这个位置信息初始化滤波器的状态然后才正式开始融合输出。在FGO中可以用第一个可靠的GNSS观测作为一个很强的先验因子。问题4坐标系混乱轨迹方向错误或尺度不对。可能原因ENU/UTM坐标转换错误base_link到gnss_antenna的TF变换弄反了ROS中odom和map坐标系的使用逻辑错误。排查最直接的验证方法。让机器人向前移动1米用卷尺测量查看融合输出的odometry在X方向上的增量是否是1米。让机器人原地旋转90度查看偏航角变化是否是90度。技巧在Rviz中同时显示base_link和gnss坐标系。当机器人静止时gnss坐标系的原点应该固定在base_link坐标系中的一个特定偏移位置。如果这个点乱跑说明TF或坐标转换有问题。画一个简单的坐标系示意图明确每个坐标系的意义和转换关系并写在项目文档里。问题5FGO优化速度越来越慢。可能原因滑动窗口未正确管理因子图无限增长iSAM2参数设置不当。排查打印优化耗时和因子图规模。检查旧的状态节点是否被正确边缘化并从优化变量中移除。解决确保实现了滑动窗口机制。调整iSAM2的relinearizeThreshold和relinearizeSkip参数。阈值设得太大可能导致线性化点过时优化不准设得太小则会导致频繁重新线性化计算变慢。找到一个平衡点。经过这一整套从理论到实践从模块拆解到系统集成再到调试排坑的完整流程一个鲁棒、高精度的ROS-GNSS融合定位系统才算真正搭建完成。选择EKF还是FGO不再是纸上谈兵而是基于具体需求和资源的务实决策。希望我的这些经验能让你在开发自己的定位系统时少走些弯路。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻