【ROS机器人】—— 从仿真到实战:Gazebo+Rviz+gmapping的SLAM建图与地图持久化
1. 环境准备与基础概念在开始SLAM建图之前我们需要先搭建好ROS开发环境。这里假设你已经安装了Ubuntu系统和ROS推荐Noetic或Melodic版本。如果还没安装可以通过以下命令快速完成sudo apt update sudo apt install ros-noetic-desktop-full安装完成后别忘了初始化rosdep并配置环境变量sudo rosdep init rosdep update echo source /opt/ros/noetic/setup.bash ~/.bashrc source ~/.bashrc为什么选择GazeboRvizgmapping这个组合我在实际项目中发现Gazebo提供了高度可配置的物理仿真环境Rviz能实时可视化传感器数据而gmapping作为经典的2D SLAM算法对新手非常友好。三者配合就像搭积木一样——Gazebo生成虚拟世界gmapping处理激光雷达数据构建地图Rviz则让我们直观看到建图过程。2. 搭建Gazebo仿真环境2.1 创建机器人URDF模型首先我们需要一个带激光雷达的机器人模型。在ROS中通常使用URDF文件描述机器人结构。这里给出一个简化版的四轮小车模型框架!-- 文件保存为car.urdf -- robot namemy_robot link namebase_link visual geometry box size0.4 0.3 0.2/ /geometry /visual collision geometry box size0.4 0.3 0.2/ /geometry /collision /link !-- 添加激光雷达 -- link namelaser_link/ joint namelaser_joint typefixed parent linkbase_link/ child linklaser_link/ origin xyz0.2 0 0.1 rpy0 0 0/ /joint gazebo referencelaser_link sensor typeray namelaser_sensor pose0 0 0 0 0 0/pose visualizetrue/visualize update_rate40/update_rate ray scan horizontal samples720/samples resolution1/resolution min_angle-1.570796/min_angle max_angle1.570796/max_angle /horizontal /scan range min0.1/min max30.0/max /range /ray plugin namelaser_controller filenamelibgazebo_ros_laser.so topicName/scan/topicName frameNamelaser_link/frameName /plugin /sensor /gazebo /robot2.2 启动Gazebo世界创建launch文件car_gazebo.launch来加载机器人模型和仿真环境launch !-- 加载URDF到参数服务器 -- param namerobot_description textfile$(find your_pkg)/urdf/car.urdf / !-- 启动Gazebo -- include file$(find gazebo_ros)/launch/empty_world.launch arg nameworld_name value$(find your_pkg)/worlds/simple_room.world/ /include !-- 在Gazebo中生成机器人 -- node namespawn_urdf pkggazebo_ros typespawn_model args-param robot_description -urdf -model my_robot / /launch启动后会看到一个带激光雷达的小车出现在空房间中。我建议先在Gazebo中添加一些墙壁和障碍物这样建图时能看到更明显的效果。3. 配置gmapping建图节点3.1 安装gmapping包如果还没安装gmapping运行以下命令sudo apt install ros-noetic-slam-gmapping3.2 编写gmapping启动文件创建gmapping.launch文件关键参数我都加了中文注释launch !-- 使用仿真时间 -- param nameuse_sim_time valuetrue/ node pkggmapping typeslam_gmapping nameslam_gmapping outputscreen !-- 激光话题重映射 -- remap fromscan to/scan/ !-- 坐标系设置 -- param namebase_frame valuebase_link / param namemap_frame valuemap / param nameodom_frame valueodom / !-- 地图更新频率 -- param namemap_update_interval value2.0/ !-- 激光参数 -- param namemaxUrange value15.0/ param namesigma value0.05/ param namekernelSize value1/ !-- 运动模型参数 -- param namelinearUpdate value0.5/ param nameangularUpdate value0.5/ param nametemporalUpdate value3.0/ !-- 粒子滤波参数 -- param nameparticles value50/ !-- 地图范围 -- param namexmin value-20.0/ param nameymin value-20.0/ param namexmax value20.0/ param nameymax value20.0/ param namedelta value0.05/ /node /launch3.3 启动建图系统依次打开三个终端分别执行# 终端1启动Gazebo仿真 roslaunch your_pkg car_gazebo.launch # 终端2启动gmapping roslaunch your_pkg gmapping.launch # 终端3启动Rviz rosrun rviz rviz在Rviz中需要手动添加以下显示项添加RobotModel显示机器人URDF模型添加LaserScan并设置Topic为/scan添加Map并设置Topic为/map4. 控制机器人建图4.1 键盘控制移动新开终端安装键盘控制包sudo apt install ros-noetic-teleop-twist-keyboard然后运行rosrun teleop_twist_keyboard teleop_twist_keyboard.py按提示使用键盘控制机器人移动。建议先缓慢绕场一周确保激光雷达能扫描到所有障碍物。4.2 自动建图策略手动控制容易漏扫区域这里分享一个自动建图技巧——编写简单的巡逻脚本patrol.py#!/usr/bin/env python import rospy from geometry_msgs.msg import Twist def patrol(): rospy.init_node(patrol_node) pub rospy.Publisher(/cmd_vel, Twist, queue_size10) rate rospy.Rate(10) while not rospy.is_shutdown(): # 前进2米 move_forward(pub, 2.0) # 原地旋转180度 rotate(pub, 3.14) def move_forward(pub, distance): cmd Twist() cmd.linear.x 0.2 start_time rospy.Time.now() while (rospy.Time.now() - start_time).to_sec() distance/0.2: pub.publish(cmd) rospy.sleep(0.1) cmd.linear.x 0 pub.publish(cmd) def rotate(pub, angle): cmd Twist() cmd.angular.z 0.5 start_time rospy.Time.now() while (rospy.Time.now() - start_time).to_sec() abs(angle)/0.5: pub.publish(cmd) rospy.sleep(0.1) cmd.angular.z 0 pub.publish(cmd) if __name__ __main__: try: patrol() except rospy.ROSInterruptException: pass5. 地图保存与重用5.1 安装map_serversudo apt install ros-noetic-map-server5.2 保存地图当建图完成后新开终端执行rosrun map_server map_saver -f ~/my_map这会生成两个文件my_map.pgm地图图像文件my_map.yaml地图元数据5.3 加载已有地图创建load_map.launchlaunch node namemap_server pkgmap_server typemap_server args$(find your_pkg)/maps/my_map.yaml/ /launch启动后可以在Rviz中添加Map组件查看历史地图。我在实际项目中常用这种方式做多机器人地图共享。6. 常见问题排查问题1地图出现重影检查各坐标系是否正确设置尝试减小map_update_interval参数确保use_sim_time参数一致问题2建图时机器人位置漂移调整gmapping的运动模型参数降低机器人移动速度在Gazebo中检查物理引擎参数问题3保存的地图不完整确保保存前机器人已探索完所有区域检查地图范围参数是否足够大尝试手动调用地图保存服务rosservice call /dynamic_map_save filename: /home/user/final_map