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为什么选择Gazebo+Rviz+gmapping这个组合?我在实际项目中发现,Gazebo提供了高度可配置的物理仿真环境,Rviz能实时可视化传感器数据,而gmapping作为经典的2D SLAM算法,对新手非常友好。三者配合就像搭积木一样——Gazebo生成虚拟世界,gmapping处理激光雷达数据构建地图,Rviz则让我们直观看到建图过程。
2. 搭建Gazebo仿真环境
2.1 创建机器人URDF模型
首先我们需要一个带激光雷达的机器人模型。在ROS中,通常使用URDF文件描述机器人结构。这里给出一个简化版的四轮小车模型框架:
<!-- 文件保存为car.urdf --> <robot name="my_robot"> <link name="base_link"> <visual> <geometry> <box size="0.4 0.3 0.2"/> </geometry> </visual> <collision> <geometry> <box size="0.4 0.3 0.2"/> </geometry> </collision> </link> <!-- 添加激光雷达 --> <link name="laser_link"/> <joint name="laser_joint" type="fixed"> <parent link="base_link"/> <child link="laser_link"/> <origin xyz="0.2 0 0.1" rpy="0 0 0"/> </joint> <gazebo reference="laser_link"> <sensor type="ray" name="laser_sensor"> <pose>0 0 0 0 0 0</pose> <visualize>true</visualize> <update_rate>40</update_rate> <ray> <scan> <horizontal> <samples>720</samples> <resolution>1</resolution> <min_angle>-1.570796</min_angle> <max_angle>1.570796</max_angle> </horizontal> </scan> <range> <min>0.1</min> <max>30.0</max> </range> </ray> <plugin name="laser_controller" filename="libgazebo_ros_laser.so"> <topicName>/scan</topicName> <frameName>laser_link</frameName> </plugin> </sensor> </gazebo> </robot>2.2 启动Gazebo世界
创建launch文件car_gazebo.launch来加载机器人模型和仿真环境:
<launch> <!-- 加载URDF到参数服务器 --> <param name="robot_description" textfile="$(find your_pkg)/urdf/car.urdf" /> <!-- 启动Gazebo --> <include file="$(find gazebo_ros)/launch/empty_world.launch"> <arg name="world_name" value="$(find your_pkg)/worlds/simple_room.world"/> </include> <!-- 在Gazebo中生成机器人 --> <node name="spawn_urdf" pkg="gazebo_ros" type="spawn_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 name="use_sim_time" value="true"/> <node pkg="gmapping" type="slam_gmapping" name="slam_gmapping" output="screen"> <!-- 激光话题重映射 --> <remap from="scan" to="/scan"/> <!-- 坐标系设置 --> <param name="base_frame" value="base_link" /> <param name="map_frame" value="map" /> <param name="odom_frame" value="odom" /> <!-- 地图更新频率 --> <param name="map_update_interval" value="2.0"/> <!-- 激光参数 --> <param name="maxUrange" value="15.0"/> <param name="sigma" value="0.05"/> <param name="kernelSize" value="1"/> <!-- 运动模型参数 --> <param name="linearUpdate" value="0.5"/> <param name="angularUpdate" value="0.5"/> <param name="temporalUpdate" value="3.0"/> <!-- 粒子滤波参数 --> <param name="particles" value="50"/> <!-- 地图范围 --> <param name="xmin" value="-20.0"/> <param name="ymin" value="-20.0"/> <param name="xmax" value="20.0"/> <param name="ymax" value="20.0"/> <param name="delta" value="0.05"/> </node> </launch>3.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为/map
4. 控制机器人建图
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_size=10) 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_server
sudo 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.launch:
<launch> <node name="map_server" pkg="map_server" type="map_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'"