ROS2 Lyrical实验5导航Nav2

初段:

实验5 移动导航实验(纯官方示例,零手写代码,只敲终端命令)

环境:ROS2 Lyrical,Nav2,TurtleBot3,Gazebo,RViz2

✅ 核心规则:不编写任何C++/Python源码、不新建功能包、不修改launch文件

全部使用 nav2_bringup 自带官方示例 tb3_simulation_launch.py,仅在终端输入命令,RViz可视化交互。

实验分为两大阶段:阶段1 SLAM建图保存地图;阶段2 加载地图+AMCL定位+自主导航+动态避障

一、实验目的

  1. 掌握Nav2官方TurtleBot3仿真一键启动命令,理解slam:=True/slam:=False参数作用。
  2. 学会SLAM-Toolbox实时建图,使用自带map_saver_cli保存栅格地图。
  3. 理解AMCL粒子滤波定位原理,掌握RViz中2D Pose Estimate初始化机器人位姿。
  4. 使用RViz2D Nav Goal下发导航目标,观察全局/局部路径规划、代价地图、动态避障。
  5. 理解Nav2导航系统数据流、TF树、激光/里程计对导航的作用。
  6. 使用ROS2自带命令行Action工具下发导航目标(不用自己写C++代码)。

二、实验原理

  1. 官方示例tb3_simulation_launch.py内部自动完成:
    • 启动Gazebo仿真环境 + TurtleBot3 Waffle机器人模型
    • 发布机器人TF坐标树:map → odom → base_link → base_scan
    • 启动激光雷达、里程计,发布/scan、/odom话题
    • 可选择开启SLAM-Toolbox实时建图,或者加载静态地图+AMCL定位+Nav2导航栈
  2. Nav2组件:map_server、AMCL定位、全局规划器、DWB局部规划器、bt_navigator行为树导航服务器。
  3. 导航四大必备条件(官方示例已经全部配置好):
    • 完整TF树、激光/scan、里程计/odom、底盘接收/cmd_vel速度指令。

三、前置准备(只需要安装官方包,不写代码)

打开终端执行安装:

bash 复制代码
sudo apt update
sudo apt install ros-lyrical-turtlebot3* ros-lyrical-nav2-bringup ros-lyrical-slam-toolbox

配置TurtleBot3模型环境变量

bash 复制代码
echo "export TURTLEBOT3_MODEL=waffle" >> ~/.bashrc
source ~/.bashrc

四、实验步骤(全程只输入终端命令,无任何代码编写)

阶段1:SLAM建图,构建并保存地图(slam:=True)

作用:机器人在未知环境中,依靠激光+里程计实时构建栅格地图。

终端1:启动官方仿真+SLAM建图
bash 复制代码
ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False slam:=True

参数说明

  • headless:=False:弹出Gazebo图形窗口
  • slam:=True:启动SLAM-Toolbox,实时建图,不加载静态地图

启动成功自动打开:Gazebo仿真窗口 + RViz2(预配置Nav2视图)

终端2:启动键盘遥控,控制小车扫图

新开终端,启动官方teleop键盘遥控包(系统自带,无需编写)

bash 复制代码
ros2 run teleop_twist_keyboard teleop_twist_keyboard

操作:

  • i:前进;,:后退;j左转;l右转;k停止
  • 缓慢移动小车,遍历整个仿真房间,RViz里观察地图逐步构建;
  • 灰色:未知区域;黑色:障碍物;白色:可通行区域;

⚠️ 小车不能开太快,否则地图畸变。

终端3:保存建好的地图(官方map_saver_cli工具)

环境全部扫描完成,新开终端执行,地图保存到家目录:

bash 复制代码
ros2 run nav2_map_server map_saver_cli -f ~/tb3_map

生成两个文件:

~/tb3_map.pgm(栅格图像)、~/tb3_map.yaml(地图配置文件)

可选检查命令(验证TF树,无代码)

bash 复制代码
ros2 run rqt_tf_tree rqt_tf_tree

观察TF链:map → odom → base_link → base_scan

建图完成,关闭所有终端窗口。


阶段2:加载静态地图,Nav2自主导航(slam:=False)

使用上一步保存好的地图,AMCL做全局定位,Nav2实现自主导航。

终端1:启动仿真导航,加载刚才保存的地图
bash 复制代码
ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False slam:=False map:=/home/$USER/tb3_map.yaml
  • slam:=False:关闭实时SLAM,启用map_server加载静态地图,启动AMCL定位。
  • map:=xxx.yaml 指定地图文件路径

自动打开Gazebo + RViz2。此时AMCL粒子云分散,机器人不知道自身位置。

步骤2:RViz初始化定位(2D Pose Estimate,快捷键P)
  1. RViz工具栏点击 2D Pose Estimate
  2. 在地图上Gazebo小车对应的真实位置,鼠标拖拽箭头,箭头方向代表小车朝向
  3. 现象:红色AMCL粒子云快速收敛聚集到机器人位置,定位成功。

如果粒子一直散开:初始位姿点选位置错误。

  1. RViz工具栏点击 2D Nav Goal
  2. 在地图上点击目标位置,拖拽箭头设置目标朝向
  3. 观察现象:
    • 绿色线条:全局规划路径Global Plan
    • 蓝色线条:局部规划轨迹Local Plan
    • 彩色区域:代价地图(障碍物+安全膨胀区域)
    • TurtleBot3自动沿路径行驶,到达目标停止。

多次设置不同目标点,测试定点导航。

步骤4:动态障碍物避障测试

在Gazebo窗口,使用Gazebo自带菜单 Insert → Cube,在小车规划路径中间插入立方体障碍物。

观察:

  1. RViz代价地图立刻识别新增障碍物;
  2. Nav2重新规划路径,小车自动绕行避开障碍物;
  3. 删除立方体,路径恢复。
步骤5:ROS2命令行直接下发导航目标(不写任何C++代码)

使用ros2 action call 命令行工具调用NavigateToPose Action,直接下发导航目标,替代手写Action客户端。

新开终端,修改x,y坐标为你仿真环境合适目标点,直接执行:

bash 复制代码
ros2 action send_goal /navigate_to_pose nav2_msgs/action/NavigateToPose "{pose: {header: {frame_id: map}, pose: {position: {x: 2.0, y:0.5, z:0.0}, orientation: {x:0.0,y:0.0,z:0.0,w:1.0}}}}"

执行效果:

  • 无需鼠标在RViz点目标;
  • 机器人自动驶向目标点;
  • 终端直接输出导航成功/失败结果。

这一步完全满足"程序下发导航目标"的实验要求,零代码。

五、可选观测工具(全部官方自带,无代码)

  1. 查看节点图:
bash 复制代码
ros2 run rqt_graph rqt_graph
  1. 查看话题数据:
bash 复制代码
ros2 topic echo /cmd_vel
ros2 topic echo /scan
  1. 动态参数调优:
bash 复制代码
ros2 run rqt_reconfigure rqt_reconfigure

六、实验现象记录(报告直接复制)

  1. TF树截图:map → odom → base_link → base_scan完整坐标变换链。
  2. SLAM建图RViz截图:完整栅格地图。
  3. AMCL定位截图:粒子云从分散→收敛。
  4. RViz导航截图:绿色全局路径、蓝色局部路径、代价地图。
  5. 动态障碍物对比截图:放置障碍物前后导航路径。
  6. ros2 action send_goal 终端输出日志(导航到达/失败)。

七、思考题

  1. 启动命令中 slam:=True 和 slam:=False 的区别?两个模式分别启动哪些核心节点?
  2. 不执行2D Pose Estimate直接下发导航目标,会出现什么现象?原因是什么?
  3. 全局代价地图、局部代价地图的作用分别是什么?rolling_window属于哪个代价地图?
  4. 在Gazebo添加动态障碍物,为什么SLAM建图阶段不会记录这个障碍物,导航阶段却可以避障?
  5. 如果/odom里程计话题丢失,导航系统会出现什么问题?

八、实验小结(报告用)

本实验全程使用Nav2官方TurtleBot3仿真示例,无需编写任何代码,仅通过终端命令和RViz交互完成移动导航实验。

  1. 使用slam:=True模式启动SLAM-Toolbox,键盘遥控遍历环境完成栅格地图构建,使用map_saver_cli保存地图。
  2. 使用slam:=False加载静态地图,AMCL粒子滤波实现机器人定位,需要通过2D Pose Estimate提供初始位姿。
  3. 通过RViz的2D Nav Goal实现人机交互导航;在Gazebo中添加动态障碍物,Nav2依靠局部代价地图实时感知障碍物,重新规划路径实现避障。
  4. 使用ros2 action send_goal命令行工具直接调用NavigateToPose动作服务,自动下发导航目标,验证Nav2 Action接口。
  5. 验证Nav2导航依赖完整TF树、激光雷达、里程计、底盘速度指令;定位效果直接决定导航能否正常工作。

九、常见故障排查

  1. Gazebo小车不动:检查TURTLEBOT3_MODEL=waffle环境变量是否生效。
  2. AMCL粒子一直发散:2D Pose Estimate初始位姿点选错误;地图yaml文件路径错误。
  3. 无法规划路径:目标点落在障碍物或者代价地图膨胀区域内。
  4. action命令报错:确认tb3_simulation_launch.py完全启动,bt_navigator正常运行。
  5. 建图地图重影:小车移动速度过快,降低键盘遥控的移动速度。

中段:

实验5 移动导航实验

基于官方TB3仿真:ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False

环境:ROS2(Lyrical),TurtleBot3,Nav2‑Bringup,Gazebo,RViz2

直接使用Nav2官方示例,不需要自己写URDF、Gazebo插件、Nav2底层启动逻辑,实验简洁,聚焦SLAM建图、地图保存、定位、自主导航、避障、代码调用导航目标。

一、实验目的

  1. 理解Nav2导航系统组成,掌握TurtleBot3仿真环境启动。
  2. 掌握使用Nav2的SLAM工具完成环境建图、保存地图。
  3. 理解AMCL粒子滤波定位原理,学会初始化机器人2D位姿。
  4. 掌握RViz2下发导航目标,实现机器人定点自主导航、动态避障。
  5. 理解全局规划器、局部DWB规划器、全局/局部代价地图作用。
  6. 编写C++ Action客户端,程序自动下发导航目标点,实现自动导航。
  7. 分析常见故障,理解TF、激光、里程计对导航的影响。

二、实验原理

  1. TurtleBot3仿真:官方已经封装好差速底盘、激光雷达、里程计、TF树。
  2. SLAM :利用激光雷达+里程计构建2D栅格地图;map_saver_cli保存地图。
  3. Nav2架构
    • map_server:加载静态栅格地图
    • amcl:蒙特卡洛粒子滤波定位,修正里程计漂移
    • global_planner:全局路径规划,规划从起点到目标的全局路径
    • dwb_controller:局部规划器,做速度输出、避障、跟踪全局路径
    • bt_navigator:行为树,管理导航流程,接收NavigateToPoseAction目标
  4. 导航必备条件:
    • 完整TF树:map → odom → base_link → base_scan
    • 激光话题 /scan
    • 里程计话题 /odom
    • 底盘接收速度指令 /cmd_vel

本实验直接复用官方tb3_simulation_launch.py,自动启动Gazebo、TurtleBot3模型、RViz2、Nav2全部节点。

三、实验环境

  • Ubuntu + ROS2

  • 依赖包安装(如未安装先执行)

    sudo apt update
    sudo apt install ros-{ROS_DISTRO}-turtlebot3* ros-{ROS_DISTRO}-nav2-bringup ros-${ROS_DISTRO}-slam-toolbox

  • 环境变量必须设置TURTLEBOT3_MODEL,一般是waffle

    echo "export TURTLEBOT3_MODEL=waffle" >> ~/.bashrc
    source ~/.bashrc

四、实验内容与步骤

实验分为两大模块:

模块1:SLAM建图(构建环境地图并保存)

模块2:加载已有地图 + AMCL定位 + Nav2自主导航 + 避障 + 代码控制导航

模块1:SLAM建图实验

tb3_simulation_launch.py可以同时启动Gazebo仿真 + SLAM模式。

步骤1:启动TB3仿真+SLAM建图

终端1:

复制代码
export TURTLEBOT3_MODEL=waffle
ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False slam:=True

参数说明:

  • headless:=False:弹出Gazebo图形窗口
  • slam:=True:启动SLAM‑Toolbox进行实时建图,而不是加载静态地图导航

启动成功后会同时打开:

  1. Gazebo:仿真室内环境,TurtleBot3小车
  2. RViz2:预配置好Nav2界面,可以看到激光、TF、正在构建的地图。
步骤2:键盘遥控小车遍历环境

新开终端2,启动键盘遥控:

复制代码
ros2 run teleop_twist_keyboard teleop_twist_keyboard
  • 使用键盘 i j k l , 控制小车缓慢移动旋转;
  • 慢速遍历整个房间,把全部墙壁、障碍物扫描到地图中;
  • RViz中观察Map话题,灰色未知,黑色障碍物,白色可通行区域。>

注意:不要高速移动,高速会导致地图畸变、重影。

步骤3:保存建好的地图

当整个环境扫描完成,新开终端3,执行保存地图:

复制代码
ros2 run nav2_map_server map_saver_cli -f ~/my_tb3_map

会在用户家目录生成两个文件:

  • my_tb3_map.pgm:地图图像
  • my_tb3_map.yaml:地图配置文件(分辨率、原点、阈值)

建图完成,关闭所有终端,准备导航实验。
可选检查工具

复制代码
ros2 run rqt_tf_tree rqt_tf_tree

观察TF变换链:map → odom → base_link → base_scan是否完整。


模块2:加载地图,Nav2自主导航实验

使用刚才保存的my_tb3_map.yaml,启动仿真+导航,不再做SLAM。

步骤1:启动TB3仿真导航(slam:=False,加载静态地图)

终端1,设置地图路径,启动官方仿真launch

复制代码
export TURTLEBOT3_MODEL=waffle
ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False slam:=False map:=/home/$USER/my_tb3_map.yaml

slam:=False:关闭SLAM,启用map_server加载静态地图,启动AMCL定位。

启动后打开Gazebo与RViz2。此时:

  • Gazebo中小车位置是仿真初始位置;
  • RViz地图已经加载,但是AMCL粒子云是散开的,机器人不知道自己在哪。
步骤2:2D Pose Estimate初始化定位
  1. 在RViz工具栏点击 2D Pose Estimate(快捷键P)
  2. 在地图上,Gazebo小车对应的真实位置,点击拖拽箭头:箭头方向为小车朝向。
  3. 现象:大量红色AMCL粒子云,几秒后粒子收敛聚集到小车真实位置,定位完成。

如果粒子一直散开:初始位姿点错;激光话题异常;地图与实际环境不匹配。

  1. RViz工具栏点击 2D Nav Goal(快捷键G)
  2. 在地图上选择目标位置,拖拽箭头设置目标朝向。
  3. 观察现象:
    • 绿色线条:全局规划路径(Global Plan),从起点到目标的全局路径;
    • 蓝色线条:局部规划轨迹(Local Plan),DWB局部规划输出;
    • 彩色色块:代价地图,障碍物、膨胀安全区域;
    • TurtleBot3小车自动运动,沿着路径向目标行驶,到达目标停止。
  4. 多次设置不同目标点,测试定点导航。
步骤4:动态障碍物避障测试
  1. 在Gazebo界面,插入Cube立方体障碍物,放到小车规划路径中间;
  2. 观察RViz代价地图,立刻识别出新障碍物;
  3. Nav2重新规划路径,小车绕行避开障碍物;
  4. 将障碍物移走,路径恢复。

记录:有障碍物和移除障碍物的导航行为对比。

步骤5:编写C++ Action客户端,程序自动下发导航目标

使用Nav2标准 nav2_msgs/action/NavigateToPose,不用鼠标,代码自动发送导航目标。

5‑1 创建功能包
复制代码
cd ~/ros2_ws/src
ros2 pkg create --build-type ament_cmake nav2_send_goal --dependencies rclcpp nav2_msgs rclcpp_action geometry_msgs
cd nav2_send_goal
mkdir src
5‑2 src/send_nav_goal.cpp
复制代码
#include <rclcpp/rclcpp.hpp>
#include <rclcpp_action/rclcpp_action.hpp>
#include <nav2_msgs/action/navigate_to_pose.hpp>
#include <geometry_msgs/msg/pose_stamped.hpp>

using NavigateToPose = nav2_msgs::action::NavigateToPose;
using GoalHandle = rclcpp_action::ClientGoalHandle<NavigateToPose>;

class NavGoalClient : public rclcpp::Node
{
public:
    NavGoalClient() : Node("send_nav_goal")
    {
        client_ = rclcpp_action::create_client<NavigateToPose>(this, "navigate_to_pose");
    }

    void send_goal(double x, double y, double yaw)
    {
        if (!client_->wait_for_action_server(std::chrono::seconds(5)))
        {
            RCLCPP_ERROR(get_logger(), "Action Server未上线");
            return;
        }
        auto goal_msg = NavigateToPose::Goal();
        goal_msg.pose.header.frame_id = "map";
        goal_msg.pose.header.stamp = this->get_clock()->now();
        goal_msg.pose.pose.position.x = x;
        goal_msg.pose.pose.position.y = y;
        // 简单设置朝向
        goal_msg.pose.pose.orientation.w = 1.0;

        auto send_goal_options = rclcpp_action::Client<NavigateToPose>::SendGoalOptions();
        send_goal_options.result_callback =
            [this](const GoalHandle::WrappedResult & result)
        {
            if(result.code == rclcpp_action::ResultCode::SUCCEEDED)
            {
                RCLCPP_INFO(this->get_logger(), "导航目标到达!");
            }else{
                RCLCPP_ERROR(this->get_logger(), "导航失败");
            }
            rclcpp::shutdown();
        };
        client_->async_send_goal(goal_msg, send_goal_options);
    }
private:
    rclcpp_action::Client<NavigateToPose>::SharedPtr client_;
};

int main(int argc, char** argv)
{
    rclcpp::init(argc, argv);
    auto node = std::make_shared<NavGoalClient>();
    // 修改为你的仿真环境中合适的目标点坐标
    node->send_goal(2.0, 0.5, 0.0);
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}
5‑3 修改CMakeLists.txt
复制代码
find_package(rclcpp REQUIRED)
find_package(nav2_msgs REQUIRED)
find_package(rclcpp_action REQUIRED)
find_package(geometry_msgs REQUIRED)

add_executable(send_goal src/send_nav_goal.cpp)
ament_target_dependencies(send_goal rclcpp nav2_msgs rclcpp_action geometry_msgs)

install(TARGETS
  send_goal
  DESTINATION lib/${PROJECT_NAME}
)
5‑4 package.xml添加依赖
复制代码
<depend>rclcpp</depend>
<depend>nav2_msgs</depend>
<depend>rclcpp_action</depend>
<depend>geometry_msgs</depend>
5‑5 编译运行
复制代码
cd ~/ros2_ws
colcon build --packages-select nav2_send_goal
source install/setup.bash

前提:已经启动tb3_simulation_launch.py,并且已经做2D Pose Estimate初始化定位成功!

新开终端执行客户端:

复制代码
ros2 run nav2_send_goal send_goal

现象:不需要鼠标,机器人自动驶向代码中设置的(x,y)目标点,终端打印到达/失败信息。

五、实验现象记录(实验报告可直接复制)

  1. TF树截图:map → odom → base_link → base_scan完整变换链。
  2. SLAM建图RViz截图:建图完成的栅格地图。
  3. AMCL定位截图:初始化后粒子云从分散收敛。
  4. RViz导航截图:绿色全局路径、蓝色局部路径、代价地图。
  5. 动态障碍物:放置障碍物前后路径对比截图。
  6. Action客户端终端输出:导航到达或失败日志。

六、思考题

  1. tb3_simulation_launch.py中参数slam:=True与slam:=False有什么区别?
  2. 如果不做2D Pose Estimate初始化定位,直接下发2D Nav Goal,会发生什么?
  3. 全局代价地图与局部代价地图分别作用?rolling_window参数在哪种代价地图开启?
  4. AMCL粒子数目调大,对定位效果和CPU负载有什么影响?
  5. 动态障碍物为什么SLAM建图时不会出现,但是导航时可以识别并避障?
  6. 如果话题/odom丢失,导航系统会出现什么现象?

七、实验小结(报告用)

  1. 使用Nav2官方tb3_simulation_launch.py快速启动TurtleBot3仿真,slam:=True完成SLAM‑Toolbox建图,通过map_saver_cli保存栅格地图。
  2. 设置slam:=False加载静态地图,AMCL粒子滤波实现机器人定位,需要2D Pose Estimate提供初始位姿。
  3. 通过RViz的2D Nav Goal实现人机交互导航;在路径上增加动态障碍物,Nav2可以实时感知障碍物并重新规划路径实现避障。
  4. 编写Action客户端调用NavigateToPose接口,程序自动下发导航目标,理解Nav2行为树Action接口。
  5. 导航依赖完整TF树、激光、里程计、底盘速度指令;定位质量直接决定导航能否正常运行。代价地图膨胀参数、规划器速度参数影响避障与运动性能。

常见故障排查

  1. Gazebo小车不动:检查/cmd_vel话题是否有速度输出;确认TURTLEBOT3_MODEL环境变量。
  2. AMCL粒子始终发散:2D Pose Estimate点的位置不对;地图yaml与实际环境不一致;/scan激光数据异常。
  3. 导航规划不出路径:机器人被代价地图膨胀层包围;目标点落在障碍物或未知区域。
  4. Action客户端提示Action Server未上线:确认tb3_simulation_launch.py完整启动,bt_navigator正常运行。
  5. 建图地图重影:小车运动速度太快,降低键盘遥控速度。

高段:

参考资料:ROS2 Lyrical第5章导航前置、第6章Nav2自主导航

环境:Ubuntu26.04 + ROS2‑Lyrical + Gazebo Garden,差速四轮小车仿真平台

一、实验目的

  1. 理解Nav2导航四大前置条件:差速底盘、完整TF2坐标树、2D激光雷达、标准里程计Odometry ,掌握导航数据流传感器→定位→规划→控制→底盘执行完整链路。
  2. 掌握TF2广播、监听,理解标准坐标链map → odom → base_footprint → laser_link。
  3. 掌握gmapping完成SLAM栅格建图、地图保存(map_saver)与加载(map_server)。
  4. 掌握Nav2配置:代价地图(全局/局部)、AMCL粒子滤波定位、DWB局部规划器参数配置。
  5. 掌握RViz2导航工具:2D Pose Estimate初始化定位、2D Nav Goal下发导航目标,实现定点自主导航、动态避障。
  6. 编写Action客户端C++代码,实现程序自动下发导航目标点。

二、实验原理

  1. TF2坐标变换 :维护机器人各连杆、传感器坐标系之间平移旋转关系,导航中激光雷达数据需要通过TF转换到底盘、地图坐标系下参与代价地图计算;URDF/Xacro+robot_state_publisher自动广播连杆TF变换。
  2. 传感器数据
    • LaserScan:2D激光雷达输出测距数据,话题/scan,为SLAM、代价地图提供障碍物观测。
    • Odometry里程计:输出机器人相对odom坐标系位姿、线角速度,Gazebo差速驱动插件积分生成里程;实体机器人通过编码器速度积分得到里程。
    • /cmd_vel:Twist消息,Nav2规划器输出速度指令,控制差速底盘运动。
  3. gmapping‑SLAM :粒子滤波SLAM,输入激光+里程计+TF,输出占用栅格地图/map;地图保存生成.pgm图像和.yaml配置文件。
  4. Nav2导航栈
    • map_server:加载静态栅格地图,发布/map话题。
    • AMCL2:自适应蒙特卡洛粒子滤波,基于已知地图+激光+里程实现全局定位,输出机器人在map坐标系位姿。
    • 全局代价地图global_costmap:基于静态地图做长距离全局路径规划。
    • 局部代价地图local_costmap:滑动窗口跟随机器人,实时处理动态障碍物。
    • DWB局部规划器:接收全局路径,结合运动约束输出/cmd_vel速度指令,替代ROS1 DWA规划器。

Nav2强制4个前提(缺一不可):

①差速底盘接收Twist /cmd_vel;②完整TF2坐标树;③2D激光LaserScan;④Odometry里程计话题输出。

三、实验设备/环境

  • 软件:Ubuntu26.04,ROS2‑Lyrical,Gazebo Garden,RViz2,colcon编译工具
  • 仿真模型:四轮差速小车Xacro模型,搭载2D激光雷达;Gazebo室内仿真世界willowgarage_world

四、实验内容与步骤

分为两大部分:PartA SLAM建图(第5章);PartB Nav2自主导航(第6章)

PartA SLAM建图(导航前置)

步骤1:编译功能包,启动建图仿真
  1. 工作空间~/ros2_ws/src放置chapter5_tutorials源码,编译
bash 复制代码
cd ~/ros2_ws
colcon build --packages-select chapter5_tutorials
source install/setup.bash
  1. 启动Gazebo仿真、机器人、gmapping建图、RViz2
bash 复制代码
ros2 launch chapter5_tutorials gazebo_mapping.launch.py model:=$(ament_index_get_resource robot1_description urdf/robot1_base_04.xacro)
  1. 键盘遥控包安装,新开终端启动键盘遥控,控制小车遍历全部室内环境
bash 复制代码
sudo apt install ros‑lyrical‑teleop‑twist‑keyboard
ros2 run teleop_twist_keyboard teleop_twist_keyboard

操作:键盘上下左右控制小车缓慢遍历整个房间,RViz2中OccupancyGrid实时观察生成栅格地图,白色=可通行,黑色=障碍物,灰色=未知区域。

步骤2:保存建好的地图

遍历完成,环境全部扫描完毕,执行map_saver保存地图:

bash 复制代码
ros2 run map_server map_saver -f my_map

生成两个文件:

  • my_map.pgm:栅格灰度地图图像
  • my_map.yaml:分辨率、原点、阈值配置文件,后续map_server加载使用。

检查:rqt_tf_tree查看TF树是否完整:map → odom → base_footprint → laser_link

bash 复制代码
ros2 run rqt_tf_tree rqt_tf_tree
步骤1:创建Nav2功能包chapter6_tutorials
bash 复制代码
cd ~/ros2_ws/src
ros2 pkg create --build‑type ament_cmake chapter6_tutorials \
rclcpp nav2_bringup nav2_amcl nav2_costmap_2d nav2_planner nav2_dwb_controller \
tf2_ros gazebo_ros xacro map_server rviz2 rqt_reconfigure

目录结构:

复制代码
chapter6_tutorials/
├── launch/          # Python launch、yaml参数
├── maps/            # 将PartA生成my_map.pgm、my_map.yaml复制到此目录
├── src/             # send_goal.cpp Action客户端代码
├── CMakeLists.txt
└── package.xml

复制4套yaml参数文件:

  1. costmap_common_params.yaml:代价地图公共参数,设置机器人footprint轮廓,障碍物膨胀半径inflation_radius
  2. global_costmap_params.yaml:全局代价地图参数,static_map:true加载静态地图
  3. local_costmap_params.yaml:局部代价地图,开启rolling_window:true滑动窗口
  4. dwb_local_planner_params.yaml:DWB规划器速度加速度约束,差速小车holonomic_robot: false
  5. amcl_params.yaml:AMCL粒子滤波参数,设置最小最大粒子数、激光观测模型。

功能:一键启动Gazebo仿真、机器人模型、map_server加载地图、AMCL定位、Nav2 bringup、RViz2导航预设界面。

编译功能包:

bash 复制代码
cd ~/ros2_ws
colcon build --packages-select chapter6_tutorials
source install/setup.bash

完整启动命令:

bash 复制代码
ros2 launch chapter6_tutorials nav2_full.launch.py
步骤3 RViz2交互导航操作
  1. 2D Pose Estimate(快捷键P)初始化定位
    点击工具栏2D Pose Estimate,在地图上小车真实位置拖拽箭头指定机器人初始位姿。AMCL粒子云从分散逐渐聚拢,代表定位收敛。

现象:红色粒子云收敛到机器人真实位置,定位成功。

  1. 下发导航目标点2D Nav Goal(快捷键G)

    点击2D Nav Goal,在地图上选择目标位置拖拽确定朝向;Nav2生成绿色全局路径Global Plan、蓝色局部Local Plan,小车自动行驶前往目标,同时局部代价地图实时显示障碍物和膨胀安全区。

  2. 动态障碍物避障测试

    Gazebo界面插入立方体障碍物,放到规划路径中间;观察RViz局部代价地图更新障碍物,Nav2重新规划路径,小车自动绕行避开障碍。

  3. 动态参数调优

bash 复制代码
ros2 run rqt_reconfigure rqt_reconfigure

在线修改DWB最大速度、AMCL粒子数量、障碍物膨胀半径,不需要重启节点,观察导航行为变化。

步骤4:C++ Action客户端自动下发导航目标

编译send_goal.cpp,调用NavigateToPose Action接口,程序自动给定点导航。

bash 复制代码
ros2 run chapter6_tutorials send_goal

现象:无需鼠标操作,机器人自动驶向代码指定的(x,y,yaw)目标点。

五、实验现象记录

  1. TF树检查:rqt_tf_tree,截图记录完整坐标链map‑odom‑base_footprint‑laser_link。
  2. SLAM建图:RViz OccupancyGrid截图,记录建好的室内栅格地图。
  3. AMCL定位:粒子云分散→收敛截图。
  4. RViz导航截图:全局绿色路径、局部蓝色轨迹、局部代价地图彩色障碍物膨胀区域。
  5. 动态障碍物:放置障碍物前后路径对比截图,观察绕行效果。
  6. Action客户端运行终端输出,记录导航到达/失败状态。

六、思考题

1 Nav2导航必须的4个前置条件是什么,如果缺少/odom里程计话题会出现什么现象?

2 TF坐标变换链map→odom→base_footprint→laser_link,每个变换分别由哪个节点发布?

3 全局代价地图与局部代价地图的区别?rolling_window参数作用是什么?

4 AMCL粒子数量min_particles/max_particles调大,定位和CPU占用会如何变化?

5 DWB规划器中holonomic_robot: false含义,麦克纳姆轮全向机器人该如何设置?

6 里程计存在漂移误差,AMCL如何利用激光与地图匹配修正里程计漂移?

七、实验报告参考小结

  1. 本实验完成仿真差速小车环境gmapping‑SLAM建图,得到室内栅格地图,掌握map_saver/map_server地图存取。
  2. 验证TF2坐标变换树,确认Nav2四大前置条件全部满足。
  3. 基于Nav2实现AMCL粒子滤波定位,DWB局部规划器完成定点导航,实现动态障碍物避障。
  4. 通过RViz2交互+Action客户端两种方式下发导航目标,理解map_server‑AMCL‑costmap‑planner‑controller‑/cmd_vel‑底盘完整数据流。
  5. 代价地图footprint轮廓、inflation_radius膨胀半径直接影响机器人碰撞安全性;AMCL粒子数量平衡定位精度与计算资源;DWB参数约束机器人速度、加速度,防止运动失控。

常见故障排查

1 RViz2红色报错TF变换超时:检查TF树,确认完整map‑odom‑base_footprint‑laser_link链条。

2 AMCL粒子云不收敛:2D Pose Estimate初始位姿偏差过大;检查激光话题/scan是否正常;AMCL参数激光观测模型配置。

3 机器人原地不动不导航:检查/cmd_vel是否有速度输出;DWB最大速度是否设置为0;footprint轮廓大于可行通道导致无可行路径。

4 建图地图扭曲:小车运动过快,激光扫描跟不上,降低键盘遥控移动速度。


相关推荐
yi0111 小时前
DAY 14: LeetCode 394. 字符串解码|递归和栈到底怎么处理嵌套?
数据结构·笔记·python·算法·leetcode
一条破秋裤1 小时前
Linux 线程分离与主动取消:pthread_detach、pthread_cancel
java·linux·jvm
j7~1 小时前
【Linux网络】四十六.《高级 IO (上篇)》-- 详解
linux·运维·网络
JWASX1 小时前
Java 转 go 学习 - 基本语法
学习·golang
随性而行3601 小时前
企业微信二次开发如何接入大模型工具?API接口实现智能任务调用的技术思路
java·前端·人工智能·python·微信·机器人·企业微信
IT大白鼠2 小时前
彭大帅的AI运维助手——自然语言管理 Linux 集群与网络设备——第 0 篇 · 导读:把 Linux 运维交给 AI,到底靠谱吗
linux·运维·人工智能
让头发掉下来2 小时前
Kettle学习-17作业的创建与定时调度
学习
小刘在重生~2 小时前
CSS 零基础完整学习笔记|选择器|盒子模型|布局
css·笔记·学习
AI智讯中枢2 小时前
高性能 C++ 实战 (五):perf+FlameGraph 火焰图生产实战,精准定位 CPU / 缓存 / 锁瓶颈,避坑 + 完整实操案例
linux·c++·性能调优·性能分析·perf·flamegraph·火焰图