初段:
实验5 移动导航实验(纯官方示例,零手写代码,只敲终端命令)
环境:ROS2 Lyrical,Nav2,TurtleBot3,Gazebo,RViz2
✅ 核心规则:不编写任何C++/Python源码、不新建功能包、不修改launch文件
全部使用 nav2_bringup 自带官方示例
tb3_simulation_launch.py,仅在终端输入命令,RViz可视化交互。实验分为两大阶段:阶段1 SLAM建图保存地图;阶段2 加载地图+AMCL定位+自主导航+动态避障
一、实验目的
- 掌握Nav2官方TurtleBot3仿真一键启动命令,理解
slam:=True/slam:=False参数作用。 - 学会SLAM-Toolbox实时建图,使用自带map_saver_cli保存栅格地图。
- 理解AMCL粒子滤波定位原理,掌握RViz中
2D Pose Estimate初始化机器人位姿。 - 使用RViz
2D Nav Goal下发导航目标,观察全局/局部路径规划、代价地图、动态避障。 - 理解Nav2导航系统数据流、TF树、激光/里程计对导航的作用。
- 使用ROS2自带命令行Action工具下发导航目标(不用自己写C++代码)。
二、实验原理
- 官方示例
tb3_simulation_launch.py内部自动完成:- 启动Gazebo仿真环境 + TurtleBot3 Waffle机器人模型
- 发布机器人TF坐标树:
map → odom → base_link → base_scan - 启动激光雷达、里程计,发布
/scan、/odom话题 - 可选择开启SLAM-Toolbox实时建图,或者加载静态地图+AMCL定位+Nav2导航栈
- Nav2组件:map_server、AMCL定位、全局规划器、DWB局部规划器、bt_navigator行为树导航服务器。
- 导航四大必备条件(官方示例已经全部配置好):
- 完整TF树、激光
/scan、里程计/odom、底盘接收/cmd_vel速度指令。
- 完整TF树、激光
三、前置准备(只需要安装官方包,不写代码)
打开终端执行安装:
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)
- RViz工具栏点击 2D Pose Estimate
- 在地图上Gazebo小车对应的真实位置,鼠标拖拽箭头,箭头方向代表小车朝向
- 现象:红色AMCL粒子云快速收敛聚集到机器人位置,定位成功。
如果粒子一直散开:初始位姿点选位置错误。
步骤3:RViz交互下发导航目标(2D Nav Goal,快捷键G)
- RViz工具栏点击 2D Nav Goal
- 在地图上点击目标位置,拖拽箭头设置目标朝向
- 观察现象:
- 绿色线条:全局规划路径Global Plan
- 蓝色线条:局部规划轨迹Local Plan
- 彩色区域:代价地图(障碍物+安全膨胀区域)
- TurtleBot3自动沿路径行驶,到达目标停止。
多次设置不同目标点,测试定点导航。
步骤4:动态障碍物避障测试
在Gazebo窗口,使用Gazebo自带菜单 Insert → Cube,在小车规划路径中间插入立方体障碍物。
观察:
- RViz代价地图立刻识别新增障碍物;
- Nav2重新规划路径,小车自动绕行避开障碍物;
- 删除立方体,路径恢复。
步骤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点目标;
- 机器人自动驶向目标点;
- 终端直接输出导航成功/失败结果。
这一步完全满足"程序下发导航目标"的实验要求,零代码。
五、可选观测工具(全部官方自带,无代码)
- 查看节点图:
bash
ros2 run rqt_graph rqt_graph
- 查看话题数据:
bash
ros2 topic echo /cmd_vel
ros2 topic echo /scan
- 动态参数调优:
bash
ros2 run rqt_reconfigure rqt_reconfigure
六、实验现象记录(报告直接复制)
- TF树截图:
map → odom → base_link → base_scan完整坐标变换链。 - SLAM建图RViz截图:完整栅格地图。
- AMCL定位截图:粒子云从分散→收敛。
- RViz导航截图:绿色全局路径、蓝色局部路径、代价地图。
- 动态障碍物对比截图:放置障碍物前后导航路径。
- ros2 action send_goal 终端输出日志(导航到达/失败)。
七、思考题
- 启动命令中
slam:=True和slam:=False的区别?两个模式分别启动哪些核心节点? - 不执行
2D Pose Estimate直接下发导航目标,会出现什么现象?原因是什么? - 全局代价地图、局部代价地图的作用分别是什么?
rolling_window属于哪个代价地图? - 在Gazebo添加动态障碍物,为什么SLAM建图阶段不会记录这个障碍物,导航阶段却可以避障?
- 如果
/odom里程计话题丢失,导航系统会出现什么问题?
八、实验小结(报告用)
本实验全程使用Nav2官方TurtleBot3仿真示例,无需编写任何代码,仅通过终端命令和RViz交互完成移动导航实验。
- 使用
slam:=True模式启动SLAM-Toolbox,键盘遥控遍历环境完成栅格地图构建,使用map_saver_cli保存地图。 - 使用
slam:=False加载静态地图,AMCL粒子滤波实现机器人定位,需要通过2D Pose Estimate提供初始位姿。 - 通过RViz的
2D Nav Goal实现人机交互导航;在Gazebo中添加动态障碍物,Nav2依靠局部代价地图实时感知障碍物,重新规划路径实现避障。 - 使用
ros2 action send_goal命令行工具直接调用NavigateToPose动作服务,自动下发导航目标,验证Nav2 Action接口。 - 验证Nav2导航依赖完整TF树、激光雷达、里程计、底盘速度指令;定位效果直接决定导航能否正常工作。
九、常见故障排查
- Gazebo小车不动:检查
TURTLEBOT3_MODEL=waffle环境变量是否生效。 - AMCL粒子一直发散:2D Pose Estimate初始位姿点选错误;地图yaml文件路径错误。
- 无法规划路径:目标点落在障碍物或者代价地图膨胀区域内。
- action命令报错:确认tb3_simulation_launch.py完全启动,bt_navigator正常运行。
- 建图地图重影:小车移动速度过快,降低键盘遥控的移动速度。
中段:
实验5 移动导航实验
基于官方TB3仿真:ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False
环境:ROS2(Lyrical),TurtleBot3,Nav2‑Bringup,Gazebo,RViz2
直接使用Nav2官方示例,不需要自己写URDF、Gazebo插件、Nav2底层启动逻辑,实验简洁,聚焦SLAM建图、地图保存、定位、自主导航、避障、代码调用导航目标。
一、实验目的
- 理解Nav2导航系统组成,掌握TurtleBot3仿真环境启动。
- 掌握使用Nav2的SLAM工具完成环境建图、保存地图。
- 理解AMCL粒子滤波定位原理,学会初始化机器人2D位姿。
- 掌握RViz2下发导航目标,实现机器人定点自主导航、动态避障。
- 理解全局规划器、局部DWB规划器、全局/局部代价地图作用。
- 编写C++ Action客户端,程序自动下发导航目标点,实现自动导航。
- 分析常见故障,理解TF、激光、里程计对导航的影响。
二、实验原理
- TurtleBot3仿真:官方已经封装好差速底盘、激光雷达、里程计、TF树。
- SLAM :利用激光雷达+里程计构建2D栅格地图;
map_saver_cli保存地图。 - Nav2架构
map_server:加载静态栅格地图amcl:蒙特卡洛粒子滤波定位,修正里程计漂移global_planner:全局路径规划,规划从起点到目标的全局路径dwb_controller:局部规划器,做速度输出、避障、跟踪全局路径bt_navigator:行为树,管理导航流程,接收NavigateToPoseAction目标
- 导航必备条件:
- 完整TF树:
map → odom → base_link → base_scan - 激光话题
/scan - 里程计话题
/odom - 底盘接收速度指令
/cmd_vel
- 完整TF树:
本实验直接复用官方
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进行实时建图,而不是加载静态地图导航
启动成功后会同时打开:
- Gazebo:仿真室内环境,TurtleBot3小车
- 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初始化定位
- 在RViz工具栏点击 2D Pose Estimate(快捷键P)
- 在地图上,Gazebo小车对应的真实位置,点击拖拽箭头:箭头方向为小车朝向。
- 现象:大量红色AMCL粒子云,几秒后粒子收敛聚集到小车真实位置,定位完成。
如果粒子一直散开:初始位姿点错;激光话题异常;地图与实际环境不匹配。
步骤3:RViz下发导航目标点(2D Nav Goal)交互导航
- RViz工具栏点击 2D Nav Goal(快捷键G)
- 在地图上选择目标位置,拖拽箭头设置目标朝向。
- 观察现象:
- 绿色线条:全局规划路径(Global Plan),从起点到目标的全局路径;
- 蓝色线条:局部规划轨迹(Local Plan),DWB局部规划输出;
- 彩色色块:代价地图,障碍物、膨胀安全区域;
- TurtleBot3小车自动运动,沿着路径向目标行驶,到达目标停止。
- 多次设置不同目标点,测试定点导航。
步骤4:动态障碍物避障测试
- 在Gazebo界面,插入Cube立方体障碍物,放到小车规划路径中间;
- 观察RViz代价地图,立刻识别出新障碍物;
- Nav2重新规划路径,小车绕行避开障碍物;
- 将障碍物移走,路径恢复。
记录:有障碍物和移除障碍物的导航行为对比。
步骤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)目标点,终端打印到达/失败信息。
五、实验现象记录(实验报告可直接复制)
- TF树截图:
map → odom → base_link → base_scan完整变换链。 - SLAM建图RViz截图:建图完成的栅格地图。
- AMCL定位截图:初始化后粒子云从分散收敛。
- RViz导航截图:绿色全局路径、蓝色局部路径、代价地图。
- 动态障碍物:放置障碍物前后路径对比截图。
- Action客户端终端输出:导航到达或失败日志。
六、思考题
tb3_simulation_launch.py中参数slam:=True与slam:=False有什么区别?- 如果不做2D Pose Estimate初始化定位,直接下发2D Nav Goal,会发生什么?
- 全局代价地图与局部代价地图分别作用?
rolling_window参数在哪种代价地图开启? - AMCL粒子数目调大,对定位效果和CPU负载有什么影响?
- 动态障碍物为什么SLAM建图时不会出现,但是导航时可以识别并避障?
- 如果话题
/odom丢失,导航系统会出现什么现象?
七、实验小结(报告用)
- 使用Nav2官方
tb3_simulation_launch.py快速启动TurtleBot3仿真,slam:=True完成SLAM‑Toolbox建图,通过map_saver_cli保存栅格地图。 - 设置
slam:=False加载静态地图,AMCL粒子滤波实现机器人定位,需要2D Pose Estimate提供初始位姿。 - 通过RViz的2D Nav Goal实现人机交互导航;在路径上增加动态障碍物,Nav2可以实时感知障碍物并重新规划路径实现避障。
- 编写Action客户端调用
NavigateToPose接口,程序自动下发导航目标,理解Nav2行为树Action接口。 - 导航依赖完整TF树、激光、里程计、底盘速度指令;定位质量直接决定导航能否正常运行。代价地图膨胀参数、规划器速度参数影响避障与运动性能。
常见故障排查
- Gazebo小车不动:检查
/cmd_vel话题是否有速度输出;确认TURTLEBOT3_MODEL环境变量。 - AMCL粒子始终发散:2D Pose Estimate点的位置不对;地图yaml与实际环境不一致;
/scan激光数据异常。 - 导航规划不出路径:机器人被代价地图膨胀层包围;目标点落在障碍物或未知区域。
- Action客户端提示Action Server未上线:确认
tb3_simulation_launch.py完整启动,bt_navigator正常运行。 - 建图地图重影:小车运动速度太快,降低键盘遥控速度。
高段:
实验5 移动导航实验(ROS2‑Lyrical Nav2)
参考资料:ROS2 Lyrical第5章导航前置、第6章Nav2自主导航
环境:Ubuntu26.04 + ROS2‑Lyrical + Gazebo Garden,差速四轮小车仿真平台
一、实验目的
- 理解Nav2导航四大前置条件:差速底盘、完整TF2坐标树、2D激光雷达、标准里程计Odometry ,掌握导航数据流
传感器→定位→规划→控制→底盘执行完整链路。 - 掌握TF2广播、监听,理解标准坐标链
map → odom → base_footprint → laser_link。 - 掌握gmapping完成SLAM栅格建图、地图保存(map_saver)与加载(map_server)。
- 掌握Nav2配置:代价地图(全局/局部)、AMCL粒子滤波定位、DWB局部规划器参数配置。
- 掌握RViz2导航工具:2D Pose Estimate初始化定位、2D Nav Goal下发导航目标,实现定点自主导航、动态避障。
- 编写Action客户端C++代码,实现程序自动下发导航目标点。
二、实验原理
- TF2坐标变换 :维护机器人各连杆、传感器坐标系之间平移旋转关系,导航中激光雷达数据需要通过TF转换到底盘、地图坐标系下参与代价地图计算;URDF/Xacro+
robot_state_publisher自动广播连杆TF变换。 - 传感器数据
- LaserScan:2D激光雷达输出测距数据,话题
/scan,为SLAM、代价地图提供障碍物观测。 - Odometry里程计:输出机器人相对
odom坐标系位姿、线角速度,Gazebo差速驱动插件积分生成里程;实体机器人通过编码器速度积分得到里程。 /cmd_vel:Twist消息,Nav2规划器输出速度指令,控制差速底盘运动。
- LaserScan:2D激光雷达输出测距数据,话题
- gmapping‑SLAM :粒子滤波SLAM,输入激光+里程计+TF,输出占用栅格地图
/map;地图保存生成.pgm图像和.yaml配置文件。 - Nav2导航栈
- map_server:加载静态栅格地图,发布
/map话题。 - AMCL2:自适应蒙特卡洛粒子滤波,基于已知地图+激光+里程实现全局定位,输出机器人在map坐标系位姿。
- 全局代价地图global_costmap:基于静态地图做长距离全局路径规划。
- 局部代价地图local_costmap:滑动窗口跟随机器人,实时处理动态障碍物。
- DWB局部规划器:接收全局路径,结合运动约束输出
/cmd_vel速度指令,替代ROS1 DWA规划器。
- map_server:加载静态栅格地图,发布
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:编译功能包,启动建图仿真
- 工作空间
~/ros2_ws/src放置chapter5_tutorials源码,编译
bash
cd ~/ros2_ws
colcon build --packages-select chapter5_tutorials
source install/setup.bash
- 启动Gazebo仿真、机器人、gmapping建图、RViz2
bash
ros2 launch chapter5_tutorials gazebo_mapping.launch.py model:=$(ament_index_get_resource robot1_description urdf/robot1_base_04.xacro)
- 键盘遥控包安装,新开终端启动键盘遥控,控制小车遍历全部室内环境
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
PartB Nav2自主导航实验
步骤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参数文件:
costmap_common_params.yaml:代价地图公共参数,设置机器人footprint轮廓,障碍物膨胀半径inflation_radiusglobal_costmap_params.yaml:全局代价地图参数,static_map:true加载静态地图local_costmap_params.yaml:局部代价地图,开启rolling_window:true滑动窗口dwb_local_planner_params.yaml:DWB规划器速度加速度约束,差速小车holonomic_robot: falseamcl_params.yaml:AMCL粒子滤波参数,设置最小最大粒子数、激光观测模型。
步骤2:编写一体化启动文件 nav2_full.launch.py
功能:一键启动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交互导航操作
- 2D Pose Estimate(快捷键P)初始化定位
点击工具栏2D Pose Estimate,在地图上小车真实位置拖拽箭头指定机器人初始位姿。AMCL粒子云从分散逐渐聚拢,代表定位收敛。
现象:红色粒子云收敛到机器人真实位置,定位成功。
-
下发导航目标点2D Nav Goal(快捷键G)
点击
2D Nav Goal,在地图上选择目标位置拖拽确定朝向;Nav2生成绿色全局路径Global Plan、蓝色局部Local Plan,小车自动行驶前往目标,同时局部代价地图实时显示障碍物和膨胀安全区。 -
动态障碍物避障测试
Gazebo界面插入立方体障碍物,放到规划路径中间;观察RViz局部代价地图更新障碍物,Nav2重新规划路径,小车自动绕行避开障碍。
-
动态参数调优
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)目标点。
五、实验现象记录
- TF树检查:rqt_tf_tree,截图记录完整坐标链
map‑odom‑base_footprint‑laser_link。 - SLAM建图:RViz OccupancyGrid截图,记录建好的室内栅格地图。
- AMCL定位:粒子云分散→收敛截图。
- RViz导航截图:全局绿色路径、局部蓝色轨迹、局部代价地图彩色障碍物膨胀区域。
- 动态障碍物:放置障碍物前后路径对比截图,观察绕行效果。
- 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如何利用激光与地图匹配修正里程计漂移?
七、实验报告参考小结
- 本实验完成仿真差速小车环境gmapping‑SLAM建图,得到室内栅格地图,掌握map_saver/map_server地图存取。
- 验证TF2坐标变换树,确认Nav2四大前置条件全部满足。
- 基于Nav2实现AMCL粒子滤波定位,DWB局部规划器完成定点导航,实现动态障碍物避障。
- 通过RViz2交互+Action客户端两种方式下发导航目标,理解
map_server‑AMCL‑costmap‑planner‑controller‑/cmd_vel‑底盘完整数据流。 - 代价地图
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 建图地图扭曲:小车运动过快,激光扫描跟不上,降低键盘遥控移动速度。


