简介这份资源面向ROS机器人开发者与学习室内自主导航的学生聚焦雷达定位EKF与环境建图这一典型SLAM场景帮助读者理解扩展卡尔曼滤波如何融合激光雷达与IMU数据为ros小车提供高精度位姿估计并配合gmapping、amcl完成建图与定位。压缩包共107个文件约21.01MB以cpp源码与h头文件为核心涵盖ekf、ukf、navsat_transform等定位节点实现test测试用例、srv服务定义、launch启动文件、yaml参数配置与bag数据包共同构成可复现的实验环境另有rst文档、pdf说明及CMakeLists.txt、package.xml等构建文件目录结构贴近真实ROS工程。目前已有4358人学习下载。读者可从中获得雷达数据预处理、坐标转换、EKF状态模型设计到地图保存与amcl定位的完整实现思路并借助测试用例与录包数据排查定位漂移、建图错位等常见问题适合具备一定ROS基础、希望深入掌握小车建图定位的中高级开发者参考。1. 从一堆点云到一条能走的路robot_localization 在 ROS 小车上到底干了什么很多人第一次把雷达装到 ROS 小车上打开 RViz 看到一圈圈点云扫出来心里觉得稳了结果一按键盘让车动起来车要么原地画龙要么走两米就偏出半米。问题往往不在雷达也不在底盘而在“定位”这一环——雷达给的是相对观测轮速计给的是累计漂移IMU 给的是高频但会飘的姿态这三路数据各说各话没有一个东西把它们揉成一个可信的位姿。robot_localization 就是干这件事的它是一个 ROS 下的状态估计节点包用扩展卡尔曼滤波EKF或无迹卡尔曼滤波UKF把轮速、IMU、雷达定位比如 AMCL 或激光里程计融合成一条连续的odom → base_link和map → odom变换。环境建图则是它的上游输入雷达定位是它的观测来源之一。这篇笔记面向的是手里有 ROS 小车、想跑通建图与定位闭环的工程师从环境建图怎么出图、EKF 参数怎么配、到融合后怎么验证一步步拆开讲。2. 环境建图先出图雷达建图的最小闭环与参数取舍2.1 为什么建图质量直接决定 EKF 融合的上限robot_localization 本身不建图它吃的是别人建好的地图和定位结果。如果你用 gmapping 或 cartographer 建出来的图本身就是歪的、重影的后面 EKF 再怎么调都是白搭。常见做法是先用激光雷达跑一遍 gmapping 或 cartographer把/map和map → odom变换发出来再用 AMCL 在已知地图上做粒子滤波定位输出/amcl_pose作为 EKF 的一个观测输入。这里有个关键点EKF 融合的是“位姿观测”不是原始点云。所以建图阶段要保证雷达安装水平、base_link → laser的外参准确否则地图和实际环境存在系统性旋转偏差AMCL 定位会持续偏。建图时我一般会先确认三件事雷达话题名对不对、tf树是否完整、轮速计标定是否靠谱。轮速计标定不准建图时转弯会“糊墙”直墙变弧墙。标定方法很简单让车直线走 2 米看odom里走了多少算比例系数填回底盘参数。这个系数不调后面 EKF 融合轮速和雷达时会互相打架。2.2 用 gmapping 跑通第一张栅格地图的命令与配置下面是一个最小可用的 gmapping 启动文件片段假设雷达话题是/scan轮速里程计话题是/odomtf树已经由底盘驱动发好。!-- gmapping_min.launch -- launch !-- 雷达驱动实际话题名按你的雷达改 -- node namerplidar pkgrplidar_ros typerplidarNode outputscreen param nameserial_port value/dev/ttyUSB0/ param nameframe_id valuelaser/ /node !-- gmapping 建图 -- node nameslam_gmapping pkggmapping typeslam_gmapping outputscreen param namebase_frame valuebase_link/ param nameodom_frame valueodom/ param namemap_frame valuemap/ param namescan_topic value/scan/ !-- 粒子数小车室内 30 够用太大吃 CPU -- param nameparticles value30/ !-- 更新间隔雷达 10Hz 就设 0.05 -- param namemap_update_interval value0.05/ !-- 栅格分辨率5cm 是室内小车常用值 -- param namedelta value0.05/ !-- 激光测距最大范围按雷达实际改 -- param namemaxRange value12.0/ param nameminRange value0.15/ /node !-- RViz 看建图效果 -- node namerviz pkgrviz typerviz args-d $(find your_pkg)/rviz/mapping.rviz/ /launch逻辑说明gmapping 订阅/scan和tf内部用粒子滤波估计机器人轨迹并增量式建图。particles是粒子数粒子越多越准但计算量越大室内小车 30 到 50 足够delta是地图分辨率0.05 表示每个栅格 5cm太小地图文件巨大太大障碍物边缘粗糙map_update_interval控制地图发布频率设太小会频繁刷新导致 RViz 卡顿。建图时让车慢速走一圈速度控制在 0.2m/s 以内转弯要缓急转弯会导致雷达帧匹配失败地图出现重影。2.3 建图完成后保存地图与检查 tf 树建完图先别急着关用map_server把地图存下来# 保存地图到当前目录生成 map.pgm 和 map.yaml rosrun map_server map_saver -f ./map保存后检查map.yaml里的resolution、origin和negate是否正确。origin是地图左下角在map坐标系下的位姿如果建图时起点不在原点这个值会偏移后续 AMCL 定位会整体平移。检查tf树用rosrun tf view_frames # 生成 frames.pdf确认 map → odom → base_link → laser 链路完整常见问题是map → odom由 gmapping 发布建图结束后 gmapping 关闭这个变换就断了。所以建图完成后要切换到 AMCL 来发布map → odom否则 EKF 拿不到地图坐标系下的位姿。3. robot_localization 双 EKF 配置把轮速、IMU、雷达定位揉成一条位姿3.1 为什么用两个 EKF 而不是一个robot_localization 的经典用法是跑两个ekf_localization_node一个做odom → base_link的局部融合输入轮速和 IMU输出连续平滑的局部里程计另一个做map → odom的全局融合输入局部融合结果和 AMCL 定位输出地图坐标系下的位姿。这样分层的好处是局部 EKF 不受地图跳变影响全局 EKF 可以处理 AMCL 的离散修正。如果只用一个 EKF 同时吃轮速和 AMCLAMCL 偶尔跳一下整个位姿会抖小车会突然“抽一下”。两个 EKF 的world_frame参数不同局部 EKF 的world_frame设为odom全局 EKF 设为map。这是最容易配错的地方配反了tf树会打架RViz 里车会瞬移。3.2 局部 EKF 配置文件与参数逐项说明下面是一个局部 EKF 的 YAML 配置融合轮速里程计和 IMU。# ekf_local.yaml ekf_local: frequency: 30.0 # 滤波频率要高于所有输入话题频率 sensor_timeout: 0.1 # 传感器超时超过就丢弃该输入 two_d_mode: true # 小车在平面运动强制二维减少漂移 publish_tf: true # 发布 odom → base_link map_frame: map odom_frame: odom base_link_frame: base_link world_frame: odom # 局部融合world 是 odom # 轮速里程计输入 odom0: /wheel_odom odom0_config: [false, false, false, false, false, false, true, true, false, false, false, false, false, false, false] odom0_differential: false odom0_relative: false # IMU 输入 imu0: /imu/data imu0_config: [false, false, false, false, false, false, false, false, false, false, false, true, false, false, false] imu0_differential: false imu0_relative: false imu0_remove_gravitational_acceleration: true逻辑说明odom0_config是一个 15 位布尔数组对应[x, y, z, roll, pitch, yaw, vx, vy, vz, vroll, vpitch, vyaw, ax, ay, az]。上面配置表示只取轮速的vx和vyaw不取绝对位置因为轮速的绝对位置会漂移取速度更稳。IMU 只取vyaw角速度不取绝对姿态因为 IMU 的 yaw 会随时间漂移但短时角速度很准。two_d_mode: true强制忽略 z、roll、pitch小车在平面跑必须开否则 IMU 的俯仰噪声会污染位姿。imu0_remove_gravitational_acceleration: true去掉重力分量不然加速度计数据不能用。3.3 全局 EKF 配置与 AMCL 接入全局 EKF 吃局部 EKF 的输出和 AMCL 的位姿。# ekf_global.yaml ekf_global: frequency: 30.0 sensor_timeout: 0.1 two_d_mode: true publish_tf: true map_frame: map odom_frame: odom base_link_frame: base_link world_frame: map # 全局融合world 是 map # 局部 EKF 输出作为里程计输入 odom0: /odometry/filtered odom0_config: [true, true, false, false, false, false, true, true, false, false, false, true, false, false, false] odom0_differential: false odom0_relative: false # AMCL 定位输入 pose0: /amcl_pose pose0_config: [true, true, false, false, false, true, false, false, false, false, false, false, false, false, false] pose0_differential: false pose0_relative: false逻辑说明全局 EKF 的odom0是局部 EKF 发布的/odometry/filtered取x, y, vx, vy, vyawpose0是 AMCL 的/amcl_pose取x, y, yaw。注意 AMCL 的协方差有时候给得不准如果发现全局 EKF 被 AMCL 拽得太狠可以在pose0_config里只取x, yyaw 靠局部 EKF 的vyaw积分这样更平滑。world_frame: map让全局 EKF 发布map → odom变换正好补上 gmapping 关闭后的空缺。3.4 启动文件与 tf 树验证把两个 EKF 和 AMCL 一起启动!-- localization.launch -- launch node namemap_server pkgmap_server typemap_server args$(find your_pkg)/maps/map.yaml/ node nameamcl pkgamcl typeamcl outputscreen param nameodom_frame_id valueodom/ param namebase_frame_id valuebase_link/ param nameglobal_frame_id valuemap/ param namescan_topic value/scan/ !-- 粒子数定位稳定后可以降到 200 省 CPU -- param namemin_particles value200/ param namemax_particles value2000/ /node node nameekf_local pkgrobot_localization typeekf_localization_node outputscreen rosparam commandload file$(find your_pkg)/config/ekf_local.yaml/ /node node nameekf_global pkgrobot_localization typeekf_localization_node outputscreen rosparam commandload file$(find your_pkg)/config/ekf_global.yaml/ /node /launch启动后验证tf树rosrun tf view_frames # 确认 map → odom 由 ekf_global 发布odom → base_link 由 ekf_local 发布如果发现map → odom有两个节点在发比如 AMCL 和 ekf_global 同时发RViz 里车会来回跳。解决办法是把 AMCL 的publish_tf设为false让它只发/amcl_posetf交给全局 EKF 统一发。4. 融合效果排查从 RViz 现象反推参数问题4.1 车在 RViz 里画龙或瞬移现象小车直走时 RViz 里的位姿左右摆动或者突然跳一段。原因通常是局部 EKF 的odom0_config取了绝对位置x, y而轮速里程计的绝对位置本身有累积误差和 IMU 的角速度积分打架。解决轮速只取vx, vyaw不取x, y。另一个原因是two_d_mode没开IMU 的 roll/pitch 噪声混进来。检查ekf_local.yaml里two_d_mode: true是否生效。4.2 AMCL 定位跳变导致全局位姿抖动现象小车走到走廊尽头或长直走廊中间AMCL 粒子发散/amcl_pose突然跳几十厘米全局 EKF 跟着跳。原因长走廊或对称环境激光匹配歧义AMCL 粒子退化。解决在pose0_config里把 AMCL 的yaw关掉只取x, y让全局 EKF 的 yaw 靠局部 EKF 的vyaw积分维持同时把 AMCL 的min_particles调大增加粒子多样性。如果环境特别对称可以考虑在走廊贴一些反光标记或改变雷达安装高度让扫描特征更丰富。4.3 EKF 输出频率低于输入频率现象/odometry/filtered话题频率只有 10Hz但轮速和 IMU 都是 50Hz。原因frequency参数设太低或者某个传感器sensor_timeout设太小导致频繁丢弃。解决frequency设为最高输入频率的 1.5 倍左右比如 IMU 50Hzfrequency设 75sensor_timeout设为输入周期的 3 到 5 倍比如 IMU 20ms 周期超时设 0.1s。4.4 tf 树出现两个父节点现象rosrun tf view_frames报错说base_link有两个父节点。原因底盘驱动发了odom → base_link局部 EKF 也发odom → base_link两者冲突。解决底盘驱动只发轮速话题/wheel_odom不发tftf统一由局部 EKF 发布。如果底盘驱动固件里写死了发tf在 launch 里把它的publish_tf参数关掉或者用tf_remove节点过滤。4.5 建图时地图重影墙变成两层现象gmapping 建出的地图里同一面墙出现两条平行线。原因轮速计标定不准转弯时odom的角速度和实际不符雷达帧匹配时把墙“抹”开了。解决重新标定轮速计让车原地转 360 度看odom里 yaw 变化是否接近 360 度差多少就调角速度比例系数。另外检查雷达安装是否松动建图过程中雷达歪了也会重影。5. 让融合更稳的几个进阶技巧与验证习惯5.1 用协方差可视化判断哪路传感器在主导robot_localization 发布的/odometry/filtered里带协方差可以在 RViz 里加PoseWithCovariance显示看椭圆大小。如果椭圆在某方向上特别大说明那个方向观测不足。比如 yaw 协方差一直很大说明 IMU 的vyaw没被正确使用检查imu0_config里vyaw位是否设为true。我一般会在调试阶段把协方差显示打开跑一圈看哪个方向椭圆鼓起来针对性调那一路的权重。5.2 用 rosbag 回放做参数对比调 EKF 参数最忌讳每次改完都实车跑。正确做法是录一段包含/wheel_odom、/imu/data、/scan、/amcl_pose的 bag然后离线回放调参# 录包 rosbag record -O localization_test.bag /wheel_odom /imu/data /scan /amcl_pose /tf /tf_static # 回放并启动 EKF rosbag play localization_test.bag --clock roslaunch your_pkg localization.launch回放时用--clock让 ROS 时间跟着 bag 走EKF 的frequency和sensor_timeout才能正确工作。每次改完 YAML 重新启动 EKF对比/odometry/filtered的轨迹和 AMCL 的轨迹看哪个更贴合实际。我习惯把两次回放的轨迹同时画在 RViz 里一眼就能看出参数改好还是改坏。5.3 关键参数速查表参数作用常用值调大后果调小后果frequency滤波频率30~75CPU 占用高输出滞后sensor_timeout传感器超时0.1~0.5响应慢频繁丢数据two_d_mode强制二维true忽略 z/roll/pitch平面车会飘odom0_config轮速取哪些量只取 vx,vyaw取绝对位置会漂取太少观测不足imu0_configIMU 取哪些量只取 vyaw取绝对姿态会漂取太少 yaw 不稳pose0_configAMCL 取哪些量只取 x,y取 yaw 会跳取太少修正弱这张表是我调参时贴在显示器边上的每次改参数先看一眼避免把two_d_mode关了还纳闷车为什么上下跳。5.4 一个我踩过的坑AMCL 和 EKF 抢着发 tf最早跑通建图后我直接启动 AMCL 和 EKFRViz 里车走两步就瞬移一次。查了半天以为是 EKF 参数问题后来rosrun tf view_frames才发现map → odom有两个发布者AMCL 和全局 EKF。AMCL 默认publish_tf是true它发的map → odom和 EKF 发的打架RViz 随机选一个车就跳。把 AMCL 的publish_tf设为false后立刻稳了。这个坑我后来每次配新机器都会先检查一遍成了肌肉记忆。希望帮到你。本文还有配套的精品资源点击获取