ESC
输入关键词搜索文章标题和内容

机器人的"眼睛":ROS2 slam\toolbox+Nav2导航全链路

本文由 linuxROS 整理发布,首发于 linuxros.cn,转载请注明出处。

机器人的"眼睛":ROS2 slam_toolbox+Nav2导航全链路

导读:机器人是怎么知道哪里有墙、哪里有空地的?ROS2 slam_toolbox每秒处理激光雷达1000+次扫描,把数据拼成2D地图,Nav2接过地图用A*规划路径、DWB控制器实时避障。本文用可运行的ROS2配置代码,拆解这条"感知→建图→规划→控制"闭环的核心原理。


一、激光SLAM是怎么工作的

1.1 激光雷达扫描原理

想象你蒙着眼睛在房间里走动,手里有一根可以探测障碍物的拐杖。激光雷达就是这个"智能拐杖"——每秒发射1000+束激光,测量每束光返回的时间,计算障碍物的距离和角度。

激光扫描数据(LaserScan):

sensor_msgs/LaserScan:
  header:                    # 时间戳 + 坐标系
    stamp: 2024-01-01 10:00:00
    frame_id: "laser_frame"

  angle_min: -3.14159       # 扫描起始角度 (-180°)
  angle_max: 3.14159        # 扫描结束角度 (+180°)
  angle_increment: 0.005     # 角度步进 (~0.3°)

  time_increment: 0.00005    # 相邻射线时间间隔
  scan_time: 0.1             # 单帧扫描周期 (10Hz)

  range_min: 0.1             # 有效测距下限 (m)
  range_max: 30.0           # 有效测距上限 (m)

  ranges: [2.3, 1.5, ..., 3.2, 0.8]  # N个距离值
  intensities: [100, 80, ...]           # 回波强度

说明:ranges数组存储N个距离值,N由扫描角度范围和角度分辨率决定:N = (angle_max - angle_min) / angle_increment + 1。以360°扫描、0.5°分辨率计算:N ≈ 721个测量值

1.2 slam_toolbox核心原理

slam_toolbox是ROS2官方维护的2D SLAM库,核心是扫描匹配+图优化:

机器人走一段路后,对比前后两次"看到"的障碍物分布,重叠部分偏移了多少就知道走了多远——这就是扫描匹配。但每步都有小误差,累积起来地图会变形,图优化就是把误差"分摊"到每步,让整体不变形。

输入:激光雷达扫描数据(/scan)
输出:2D栅格地图(OccupancyGrid)+ 机器人位姿(TF变换)
目的:让机器人边移动边构建环境地图,同时知道自己在哪里

flowchart TB subgraph 输入["激光数据流"] L["激光雷达<br/>LaserScan"] --> S["/scan话题"] end subgraph 处理["slam_toolbox处理"] S --> M["PosedScan队列"] M --> P["扫描匹配<br/>Scan Matching"] P --> G["PoseGraph图优化"] G --> O["输出TF变换<br/>map→odom"] end subgraph 建图["地图生成"] P --> K["关键帧选取"] K --> MAP["OccupancyGrid<br/>2D栅格地图"] end subgraph 回环["闭环检测"] G --> L2{"发现闭环?"} L2 -->|"是"| O2["图优化修正"] L2 -->|"否"| G end style S fill:#E3F2FD,stroke:#1976D2 style P fill:#FFF8E1,stroke:#F57C00 style G fill:#F3E5F5,stroke:#7B1FA2 style MAP fill:#E8F5E9,stroke:#388E3C

关键特性:

特性 算法/实现 参考
同步/异步模式 同步:阻塞等待每帧处理完;异步:放入队列后台处理 ROS2消息机制
地图序列化 karto Mapper序列化协议,支持pgm/yaml格式保存加载 karto开源库
持久化建图 加载历史pose-graph,继续扩展地图 Lifelong SLAM
图优化后端 Ceres Solver非线性最小二乘优化 Google ceres-solver

二、slam_toolbox安装与配置

2.1 一键安装

# ROS2 Jazzy安装
sudo apt install -y ros-jazzy-slam-toolbox

# 验证安装
ros2 pkg list | grep slam_toolbox

2.2 核心参数配置

# slam_params.yaml
slam_toolbox:
  # 建图模式
  mode: "mapping"                      # mapping | localization
  sync: false                          # true=同步, false=异步

  # 地图参数
  resolution: 0.05                     # 5cm/格(室内推荐)
  max_laser_range: 25.0                # 最大探测距离(米)

  # 优化参数
  transform_timeout: 0.2               # TF超时(秒)
  map_update_interval: 5.0              # 地图刷新频率(秒)

  # 扫描匹配
  minimum_time_interval: 0.5            # 最小扫描间隔(秒)
  scan_buffer_size: 100                 # 扫描缓冲区大小

  # 闭环检测
  loop_search_maximum_distance: 15.0   # 闭环搜索范围
  loop_closure_enabled: true            # 启用闭环

2.3 启动与地图保存

# 终端1:启动Gazebo仿真
export TURTLEBOT3_MODEL=waffle
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py

# 终端2:启动SLAM建图
ros2 launch slam_toolbox online_async_launch.py

# 终端3:保存地图
ros2 run nav2_map_server map_saver_cli -f my_map

说明:执行后会生成两个文件:

来自 linuxros.cn · linuxROS
  • my_map.pgm:地图图片(灰度图,白色=可通行区域,黑色=障碍物)
  • my_map.yaml:地图配置文件(分辨率、原点坐标、occupied_thresh等)
  • 导航必须加载yaml文件,PGM图片由yaml引用

3.1 四大核心组件

Nav2是ROS2官方导航框架,包含:

拿到地图后,机器人需要三步:1)知道自己在哪(定位)→ 2)规划怎么走(全局规划)→ 3)边走边躲避障碍物(局部控制)。Nav2就是把这三步封装成可复用的组件库。

组件 功能 核心算法
map_server 提供地图数据 加载pgm+yaml
amcl 定位 粒子滤波
planner_server 全局路径规划 SmacPlanner (Hybrid A*)
controller_server 局部轨迹跟踪 DWB Controller(Dynamic Window Based)
flowchart LR subgraph 感知["感知层"] S["/scan"] --> A["amcl定位"] end subgraph 规划["规划层"] A -->|"位姿"| P["SmacPlanner A*"] P --> P2["/plan 路径"] end subgraph 控制["控制层"] P2 --> C["DWB控制器"] C --> C2["/cmd_vel 速度"] end subgraph 执行["执行层"] C2 --> R["机器人移动"] R --> S end style S fill:#E3F2FD,stroke:#1976D2 style A fill:#FFF8E1,stroke:#F57C00 style P fill:#F3E5F5,stroke:#7B1FA2 style C fill:#E8F5E9,stroke:#388E3C

3.2 Nav2参数配置

# nav2_params.yaml
nav2:
  # 全局规划器
  planner_server:
    ros__parameters:
      planner_plugins: ["GridBased"]
      GridBased:
        plugin: "nav2_smac_planner/SmacPlanner2D"
        tolerance: 0.5                    # 目标容差(米)
        max_iterations: 1000000         # 最大迭代次数
        allow_unknown: true              # 允许穿越未知区域

  # 局部控制器
  controller_server:
    ros__parameters:
      controller_plugins: ["FollowPath"]
      FollowPath:
        plugin: "dwb_core/DWBFollower"
        min_vel_x: 0.0
        max_vel_x: 0.5                  # 最大线速度
        max_vel_theta: 1.0              # 最大角速度

  # 代价地图
  global_costmap:
    ros__parameters:
      inflation_radius: 0.55            # 障碍物膨胀半径(防止机器人贴墙/撞障碍物)
      cost_scaling_factor: 1.0

  local_costmap:
    ros__parameters:
      width: 100                         # 局部地图宽
      height: 100                       # 局部地图高
      inflation_radius: 0.35            # 膨胀区(局部地图可设较小值,提高通行效率)

3.3 启动导航

ros2 launch nav2_bringup navigation_launch.py \
  use_sim_time:=true \
  map:=/absolute/path/to/my_map.yaml   # ⚠️ 建议使用绝对路径,避免仿真/真机报错

提示:相对路径在仿真环境下可能正常,但在真机部署或不同工作目录下会找不到地图文件。


四、全链路数据流

flowchart TB subgraph Gazebo["Gazebo仿真"] G["机器人模型"] --> L["激光雷达"] L --> S["/scan LaserScan"] end subgraph SLAM["SLAM建图"] S --> T["slam_toolbox"] T --> M["/map OccupancyGrid"] T --> TF["/tf map→odom"] end subgraph Nav2["Nav2导航"] M --> MS["map_server"] S --> AMCL["amcl定位"] AMCL -->|"位姿"| PL["SmacPlanner"] AMCL -->|"位姿"| CT["DWB控制器"] PL --> P["/plan 路径"] CT --> V["/cmd_vel 速度"] V --> G end subgraph 保存["地图保存"] M --> SAVER["map_saver"] SAVER --> PG["my_map.pgm"] SAVER --> YM["my_map.yaml"] end style G fill:#E3F2FD,stroke:#1976D2 style T fill:#FFF8E1,stroke:#F57C00 style MS fill:#E3F2FD,stroke:#1976D2 style PL fill:#F3E5F5,stroke:#7B1FA2 style CT fill:#E8F5E9,stroke:#388E3C

五、实战踩坑

6.1 建图阶段常见问题

问题 原因 解决方案
/map话题无数据 激光话题名不对 ros2 topic list \| grep scan 确认话题名
TF变换超时 transform_timeout太小 增大至0.5s
地图严重漂移 激光帧率不稳定 检查激光雷达驱动,降低scan_buffer_size
地图重影 ICP匹配失败到局部最优 降低max_laser_range,减少远距离噪声

调试命令:

# 检查激光话题是否正常发布
ros2 topic echo /scan --once

# 查看TF树是否完整
ros2 run tf2_tools view_frames

# 检查slam_toolbox节点状态
ros2 node list | grep slam
ros2 topic list | grep map

6.2 导航阶段常见问题

问题 原因 解决方案
规划失败(Failed to get plan) 目标点在障碍物/未知区域 检查inflation_radius,确保目标不在膨胀区内
机器人原地转圈 AMCL粒子未收敛 在已知区域多移动,让粒子收敛
导航路径不合理 全局规划器参数问题 调整tolerance和max_iterations
局部避障失效 DWB参数过激进 降低max_vel_x,增大acc_lim_x

AMCL调试:

# 查看粒子云分布(粒子越集中越好)
ros2 topic echo /particle_cloud --once

# 手动设置初始位姿(当机器人位置已知时)
ros2 topic pub /initialpose geometry_msgs/PoseWithCovarianceStamped ...

6.3 内存与性能优化

VMware 7.7GB内存限制下,建议:

# slam_params.yaml 优化
slam_toolbox:
  scan_buffer_size: 50          # 减小缓冲区,节省内存
  map_update_interval: 3.0      # 降低地图刷新频率

# Nav2优化
planner_server:
  ros__parameters:
    max_iterations: 500000      # 限制迭代次数

六、技术对比

2D激光SLAM+Nav2导航全链路核心是**"感知→建图→定位→规划→控制"**闭环:

  • slam_toolbox:扫描匹配+图优化,输出2D栅格地图
  • map_server:加载已保存地图
  • amcl:粒子滤波定位
  • SmacPlanner:Hybrid A*全局规划
  • DWB(Dynamic Window Based):经典DWA算法的工程优化版,局部避障控制

这套方案成熟稳定,是室内移动机器人的标配。代码量不大,关键是理解各模块的输入输出关系。


看完不点赞?机器人明天就撞墙。看完不转发?Bug跟你到白头。推荐给朋友,一起掉坑到永久。评论区见,谁家的AGV最稳?

版权声明

作者linuxROS
协议本作品采用 CC BY-NC-SA 4.0 许可协议:署名-非商业性使用-相同方式共享
关注欢迎关注微信公众号 linuxROS,获取更多机器人 / 嵌入式 / Linux 干货
返回首页