TurtleBot3 ROS2 避障仿真

安装

二进制安装

sudo apt-get install ros-humble-turtlebot3-gazebo ros-humble-turtlebot3-navigation2 ros-humble-turtlebot3-cartographer
export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/opt/ros/galactic/share/turtlebot3_gazebo/models
export TURTLEBOT3_MODEL=burger

源码安装

mkdir -p ~/turtlebot3_ws/src
cd ~/turtlebot3_ws/src/
# 如果没有`humble-devel`,则试试`humble` simulations humble 缺东西,用main
git clone -b humble-devel https://github.com/ROBOTIS-GIT/DynamixelSDK.git
git clone -b humble-devel https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git
git clone -b humble-devel https://github.com/ROBOTIS-GIT/turtlebot3.git

#git clone -b humble-devel https://github.com/ROBOTIS-GIT/turtlebot3_simulations.git
git clone -b humble https://github.com/ROBOTIS-GIT/turtlebot3_simulations.git
#编译
colcon build --symlink-install

建议~/.bashrc 添加:

_turtle3init() {
  source ~/workspace/simulation_ws/install/setup.bash
  export ROS_DOMAIN_ID=30 #TURTLEBOT3
  export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:~/workspace/simulation_ws/src/turtlebot3/src/turtlebot3_simulations/turtlebot3_gazebo/models
  export TURTLEBOT3_MODEL=burger
}
alias turtle3init='ros2init && _turtle3init && source ~/.bashrc'

而后,打开新终端时,通过执行turtle3init可进行turtlebot3相关包及ros2的环境初始化。

先建图后避障步骤

1 SLAM 建图 (Creating a Map using SLAM)

建图的目标是让机器人在未知环境中移动,同时利用其传感器(如激光雷达)数据构建一张环境地图。

  1. 启动 Gazebo 仿真环境
    打开一个终端,设置机器人模型(例如 burgerwaffle_pi)并启动 Gazebo 世界:

    ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
    

    Gazebo 会启动并显示一个带有 TurtleBot3 和一些障碍物的世界。

    对应的tf-tree:ros2 run rqt_tf_graph rqt_tf_graph
    在这里插入图片描述
    如下报错:
    - PackageNotFoundError: "package 'ros_gz_sim' not
    - PackageNotFoundError: "package 'ros_gz_brdige' not
    安装对应sudo apt-get install ros-humble-ros-gz-bridge ros-humble-ros-gz-sim

  2. 启动 SLAM 节点
    打开另一个终端,同样设置机器人模型,然后启动 SLAM 节点(例如使用 cartographer):

    ros2 launch turtlebot3_cartographer cartographer.launch.py use_sim_time:=True
    

    use_sim_time:=True 参数告诉 ROS 2 使用 Gazebo 的仿真时间。同时,Rviz2 会打开以可视化正在创建的地图。

  3. 控制机器人移动以构建地图
    再打开一个终端,启动键盘控制节点:

    ros2 run turtlebot3_teleop teleop_keyboard
    

    根据终端提示,使用键盘控制 TurtleBot3 在 Gazebo 环境中缓慢移动(移动太快可能导致建图效果不佳),尽可能探索整个环境,直到 Rviz2 中的地图绘制完整。

  4. 保存地图
    当地图构建完成后,打开一个新的终端,使用 map_saver 保存地图:

    ros2 run nav2_map_server map_saver_cli -f ~/map  # -f 后接地图文件的保存路径和名称
    

    此命令会在指定目录(如用户主目录 ~)下生成两个文件:map.pgm(地图图像)和 map.yaml(地图配置文件)。请记下 map.yaml 文件的完整路径,导航时需要用到。

  5. 关闭所有节点
    完成建图后,在之前启动 teleop_keyboard, cartographer, gazebo 的终端中分别按 Ctrl+C 退出所有节点。

2 导航与避障 (Navigation and Obstacle Avoidance)

导航阶段的目标是让机器人利用上一阶段创建的地图,进行自主定位并规划路径到指定目标点,同时能避开动态障碍物。

  1. 再次启动 Gazebo 仿真环境
    (确保 TURTLEBOT3_MODEL 设置一致)在终端中运行:

    ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
    

    在这里插入图片描述

  2. 启动 Nav2 导航系统
    打开另一个终端,确保已指定机器人模型,并运行导航启动文件,注意提供正确的地图路径(即刚才保存的 map.yaml 文件的路径):

    ros2 launch turtlebot3_navigation2 navigation2.launch.py use_sim_time:=True map:=/home/xx/map.yaml #建议为为绝对路径
    

在这里插入图片描述

此命令会启动 Nav2 的所有相关节点,包括全局和局部规划器、行为服务器等,并在 RViz2 中打开可视化界面。

在启动 turtlebot3_navigation2 navigation2.launch.py 后,在RViz2中看到地图和机器人模型时,导航并没有真正开始。因为此时AMCL还不知道机器人在地图中的初始位置,因此无法发布正确的 map -> odom 变换。

  1. 设置初始位姿(初始化)
    在 RViz2 中:
    • 点击工具栏中的 “2D Pose Estimate” 按钮。

需要通过RViz2中的 “2D Pose Estimate” 按钮来告诉AMCL机器人大概的初始位置和朝向:

  1. 点击RViz2工具栏中的 “2D Pose Estimate” 按钮。
  2. 在地图上点击机器人实际所在的大致位置,并拖动鼠标以指定机器人的朝向
  3. 操作后,会看到一些分散的绿色小箭头(粒子),这表示AMCL正在尝试定位。随着机器人移动或传感器数据输入,这些粒子会逐渐收敛到机器人真实的位置上。
  • 在地图上点击并拖动,指出机器人当前大致的位置和朝向。通过此操作,为自适应蒙特卡罗定位 (AMCL) 算法提供了初始估计。
  1. 设置导航目标并观察避障行为
    在 RViz2 中:
    • 点击工具栏中的 “2D Nav Goal” 按钮。
    • 在地图上点击希望机器人到达的目标位置,并拖动鼠标以指定机器人的最终朝向。
      之后,Nav2 便会开始工作。会在 RViz2 中看到:
    • 全局路径(通常显示为灰色或浅色线
    • 局部代价地图(显示机器人周围的障碍物信息)
    • 局部规划路径动态调整的路径,通常显示为绿色其他醒目颜色的线)
    • 如果 Gazebo 世界中有动态障碍物(如移动的物体),可以观察到 TurtleBot3 如何通过局部规划实时避开它们。
      在这里插入图片描述

边建图后避障步骤

  1. 启动 Gazebo 仿真环境
    打开一个终端,设置机器人模型(例如 burgerwaffle_pi)并启动 Gazebo 世界:
    ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
    
  2. 启动 SLAM 节点
    打开另一个终端,同样设置机器人模型,然后启动 SLAM 节点(例如使用 cartographer):
    ros2 launch turtlebot3_cartographer cartographer.launch.py use_sim_time:=True
    
  3. 启动Nav2 节点
    打开另一个终端,确保已指定机器人模型,并运行导航启动文件,注意提供正确的地图路径(即刚才保存的 map.yaml 文件的路径):
    ros2 launch turtlebot3_navigation2 navigation2.launch.py use_sim_time:=True 
    
    而后同样使用 RViz2 ,点击工具栏中的 “2D Nav Goal” 按钮。机器人开始边避障边建图。

Tips

遇到问题

查看详细日志: 启动导航launch文件时,添加--screen参数可以让所有节点的输出都显示在终端上,便于查看更详细的错误信息:
bash ros2 launch turtlebot3_navigation2 navigation2.launch.py use_sim_time:=True map:=/path/to/your/map.yaml --screen

问题1:

ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
卡在Waiting for service /spawn_entity, timeout = 30

解决: 或者是慢,多等会

其他解决参考:
检查 /spawn_entity 服务是否可用:
ros2 service list | grep spawn_entity

Gazebo must be launched with the libgazebo_ros_factory.so plugin to enable the /spawn_entity service.
Solution: Launch Gazebo with the correct plugins:

gazebo -s libgazebo_ros_init.so -s libgazebo_ros_factory.so

问题2: 卡在:

ros2 run nav2_map_server map_server --ros-args --param yaml_filename:=/home/nin/maps/tb3map.yaml
[INFO] [1758447137.857729744] [map_server]:
map_server lifecycle node launched.
Waiting on external lifecycle transitions to activate
See https://design.ros2.org/articles/node_lifecycle.html for more information.
[INFO] [1758447137.857788968] [map_server]: Creating

解决: 手动触发生命周期转换(用于测试和调试)

在两个新的终端中,分别执行以下命令:

  1. 配置 (Configure) 节点,将其从未配置状态转换为非活动状态,进行必要的初始化:

    ros2 lifecycle set /map_server configure
    

    成功执行后,终端应显示 Transitioning successful

  2. 激活 (Activate) 节点,将其从非活动状态转换为活动状态,开始发布地图数据:

    ros2 lifecycle set /map_server activate
    

    成功执行后,终端应显示 Transitioning successful,并且 map_server 应该会开始发布 /map 话题。

其他的各种问题: 世界中没有模型等通用尝试方法

  • 尝试清理Gazebo进程并重启
    有时可能是由于Gazebo进程没有完全退出导致的。

    killall gzserver
    killall gzclient
    # 然后再重新启动launch文件
    
  • 检查 GAZEBO_MODEL_PATHGAZEBO_RESOURCE_PATH 环境变量是否包含正确路径:

    echo $GAZEBO_MODEL_PATH
    echo $GAZEBO_RESOURCE_PATH
    
  • 查看详细日志
    为了获得更具体的错误信息,尝试以详细模式运行Gazebo,或者查看生成的日志文件。

    • 日志文件位置通常在 ~/.ros/log/ 目录下,根据错误信息中提到的路径查找对应的 .log 文件。
    • 可以尝试直接运行 gazebo --verbose 看Gazebo本身能否正常启动。

问题:LDS_MODEL没有指定时指的是雷达型号
export export LDS_MODEL=rplidars1=rplidars1 rplidars1是可选的,参考官网。


ROS 2 TurtleBot3常用节点

TurtleBot3 的自主导航和避障功能通常由 Nav2 (Navigation2) 系统实现。Nav2 采用分层规划策略:

  • 全局规划器 (Global Planner):负责计算从起点到终点的全局路径,通常基于已知的静态地图(如 SLAM 得到的地图)。常用算法有 A*、Dijkstra 等。
  • 局部规划器 (Local Planner) / 控制器 (Controller):负责执行全局路径,并实时避开未知或动态的障碍物。它接收来自激光雷达等传感器的实时感知数据,并输出速度控制指令 (/cmd_vel)。常用算法有 DWA (Dynamic Window Approach)TEB 等。

全局+局部避障是指在全局路径规划的基础上,局部规划器根据实时传感器信息(如激光雷达)动态调整机器人的运动,以避开未在地图中标注的障碍物(如临时出现的人或物体)。

ROS 2 TurtleBot3常用的launch文件及其节点作用:

Launch 文件 (Package) 主要功能 关键节点/系统 主要作用描述 常用场景/备注
turtlebot3_gazebo/turtlebot3_world.launch.py 启动 Gazebo 仿真环境 gazebo 加载包含障碍物的世界模型和 TurtleBot3 机器人 SLAM、导航算法测试
turtlebot3_gazebo/turtlebot3_house.launch.py 启动房屋环境的 Gazebo 仿真 gazebo 加载一个房屋内部环境的模型和 TurtleBot3 机器人 在更复杂的环境中进行导航测试
turtlebot3_gazebo/turtlebot3_empty_world.launch.py 启动空世界的 Gazebo 仿真 gazebo 加载一个空的世界(仅地面和天空)和 TurtleBot3 机器人 基础功能测试
turtlebot3_cartographer/cartographer.launch.py 启动 Cartographer SLAM 建图 cartographer_node
rviz2
处理激光雷达和里程计数据,实时构建地图
可视化建图过程和传感器数据
需要在启动命令中设置 use_sim_time:=True
turtlebot3_navigation2/navigation2.launch.py 启动 Nav2 导航系统 controller_server
planner_server
behavior_server
bt_navigator
rviz2
局部路径规划与跟踪(DWA 等算法)
全局路径规划(A* 等算法)
处理恢复行为等
执行导航任务的行为树
可视化导航、设置目标、初始化位姿
依赖现有地图,需提供 map.yaml 路径
turtlebot3_teleop/teleop_keyboard.launch.py 启动键盘控制节点 teleop_keyboard 发布 /cmd_vel 话题控制机器人移动 手动控制机器人运动进行建图或测试
turtlebot3_fake_node/turtlebot3_fake_node.launch.py 启动假的 TurtleBot3 节点(无物理仿真) turtlebot3_fake_node
robot_state_publisher
发布模拟的机器人关节状态和 TF 变换
发布机器人模型到 TF
在没有 Gazebo 的情况下测试机器人模型或 TF 树
nav2_bringup/tb3_simulation_launch.py (Nav2 官方) Nav2 完整的仿真导航启动文件(包含 Gazebo 和 Nav2) gazebo
nav2_bringup 相关节点
启动 Gazebo 仿真世界和 TurtleBot3
启动完整的 Nav2 导航节点栈
一条命令启动完整仿真,无需分别启动 Gazebo 和 Nav2

在RViz2中单独加载地图

如果导航启动文件无法正确加载地图,手动在RViz2中添加并配置地图显示。以下是操作步骤:

  1. 启动RViz2:
    在一个新的终端中,直接启动RViz2:

    rviz2
    
  2. 添加Map显示组件:

    • 在RViz2左侧的Displays面板中,点击底部的 “Add” 按钮。
    • 在弹出的对话框中,选择 “Map” 并点击 “OK”
    • Displays 面板下会列出新添加的"Map"选项。
  3. 配置Map显示组件:

    • 点击新添加的 “Map” 旁边的展开箭头。
    • 找到 “Topic” 设置项。将其修改为地图话题。导航栈通常默认使用 /map 话题,但有时也可能不同(例如 /global_costmap/costmap 或其他)。可以使用 ros2 topic listros2 topic echo /map 来检查地图话题是否有数据。
    • 确保 “Alpha”(透明度)和 “Color Scheme”(颜色方案)等参数符合需求。
    • 最重要的是,检查并设置RViz2的 “Fixed Frame”(位于Global Options下)。通常需要将其设置为 map。如果map坐标系不存在,RViz2可能会报错,并且地图无法显示。
  4. 检查坐标系(TF):
    地图需要与一个坐标系(通常是map坐标系)关联。如果RViz2的固定帧(Fixed Frame)设置为map,但却提示[map]坐标系不存在或TF树有问题,地图也无法显示。此时,需要确保有节点(如map_server)正在发布地图数据,并且TF树是完整的(例如,存在mapodom的变换)。

  5. 从YAML文件加载地图(备用方法):
    如果想直接从一个已保存的.yaml地图文件加载地图到RViz2中,可以手动运行map_server

    ros2 run nav2_map_server map_server /path/to/your/map.yaml
    

    运行这个命令后,map_server节点会发布地图话题(默认是/map),然后就可以在RViz2中通过订阅这个话题来显示地图了。

RViz 2D Nav Goal 消息
信息组成部分 含义 数据类型/示例
Topic 名称 通信的通道 /goal_pose (默认)
消息类型 数据的结构 geometry_msgs/msg/PoseStamped
Header 元数据 -
header.stamp 消息发布时间 {sec: 1717…, nanosec: …}
header.frame_id 目标位姿的参考坐标系 “map”
Pose 具体的位姿 -
pose.position 目标点的 (x, y, z) 坐标 {x: 2.5, y: 1.8, z: 0.0}
pose.orientation 目标朝向(四元数表示) {x: 0.0, y: 0.0, z: 0.707, w: 0.707}

因此,“2D Nav Goal” 按钮发送的核心信息就是:一个基于特定坐标系(如 map)下的目标位置和朝向。 导航栈接收到这个信息后,便会开始规划路径并控制机器人向该目标移动。

Nav2 与避障调参

Nav2 的强大之处在于其高度可配置性。可以通过修改 nav2_params.yaml 等参数文件来精细调整全局和局部规划器的行为,例如:

  • 全局代价地图参数inflation_radius(膨胀半径,控制与障碍物保持的距离)。
  • 局部规划器参数(以 DWA 为例)max_vel_x(最大线速度)、max_rot_vel(最大旋转速度)、sim_time(模拟前瞻时间)等。
  • 目标容差xy_goal_tolerance(位置容差)、yaw_goal_tolerance(朝向容差)。
坐标系 描述 发布者 (TurtleBot3典型情况) 父坐标系
map 全局固定坐标系,代表世界坐标系。 导航栈 (如 amcl) None (最顶层)
odom 里程计坐标系,随机器人移动而累积漂移。 Gazebo插件 (如 libgazebo_ros_diff_drive.so) map
base_footprint 机器人底座中心的当前瞬时位置,无漂移。 robot_state_publisher (根据URDF和/joint_states计算) odom
base_link 机器人底座的中心,是机器人模型的主要链接。 robot_state_publisher (根据URDF和/joint_states计算) base_footprint
  • odom (Gazebo):提供短期、连续但可能有漂移的位姿信息,基于里程计。
  • map (Navigation2):代表全局、准确但需要初始化的世界坐标系。
  • AMCL:作为“桥梁”,通过定位算法动态发布mapodom的变换,修正漂移,将两者连接起来。
  • 操作:通过RViz2的 “2D Pose Estimate” 给AMCL提供初始位置估计,启动整个定位和导航过程。
nav2_bringup

nav2_bringup 是Nav2功能包套件中的一个核心元包(metapackage),它的主要作用是提供标准的启动文件(Launch Files)和配置文件,让你能够快速启动一个具备基本导航功能的机器人系统。

其关键组成部分如下表所示:

组件 功能描述 在导航中的作用
生命周期管理器 (nav2_lifecycle_manager) 按正确顺序启动、激活和关闭Nav2中的各个节点。 系统的“指挥”,确保依赖关系正确的启动。
行为树导航器 (nav2_bt_navigator) 导航的“大脑”,通过行为树(Behavior Tree)编排全局规划、局部控制、恢复行为等流程。 决定在什么情况下执行什么任务。
全局/局部规划器 (nav2_planner, nav2_controller) 负责全局路径规划和局部实时避障。 导航的“双足”,一个看地图规划,一个看实时环境行走。
代价地图 (nav2_costmap_2d) 融合地图信息、传感器数据,生成包含障碍物信息的网格地图。 为规划器提供环境信息。
恢复行为 (nav2_behaviors) 当机器人被困住时,执行如旋转、后退、等待等预定义动作以摆脱困境。 导航的“自救措施”。

启动Nav2的一个典型命令如下,它会加载一个默认的配置文件:

ros2 launch nav2_bringup bringup_launch.py
Nav2的核心恢复机制(Recovery Behaviors)

简单来说:nav2 的nav2_params.yamlYAML文件定义了“机器人能做什么能力(Server)”,而XML文件定义了“机器人在什么时候、怎么使用这些能力(Client)”。

  • YAML(nav2_params.yaml) 像是工具箱,你在这里把 BackUp(后退)和 DriveOnHeading(直行)这两个工具放进去,并设定了出厂默认值(如0.2米)。
  • XML(custom_recovery_tree.xml(nav2_params.yaml指定的)) 像是施工图纸,你在这里告诉机器人:“用工具箱里的 BackUp (nav2_params.yaml指定的)工具,但我这次只想退 0.15 米,速度要快点(0.08)”。

这是一种 Server-Client 模式。behavior_server 是服务端,Behavior Tree (XML) 是客户端。

配置项 YAML (behavior_server) XML (custom_recovery_tree.xml) 关系说明
插件定义 behavior_plugins: ["backup", ...]

backup: plugin: "nav2_behaviors/BackUp"
<BackUp ... /> XML标签必须对应YAML中加载的插件。XML中的<BackUp>节点会自动寻找YAML中名为backup的插件服务。
后退距离 backup_dist: 0.2 backup_dist="0.15" 关键点:XML参数覆盖YAML参数。虽然YAML默认设了0.2米,但XML调用时指定了0.15米,所以实际执行0.15米
后退速度 backup_speed: 0.05 backup_speed="0.08" 同上,XML参数优先级更高。实际速度为0.08。
直行插件 drive_on_heading: plugin: ... <DriveOnHeading ... /> XML调用DriveOnHeading插件让小车向前动一动。
安全检查 simulate_ahead_time: 1.0 (未在XML显式设置,使用默认/YAML值) XML没写的参数,会回退使用YAML中的默认配置。
生命周期节点

ROS 2 中的 Lifecycle Node 遵循一个明确定义的状态机,以确保节点在开始处理数据之前已被正确配置,并在关闭时能优雅地清理资源。nav2_map_server 就是一个生命周期节点。

其核心状态包括:

  • Unconfigured:节点已实例化,但尚未配置。
  • Inactive:节点已配置(参数已加载,资源已分配),但未激活运行。
  • Active:节点已激活,正在运行并处理数据(发布地图)。
  • Finalized:节点已被清理和关闭。

使用 launch 文件自动管理map_server和nav2_lifecycle_manager 节点

手动操作适合测试,但在自动化部署中,更推荐使用 nav2_lifecycle_manager 节点来管理生命周期。

一个简单的 launch 文件示例(例如 map_server.launch.py)如下:

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory

def generate_launch_description():
    map_arg = DeclareLaunchArgument(
        'map_file',
        default_value=PathJoinSubstitution([
            get_package_share_directory('nav2_map_server'),
            'maps',
            'your_map.yaml'
        ]),
        description='Full path to the map YAML file to load'
    )

    use_sim_time_arg = DeclareLaunchArgument(
        'use_sim_time',
        default_value='true',
        description='Use simulation (Gazebo) clock if true'
    )

    parameters=[{
        'yaml_filename': LaunchConfiguration('map_file'),
        'use_sim_time': LaunchConfiguration('use_sim_time')
    }]

    map_server_node = Node(
        package='nav2_map_server',
        executable='map_server',
        name='map_server',
        output='screen',
        parameters=[{
            'yaml_filename': LaunchConfiguration('map_file')
        }]
    )

    lifecycle_manager_node = Node(
        package='nav2_lifecycle_manager',
        executable='lifecycle_manager',
        name='lifecycle_manager',
        output='screen',
        parameters=[{
            'autostart': True,
            'node_names': ['map_server']
        }]
    )

    return LaunchDescription([
        map_arg,
        map_server_node,
        lifecycle_manager_node
    ])

使用这个 launch 文件

ros2 launch you_package map_server.launch.py map_file:=/path/to/your/map.yaml

nav2_lifecycle_manager 会自动按顺序执行 configureactivate 转换,无需手动干预。
可以直接放nav2_map_server下,其下有一个启动lifecycle 的launch文件

ros2 launch nav2_map_server map_server.launch.py map_file:=/path/to/your/map.yaml

TurtleBot3 ROS2 自主探索

实现自主探索。其核心思想是:机器人通过分析当前已建地图,寻找“前沿”(即已知区域和未知区域的边界),然后选择其中一个前沿作为目标点进行导航,从而逐步扩大已知地图范围。

1. 安装自主探索包
在 ROS 2 中,一个常用且维护良好的自主探索包是 explore_lite

cd ~/turtlebot3_ws/src
git clone https://github.com/robo-friends/m-explore-ros2.git
cd ~/turtlebot3_ws
colcon build --symlink-install
# 或 单机探索只编这个包就可以
colcon build --symlink-install --packages-select  explore_lite

环境变量.bashrc设置与之前相同–必须,通过执行turtle3init可进行turtlebot3相关包及ros2的环境初始化。参见文章开头的.bashrc的相关配置


2. 启动仿真世界与导航栈

1. 启动tb3 Gazebo 仿真环境
在一个终端中启动一个空世界或房屋世界,这样机器人有足够的未知区域可以探索。

bash turtle3init export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/opt/ros/${ROS_DISTRO}/share/turtlebot3_gazebo/models ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py

2. 启动导航栈(包含 SLAM)
在另一个终端中,启动导航相关的节点。这里我们使用 slam_toolbox 进行在线 SLAM(边建图边定位),这是实现“边规划边建图”的关键。

bash turtle3init ros2 launch turtlebot3_cartographer cartographer.launch.py use_sim_time:=True
注意:TurtleBot3 官方也提供了 slam_toolbox 的启动文件,可以查看 turtlebot3_navigation2 包中是否有更适合的 slam_toolbox 启动文件。这里以 cartographer 为例,它同样强大。


2. 启动仿真自主节点

在新的终端中,启动探索节点。这个节点会订阅 /map 话题,计算前沿,并发布目标点到 /goal_pose 等话题。

turtle3init
export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/opt/ros/${ROS_DISTRO}/share/turtlebot3_gazebo/models
ros2 launch nav2_bringup tb3_simulation_launch.py slam:=True

关键配置:可能需要根据环境修改探索参数,例如探索边界、目标点距离等。这些参数通常在启动文件中或可在启动时传入。请查阅 explore_lite 的文档了解可调参数。

遇到问题:gazebo起不来,或者障碍地图不能显示.
在这里插入图片描述

解决方法:断网重新执行 ros2 launch nav2_bringup tb3_simulation_launch.py slam:=True`

3. 在 RViz2 中初始化位姿及设定目标点
* 点击工具栏中的 2D Pose Estimate按钮。
需要通过RViz2中的 2D Pose Estimate 按钮来告诉AMCL机器人大概的初始位置和朝向:

  1. 点击RViz2工具栏中的 2D Pose Estimate 按钮。
  2. 在地图上点击机器人实际所在的大致位置,并拖动鼠标以指定机器人的朝向
  • 在地图上点击并拖动,指出机器人当前大致的位置和朝向。通过此操作,为定位算法提供了初始估计。

位姿初始化成功,explore_lite会发布:

explore-1] [INFO] [1758617661.462752606] [explore_node]: Getting initial pose of the robot

设置导航目标并观察避障行为
在 RViz2 中:
* 点击工具栏中的 2D Nav Goal按钮。
* 在地图上点击希望机器人到达的目标位置,并拖动鼠标以指定机器人的最终朝向。

而后机器人探索到目标点。

在这里插入图片描述

4. 启动自主探索节点

turtle3init
ros2 launch explore_lite explore.launch.py

该节点不用设置2D Pose Estimate2D Nav Goal 节点开启,就会自主探索。

在 RViz2 中,能看到:

  • 激光扫描数据(如 /scan 话题)。
  • 正在构建的地图/map 话题)。
  • 机器人的姿态估计
  • 全局和局部代价地图
  • 探索算法发布的目标点

5. 结束与地图保存

  1. 观察行为:机器人会开始移动,主动前往未知区域的边界。会看到地图随着机器人的移动被逐渐构建出来。
  2. 结束探索:当整个环境都被探索完毕(即没有新的前沿可被找到),探索节点会自动停止发布目标点,机器人会停止运动。
  3. 保存地图(可选):探索完成后,可以保存最终生成的地图,用于以后的导航。
    ros2 run nav2_map_server map_saver_cli -f ~/my_explored_map
    

常见问题

  • 机器人不移动或原地打转

    • 检查 explore_lite 节点是否成功发布了目标点(/goal_pose)。
    • 检查导航栈的全局/局部规划器是否成功规划出了路径。在 RViz2 中检查代价地图是否有异常障碍物阻挡了路径。
    • 尝试在 RViz2 中手动使用 “2D Goal Pose” 发送一个目标,看导航是否正常工作。如果手动导航也不行,问题出在导航栈(SLAM 定位或路径规划)。
  • 探索效率低下:可以通过调整 explore_lite 的参数来优化,例如调整目标点选择策略、最小前沿尺寸等。

  • SLAM 定位丢失:如果环境特征太少或机器人移动太快,可能导致定位丢失。可以尝试使用 cartographerslam_toolbox 中更稳健的配置。



TurtleBot3 ROS1 避障仿真

环境准备与仿真启动

首先,确保ROS1的基础环境就绪。

  1. 安装必要的软件包:根据使用的 ROS1 发行版(如 Kinetic、Melodic 或 Noetic),安装 TurtleBot3 相关的软件包。

    # 以 ROS Noetic 为例
    sudo apt-get install ros-noetic-turtlebot3 ros-noetic-turtlebot3-simulations ros-noetic-gmapping ros-noetic-dwa-local-planner 
    
  2. 设置机器人模型:在启动任何仿真或节点前,需要指定 TurtleBot3 的模型(通常是 burgerwaffle)。

    echo "export TURTLEBOT3_MODEL=burger" >> ~/.bashrc
    source ~/.bashrc
    
  3. 启动 Gazebo 仿真环境:选择一个仿真世界启动,例如空世界或内置的 turtlebot3_world

    roslaunch turtlebot3_gazebo turtlebot3_empty_world.launch
    # 或者
    roslaunch turtlebot3_gazebo turtlebot3_world.launch
    

避障的两种主要方式

1 手动编写避障算法

这种方法通过直接处理激光雷达(LaserScan)数据并发布速度指令来控制机器人。

  • 核心思路:订阅 /scan 话题获取激光雷达数据,这些数据代表了机器人周围障碍物的距离。然后编写逻辑,当某个方向的障碍物距离小于设定的安全阈值时,让机器人转向或停止。

  • Python 示例代码:创建一个 Python 脚本(如 obstacle_avoidance.py),其核心逻辑如下:

    #!/usr/bin/env python
    import rospy
    from sensor_msgs.msg import LaserScan
    from geometry_msgs.msg import Twist
    
    def scan_callback(scan_data):
        # 设置安全距离阈值,例如 0.5 米
        safe_distance = 0.5
        # 找到正前方(通常是激光数据的中点区域)的最小距离值
        # 实际应用中可能需要检查多个角度区域以提高鲁棒性
        front_range = min(scan_data.ranges[len(scan_data.ranges)//4 : 3*len(scan_data.ranges)//4])
        
        move_cmd = Twist()
        
        if front_range < safe_distance:
            # 太近了,转向避障
            move_cmd.linear.x = 0.0  # 停止前进
            move_cmd.angular.z = 0.5  # 原地旋转
        else:
            # 前方安全,继续前进
            move_cmd.linear.x = 0.2
            move_cmd.angular.z = 0.0
        
        # 发布速度指令
        cmd_vel_pub.publish(move_cmd)
    
    if __name__ == '__main__':
        rospy.init_node('turtlebot3_obstacle_avoidance')
        cmd_vel_pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)
        scan_sub = rospy.Subscriber('/scan', LaserScan, scan_callback)
        rospy.spin()
    
  • 运行算法

    1. 确保脚本有可执行权限:chmod +x obstacle_avoidance.py
    2. 在终端中运行:rosrun your_package_name obstacle_avoidance.py

2 使用 ROS Navigation Stack

这种方法功能强大,能处理全局路径规划和动态避障,但配置相对复杂。

  1. 首先进行 SLAM 建图:需要一张环境地图。(也可以不建,支持直接第2步,使用默认地图)

    • 启动仿真环境后,运行 SLAM 节点(如 gmapping):
      roslaunch turtlebot3_slam turtlebot3_slam.launch slam_methods:=gmapping
      
    • 接着,使用键盘控制机器人移动,完整扫描环境:
      roslaunch turtlebot3_teleop turtlebot3_teleop_key.launch
      
    • 地图构建完整后,保存地图:
      rosrun map_server map_saver -f ~/my_map
      
  2. 使用导航栈进行自主避障导航

    • 关闭之前的 SLAM 和键盘控制节点。
    • 启动导航栈,并加载刚才保存的地图:
      roslaunch turtlebot3_navigation turtlebot3_navigation.launch map_file:=$HOME/my_map.yaml
      # roslaunch turtlebot3_navigation turtlebot3_navigation.launch 不指定地图,可以加载默认地图,但是需要2D estimate指定机器人初始位置
      
    • 在 RViz 中初始化机器人位姿:点击 RViz 中的 “2D Pose Estimate” 按钮,然后在地图上点击并拖动箭头,以匹配 Gazebo 中机器人的实际位置和朝向。这一步对导航精度至关重要。
    • 设置导航目标:点击 “2D Nav Goal” 按钮,在地图上点击目标点,机器人便会自动规划路径并移动,途中能自动避开动态或未知的障碍物。

TurtleBot3 ROS1自主探索

  1. 安装explore lite
sudo apt-get install ros-noetic-turtlebot3 ros-noetic-turtlebot3-simulations ros-noetic-gmapping ros-noetic-dwa-local-planner
sudo apt install ros-noetic-explore-lite
  1. 启动定位建图
roslaunch turtlebot3_slam turtlebot3_slam.launch slam_methods:=gmapping
  1. 启动move_base
roslaunch turtlebot3_navigation move_base.launch
  1. 启动 explore lite
roslaunch explore_lite explore.launch

常见问题与调试技巧

  • 机器人模型未显示:检查 TURTLEBOT3_MODEL 环境变量是否设置正确。
  • 导航栈定位失败:确保在 RViz 中正确初始化了机器人的初始位姿(“2D Pose Estimate”)。如果机器人定位丢失(在 RViz 中表现为粒子云散乱),可以尝试重新设置位姿。
  • 避障算法不灵敏:调整激光雷达数据处理的区域和安全距离阈值。对于导航栈,则可以调参优化代价地图(costmap)参数,如 inflation_radius(膨胀半径)等。
  • 速度指令冲突:确保同时只有一个节点在向 /cmd_vel 话题发布速度指令。例如,在运行导航栈时,需要关闭键盘控制节点。

当掌握了基本避障后,可以尝试:

  • 优化算法:例如,自定义避障算法不只检查正前方,而是考虑左右多个扇区的数据,做出更智能的决策(如“往障碍物更远的一侧转向”)。
  • 探索高级功能:利用 Navigation Stack 实现更复杂的任务,如多点巡逻。
  • 结合视觉:如果 TurtleBot3 模型是 Waffle Pi,它配有摄像头,可以尝试结合 OpenCV 进行视觉障碍物识别。
# 1 启动Gazebo并让它自动下载所有默认模型
gazebo --verbose
---------------------------- 
# 下载所有必需的Gazebo模型
mkdir -p ~/.gazebo/models
cd ~/.gazebo/models

# 下载常用模型
wget -q -O - http://models.gazebosim.org/ | grep -o 'href="[^"]*\.tar\.gz"' | sed 's/href="//;s/"$//' | while read model; do
    wget http://models.gazebosim.org/$model
    tar -xzf $(basename $model)
done

#也可以使用如下仓库下载模型:
git clone https://github.com/macc-n/gazebo_worlds

其他链接

ROS2:
https://www.cnblogs.com/arnoldlu/p/18648901
https://blog.csdn.net/m0_67517854/article/details/136217399

自主探索:
cmu自主探索: https://github.com/HongbiaoZ/autonomous_exploration_development_environment.git
http://wiki.ros.org/rrt_exploration
ros2 explore lite:
https://github.com/robo-friends/m-explore-ros2
ros1 explore lite:
https://github.com/hrnr/m-explore
https://wiki.ros.org/frontier_exploration

其他:
https://github.com/guyuehome/ros_basic_tutorials/tree/master

ROS1 和ROS2 自主探索区别

ROS 2 的 Nav2 默认会将全局代价地图的大小“强制”与 SLAM 发布的地图大小同步,而 ROS 1 允许global costmap的static layer跟/map解耦。

1. 核心差异:Costmap 的尺寸由谁决定?

ROS 1 (move_base)

可以非常容易地创建一个“虚假的全局地图”。

  • 即便你跑着 Gmapping/Cartographer(地图在不断变大),你可以在 move_baseglobal_costmap_params.yaml 中设置:

  • static_map: false (不订阅 SLAM 的静态地图)

  • rolling_window: true (跟随机器人移动)

  • width: 10.0, height: 10.0 (强制固定大小)

  • 结果explore_lite 订阅这个 Costmap 时,它看到的“世界”只有机器人周围 10x10 米。一旦探索到边缘,算法认为世界结束了,探索自然停止。

ROS 2 (Nav2)

Nav2 的架构基于插件(Plugins)。默认的 global_costmap 配置几乎总是包含 StaticLayer 插件。

  • StaticLayer 的霸道机制:这个插件的设计初衷是“不仅加载地图,还要调整 Costmap 的尺寸以匹配地图”。
  • 只要你的 SLAM 节点(如 slam_toolboxcartographer)发布了一张正在扩大的地图,Nav2 的 StaticLayer 就会收到更新,并强制将 global_costmap 的尺寸 resize 成和 SLAM 地图一样大。
  • 结果:你在 yaml 里写的 widthheight 参数会被 StaticLayer 覆盖无效化。explore_lite 看到的边界永远随着 SLAM 地图在扩大,所以它永远停不下来。

2. 为什么 ROS 2 看起来“不能”?

并不是完全不能,而是默认配置逻辑变了,导致 ROS 1 的“野路子”行不通了。

  • ROS 1 逻辑:Costmap 是一个独立的容器,可以选择装 SLAM 地图,也可以选择不装(只装传感器数据)。
  • ROS 2 逻辑:Global Costmap 被设计为“全局规划的画布”,而 StaticLayer 默认接管了画布的大小控制权。

3. 如何在 ROS 2 中实现限制探索范围?

如果你想在 ROS 2 中复现 ROS 1 的“限制范围”效果,你需要修改 Nav2 的配置,切断 Costmap 与 SLAM 地图尺寸的强绑定

方法 A:使用 Rolling Window(完全复现 ROS 1 方式)
在你的 nav2_params.yaml -> global_costmap 中:

  1. 删除 plugins 列表中的 static_layer
  2. 添加或保留 obstacle_layerinflation_layer
  3. 设置 rolling_window: true
  4. 设置 width: 10.0, height: 10.0
  • 副作用:全局路径规划将不知道墙壁在哪里(除非墙壁在雷达视野内),可能规划出穿墙路径,直到雷达扫到墙壁。即SLAM的建立的全局地图就没用上。ROS2的SLAM地图和global costmap的static layer绑定。

方法 B:使用 Keepout Zone(ROS 2 的正规军做法)
ROS 2 推荐的做法不是改地图大小,而是使用 Keepout Filter(禁行区)

  1. 画一张和你希望探索区域一样大的虚拟地图(黑白图,黑色为禁止区域)。
  2. 在 Nav2 中启用 Costmap Filter -> KeepoutFilter
  3. explore_lite 在探索时,会发现禁行区的 Cost 是致命的(Lethal),从而不会向那个区域生成探索点。

总结

  • ROS 1:允许你通过“配置参数”欺骗 Costmap,让它以为世界只有这么大。
  • ROS 2:组件更加模块化,StaticLayer 插件强制同步了真实世界(SLAM地图)的大小。如果不移除这个插件,Costmap 大小参数就会失效。
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐