简介:面向机器人技术学习者与研究者的技术综述文档,系统梳理移动机器人导航与定位领域的关键技术与研究框架。PDF全文围绕导航、定位、路径规划、实时控制四大核心模块展开,明确区分自主与半自主导航、绝对与相对定位、静态与动态路径规划、开环与闭环控制等概念,并覆盖工业、服务、医疗等应用场景。文中还引入“我现在何处?我要往何处去?要如何到该处去?”三个基本问题,阐述车体定位、多传感器信息融合、障碍识别与路径规划等核心环节的研究目标。资源共1个PDF文件,压缩包仅205KB,轻量便携,属于早期学术文献,适合快速把握移动机器人导航定位基本体系的入门读者。已有517人学习下载,可用于课程报告、技术调研或知识扫盲参考。
1. 移动机器人导航与定位:三个问题一次讲清
“移动机器人导航与定位技术”是个容易被低估的领域。很多人以为装好激光雷达、跑通 ROS2 导航就算完成,结果一上真机,地图对不齐、定位跳变、路径来回抽动,问题几乎全出在最底层的“我在哪”上。移动机器人导航与定位实际上只回答三个问题:我在哪、去哪里、怎么安全到达。定位解决第一问,建图解决环境表示,全局路径规划与局部避障解决后两问。三个环节必须闭合成环:定位一旦漂移,路径规划和避障都会基于错误位姿做判断,动作越果断,跑偏越快。这篇按定位、地图、导航、验证的顺序,给出一套能在 ROS2 环境里落地的方案、参数和边界条件。适合正从仿真转真机,或者被漂移和碰撞反复折磨的机器人工程师。
2. 定位层:从惯性导航到 ROS2 滤波融合,先回答“我在哪”
2.1 为什么单靠里程计会漂:轮式里程计与惯性导航的互补关系
轮式里程计通过轮子转速和转向角积分出位移,短时间精度尚可,时间一长,打滑、轮胎半径误差、地面不平等都会让累积误差越来越明显。IMU 提供高频加速度和角速度,直接积分能解算姿态和速度,但静止时的加速度计噪声和陀螺零偏会让位置误差随时间发散。惯性导航的优点在短时高频,缺点在长时漂移。卡尔曼滤波与惯性导航的结合,正是用运动模型做“预测”,用外部位姿观测做“更新”,把两者的强项拼起来。
移动机器人定位的常见做法是:用轮式里程计或视觉/激光里程计作为预测源,用 IMU 姿态修正航向,用激光雷达匹配、GNSS 或 UWB 观测修正位置。这样既保留高频输出,又不会让误差无限累积。要注意的是,这里的“卡尔曼滤波”在机器人领域一般指扩展卡尔曼滤波,因为轮式里程计和 IMU 的模型都包含姿态旋转项,直接套线性卡尔曼会丢项。EKF 的核心思想是在当前状态点求雅可比,把非线性系统近似成线性系统,再按标准更新流程迭代。
2.2 用 ROS2 的 robot_localization 做多传感器 EKF 融合
ROS2 环境里最常用的就是robot_localization包。它提供ekf_node和ukf_node,可以同时接入里程计、IMU、GNSS、UWB 等消息,输出一个融合后的 odometry。下面是一个 2D 差速机器人的基本配置,按 YAML 方式组织。
# ekf.yaml -- ROS2 robot_localization 常见配置 frequency: 50.0 sensor_timeout: 0.2 two_d_mode: true differential: false odom0: /odom odom0_config: [true, true, false, false, false, true, false, false, false, false, false, false, false, false, false] odom0_queue_size: 10 imu0: /imu/data imu0_config: [false, false, false, true, true, true, false, false, false, true, true, false, false, false, false] imu0_queue_size: 10 process_noise_covariance: [0.05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.06, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.04, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.02]odom0_config和imu0_config都是 15 维布尔向量,固定顺序是 x、y、z、roll、pitch、yaw、vx、vy、vz、vroll、vpitch、vyaw、ax、ay、az。这里对/odom只启用了 x、y 和 yaw,其余维度交给 IMU 的姿态观测。two_d_mode: true会把 z、roll、pitch 直接置零,适合室内地面机器人。differential: false表示允许 IMU 直接提供绝对朝向;如果机器人没有磁力计或方向观测,建议改成true,这样算法只取角速度做相对更新,避免室外磁干扰把航向拉飞。
process_noise_covariance对角线上的值代表运动模型的噪声大小,调得越大,滤波器越愿意相信传感器观测,输出跳动会变明显;调得太小,滤波结果会拖尾滞后。最常见的坑是 IMU 的时间戳没有同步到/odom的时钟域,导致sensor_timeout一直触发丢弃。启动后要用ros2 topic echo /odometry/filtered观察协方差是否收敛,协方差不再持续增大说明融合状态正常。
2.3 粒子滤波与 AMCL:已知地图下的重定位兜底
EKF 适合预测连续位姿,但无法处理“机器人到底在地图哪个位置”这种多峰分布问题。粒子滤波通过一组带权重的假设位姿逼近真实后验分布,可以同时保留多个可能位置,再靠激光匹配和重采样收敛到高概率区域。ROS2 的nav2_amcl模块就是自适应蒙特卡洛定位的实现,适合在已知栅格地图上做重定位和长期定位。
AMCL 的参数需要结合底盘噪声和激光特性调整,下表的几个参数是上线前必调的。
| 参数 | 常用范围 | 作用 | 调参要点 |
|---|---|---|---|
| min_particles / max_particles | 500 / 2000 | 粒子数上下限 | 粒子太少容易丢定位,太多会占 CPU |
| update_min_d / update_min_a | 0.2 m / 0.5 rad | 最小移动/旋转阈值 | 小于阈值不触发激光更新,避免原地空转 |
| resample_interval | 1 | 每几次激光更新重采样 | 数值大收敛慢,数值小粒子容易退化 |
| laser_min_range / laser_max_range | 0.1 / 10.0 | 激光有效距离 | 过滤掉雷达罩和远距离噪点 |
| odom_alpha1 ~ odom_alpha5 | 0.05 ~ 0.2 | 里程计噪声模型 | 对应旋转/平移误差,需按底盘实测调整 |
启动 AMCL 的常见方式是通过nav2_bringup的 localization launch,也可以单独执行ros2 run nav2_amcl amcl。它订阅/map、/scan、/odom和 TF,输出map -> odom的坐标变换。如果 TF 树不完整,AMCL 会在启动后一直“等待有效数据”。排查顺序是先看ros2 run tf2_echo map odom是否有输出,再看激光话题时间戳是否滞后。
2.4 激光配准与退化环境:NDT、ICP 和复合定位
当机器人使用 3D 雷达或对长距离定位有更高要求时,点云配准比 2D 激光匹配更常用。NDT 和 ICP 都能把当前帧点云对齐到已有地图,差异在于 NDT 先把参考点云栅格化成正态分布,匹配时更快;ICP 直接找最近点,精度高但容易陷局部极值。常见做法是把 NDT 作为里程计因子,和 IMU、轮式里程计一起送进 EKF。nav2 导航使用 3d 雷达时,通常需要先把点云降维成 2D LaserScan 或直接在代价地图 obstacle layer 订阅 PointCloud2。这个降维过程要小心,高度过滤区间设得太宽会把车身附近的坡道误判为障碍物,设得太窄又会漏掉低矮路肩。
退化环境是定位失效的高发地。长走廊两侧平行,点云匹配在走廊方向没有约束,NDT 会给出一个看似平滑但实际漂移的结果。电梯口、空旷停车场、隧道里也一样。遇到这类场景,只用激光雷达是不够的,至少需要一路航向观测:IMU 的 yaw 或磁力计提供绝对航向,再加一路里程计约束平移。预算允许时,UWB 或二维码定位可以提供绝对坐标修正,和 EKF 融合后能明显抑制退化漂移。上真机前,我一般会在退化场景先录制 bag,离线回放不同定位策略,确认协方差不会在走廊中段突然爆掉,再决定是否改动传感器配置。
3. 建图与地图表示:从栅格地图到八叉树地图导航的选型
3.1 栅格地图的概率更新与八叉树地图导航的优势
2D 占用栅格地图把环境切成长方形格子,每个格子保存被占用的概率或对数几率。激光命中时格子概率增加,激光穿过的自由空间概率降低。这个更新过程用对数几率的加法实现:l = l + l_measurement,输出概率为p = 1 / (1 + exp(-l))。直接用概率乘法会很快饱和到 0 或 1,丢失不确定性信息。下面的 Python 片段演示了这个等效更新过程。
# 简化的八叉树/栅格概率更新 import math def logit(p): return math.log(p / (1 - p)) def sigmoid(l): return 1.0 / (1.0 + math.exp(-l)) node_log_odds = 0.0 # 初始未知,概率 0.5 hit = 0.7 # 激光命中时观测概率 miss = 0.4 # 激光穿过的观测概率 node_log_odds += logit(hit) # 命中一次 print(sigmoid(node_log_odds)) # 约 0.7 node_log_odds += logit(miss) # 之后被一次穿透观测覆盖 print(sigmoid(node_log_odds)) # 低于 0.7,体现证据叠加这段代码里hit代表某次激光打到物体表面,miss代表激光束穿过的空白区域。八叉树地图导航的核心优势是用递归八叉树存储稀疏 3D 空间,分辨率可调,且支持概率更新,内存占用远小于等分辨率的 3D 栅格数组。对地面机器人,2D 栅格地图已经够用;对无人机或需要跨越坡道、限高杆的移动机器人,3D 八叉树地图是更合理的导航地图。ROS 生态中octomap_server会把 3D 点云增量构建成八叉树,并提供 OctoMap 话题。需要注意,Nav2 原生规划器默认在 2D 代价地图上工作,八叉树地图要接入 Nav2 通常得先把障碍物投影到 2D 层面,或者在局部规划器里直接查体素占用。
3.2 建图方案选型:GMapping、Cartographer 与 SLAM Toolbox
ROS2 环境下主流建图链路有三种,选型取决于场景大小、传感器质量和是否需要回环。GMapping 是粒子滤波加激光匹配的经典方法,适合几十平米内的小场景,计算快,但缺少显式回环检测,在长走廊或多次往返时地图容易裂开。Cartographer 使用子图与回环约束,能构建大场景且支持 2D 和 3D 点云,代价是配置项多,调参周期长。SLAM Toolbox 把图优化和栅格地图结合,支持在线建图和长期定位复用,是目前 ROS2 里最省心的一个选择。
| 方案 | 核心思想 | 推荐场景 | 主要短板 |
|---|---|---|---|
| GMapping | 粒子滤波 + scan matching | 小面积、简单室内 | 回环差,地图容易错位 |
| Cartographer | 子图 + 回环检测 | 大面积、复杂结构 | 参数多,计算和内存消耗大 |
| SLAM Toolbox | 图优化 + 栅格建图 | 长期建图、定位复用 | 对激光帧率敏感,需要好数据 |
启动 SLAM Toolbox 的常见命令如下,更适合实时建图的是 online_async 模式,建图过程中不会因为扫描匹配把界面卡住。
ros2 launch slam_toolbox online_async_launch.py slam_params_file:=my_mapper_params.yaml参数文件里需要关注mode: mapping、min_laser_range、max_laser_range、map_update_interval和use_scan_matching。map_update_interval控制多久发布一次地图,太频繁会拖垮 RVIZ,太稀疏会让人看不到实时效果。建图时机器人转速要慢,角速度超过 0.5 rad/s 时激光点云会变形,SLAM 工具能处理一部分,但超了阈值仍然会出现重影。这个教训在真机调试中几乎都会遇到。
3.3 Nav2 地图与代价地图层的配置:从地图到避障约束
Nav2 的地图加载由map_server完成,地图文件是栅格图加一个 YAML 描述文件。occupied_thresh和free_thresh决定灰度值如何映射到占用状态,origin和resolution必须和建图时一致,否则导航直接把人放到地图外。代价地图则分为 static layer、obstacle layer 和 inflation layer。static layer 负责静态地图,obstacle layer 负责实时传感器障碍物,inflation layer 负责把障碍物向外膨胀,生成有梯度的代价区域。下面的costmap_common.yaml是接 2D 激光雷达的最小配置。
obstacle_layer: enabled: true obstacle_range: 3.0 raytrace_range: 3.5 observation_sources: scan scan: data_type: LaserScan topic: /scan marking: true clearing: false lethal_cost_threshold: 100 track_unknown_space: false inflation_layer: enabled: true cost_scaling_factor: 3.0 inflation_radius: 0.8 static_layer: enabled: true map_topic: /mapobstacle_range是传感器能确认障碍物的最大距离,raytrace_range是传感器能清除自由空间的射线距离。marking表示把命中点标为障碍,clearing表示允许穿过的点被清空。对于 3D 雷达,把observation_sources改成 PointCloud2 类型即可,但必须先做高度裁剪。常见做法是保留雷达安装高度以上 0.2 米到以下 0.3 米之间的点云,其余点云滤掉。不做这个过滤,地面点、车顶扫描线都会被算成障碍物,导航会频繁急停。
4. 导航层:全局路径与局部避障的工程化调参
4.1 全局规划器选型:NavFn、SmacPlanner 与混合 A*
Nav2 中全局路径规划器有 NavFn 和 SmacPlanner 两类。NavFn 是基于网格的 A* / Dijkstra 实现,简单稳定,适合差速底盘和自由空间较大的环境。SmacPlanner 提供更接近业务的插件:SmacPlanner2D对应 2D 网格 A*,SmacPlannerHybrid是混合 A*,支持阿克曼最小转弯半径约束,SmacPlannerStateLattice适合差速和全向底盘的状态栅格搜索。对于阿克曼底盘,直接用 NavFn 规划的路径往往有尖角,底盘根本跟随不上,必须用混合 A*。下面是一个 SmacPlanner2D 的配置片段。
planner_server: ros__parameters: expected_planner_frequency: 1.0 planner_plugins: ["GridBased"] GridBased: plugin: "nav2_smac_planner/SmacPlanner2D" tolerance: 0.5 downsample_costmap: true allow_unknown: true smooth_path: true w_eucl_cost: 1.0 w_heuristic_cost: 0.5 w_costmap: 2.0tolerance是目标点被障碍物占据时允许搜索附近自由点的半径。downsample_costmap可以降低大地图上的搜索耗时,但会丢失窄通道细节。w_costmap控制路径距离障碍物的偏好,调太大路径会绕远但安全,调太小路径贴墙走,局部规划器稍一抖动就会刮蹭。实际调参时我一般先固定w_eucl_cost,只调w_costmap,观察全局路径是否在门洞、墙边出现来回折线。折线通常不是算法问题,是代价地图在障碍物边缘分辨率不够。
4.2 局部规划器:DWA、TEB 与速度采样参数
局部规划器负责把全局路径转成实时速度指令。DWB(DWA 在 Nav2 中的实现)在速度空间随机采样多组线速度与角速度,用轨迹评价函数选一条最优。TEB 把一段轨迹建模成带时间戳的弹性带,通过优化时间间隔获得平滑轨迹,适合阿克曼底盘,但参数不当会在窄通道里抖动。MPPI 则基于随机采样和代价函数滚动优化,能自然处理多目标约束,但计算量大,调试也更复杂。下面的对比表可以辅助选型。
| 局部规划器 | 适用底盘 | 优势 | 常见问题 |
|---|---|---|---|
| DWB/DWA | 差速、全向 | 模型简单、稳定 | 轨迹不平滑,阿克曼跟随困难 |
| TEB | 阿克曼、差速 | 轨迹平滑、时间最优 | 窄通道抖动,靠墙易震荡 |
| MPPI | 任意 | 多约束、可预测未来 | 参数依赖高,CPU 开销大 |
DWB 的参数设计直接影响运动品质。一个最小可用的本地规划器配置如下。
FollowPath: plugin: "nav2_dwb_controller/DWBLocalPlanner" max_vel_x: 0.5 min_vel_x: -0.1 max_vel_theta: 1.0 acc_lim_x: 2.5 acc_lim_theta: 3.2 xy_goal_tolerance: 0.2 yaw_goal_tolerance: 0.1 sim_time: 2.0 vx_samples: 20 vtheta_samples: 20 path_distance_bias: 4.0 goal_distance_bias: 1.5 occdist_scale: 0.02sim_time表示预测未来轨迹的时间窗口,窗口越短制动反应越快,但容易在高速时刹车距离不够;窗口太长轨迹计算量大,且遇到突然出现的障碍反应迟钝。path_distance_bias和goal_distance_bias的比值需要平衡。occdist_scale是障碍物代价权重,设成 0 会导致机器人贴着障碍物走,设得过大又会认为所有接近障碍的轨迹都不可行,窄门过不去。TEB 里常见的关键参数是dt_ref、min_obstacle_dist和weight_obstacle。min_obstacle_dist代表轨迹上每一点到障碍物的最小允许距离,小于这个值会被当作硬约束。
4.3 膨胀层、无解区域与恢复行为:导航失败的真正原因
很多导航失败不是规划器的问题,而是膨胀代价把可行空间吞噬了。inflation_radius是代价从障碍物向外衰减的最大半径,cost_scaling_factor控制衰减曲线的陡峭程度。同一个 1 米宽的过道,如果inflation_radius设为 0.8,左右两侧各膨胀 0.4 米后,过道中间的安全宽度只剩 0.2 米,全局规划器很可能判定无解。此时机器人会在过道前反复重规划,甚至触发恢复行为。判断这个问题的办法是在 RVIZ 里打开代价地图层,看可行区域是否已经变成一条细线。
Nav2 恢复行为默认是清理代价地图、原地旋转、后退一步。行为树里可以配置多次恢复的失败容忍度,比如两次旋转失败后直接宣布导航失败。对移动机器人导航与定位的整体稳定性来说,恢复行为只是兜底,不是救火工具。若真机上频繁出现“恢复-规划-再恢复”的循环,优先查两件事:地图是否包含动态物体残留,以及膨胀参数是否与车身实际宽度匹配。地图里的幽灵障碍物只要出现一次,后续每次导航都会被当作硬墙。
5. 最后一公里验证:用离线回放和协方差判断定位是否可用
5.1 真机前先回放 bag:定位参数别在实物上盲目试
最稳妥的验证方式不是把机器人推到现场直接跑导航,而是先录制传感器数据,再用 ROS2 bag 离线回放。录制时至少要包含/scan、/odom、/imu/data、/map、/tf和/tf_static,机器人静止几秒后绕环境走一圈,回到起点。然后离线启动定位节点,把use_sim_time设为true,用ros2 bag play --clock驱动时间轴。此时可以通过ros2 run tf2_tools view_frames检查 TF 树,或者用ros2 run tf2_echo map odom观察map -> odom的变换。
map -> odom的变换在机器人静止时应该稳定在平移微小抖动范围内。如果这个变换随时间明显漂移,说明里程计或 IMU 的噪声参数不准,或者外参标定有误。另一个容易忽略的环节是时间同步。机器人导航要求所有传感器消息时间戳对齐,使用 ros2 bag 回放时尤其要注意/clock是否发布,否则定位节点会因为时间戳跳变直接丢弃数据。
5.2 用定位协方差做自动化判定
EKF 融合后的PoseWithCovarianceStamped协方差能直接反映定位质量。协方差矩阵对角线代表 x、y、yaw 的不确定度,膨胀到一定程度就该触发重定位,而不是继续带着错误位姿导航。生产环境可以写一个简单监控脚本:订阅位姿话题,当 x 或 y 的方差超过设定阈值时,发布一个/recover/trigger消息,让机器人在原地旋转重新建粒子云。这个方法比依赖导航失败反馈更早一步,能显著减少碰撞风险。在施工现场,我一般设定机器人单方向平移方差超过 0.05 平方米或航向方差超过 0.1 平方弧度就执行重定位,具体阈值按激光帧率会收紧或放宽。
定位收敛状态还有一个直观判据:静止时把 AMCL 粒子云显示在 RVIZ 里,所有粒子应该在机器人真实位置附近聚成一个直径 0.3 米以内的团。若粒子呈线状分布,多半是激光匹配在某个方向上没有约束,此时检查周围环境是否过于空旷;若粒子散开成几团,说明初始位姿错得太远或地图有多层重影。处理办法是先手动给定初始位姿,再验证移动过程协方差是否回落。稳定后再接规划器,整套移动机器人导航与定位系统才有可能在真实场景里长期工作。
本文还有配套的精品资源,点击获取