跳到主要内容ROS1 机器人 SLAM:Gmapping 算法详解与实战 | 极客日志C++AI算法
ROS1 机器人 SLAM:Gmapping 算法详解与实战
Gmapping 是基于粒子滤波的 2D 激光 SLAM 算法,适用于中小规模室内环境。解析其 RBPF 原理、运动模型及扫描匹配机制,提供 ROS 参数配置指南与 TurtleBot3 建图实战步骤。涵盖常见地图漂移、重影问题的解决方案,并给出 Python 调用地图服务的代码示例,适合 SLAM 入门者掌握核心流程与优化技巧。
人间过客10 浏览 ROS1 机器人 SLAM:Gmapping 算法详解与实战
1. Gmapping 算法简介
1.1 什么是 Gmapping?
Gmapping 是一种基于粒子滤波(Rao-Blackwellized Particle Filter, RBPF)的 2D 激光 SLAM 算法。它由 Giorgio Grisetti 等人于 2007 年提出,是 ROS 中最经典、应用最广泛的 SLAM 算法之一。
主要特点:
- 基于粒子滤波的概率框架
- 适用于 2D 激光雷达
- 需要里程计信息
- 实现成熟,稳定可靠
- 适合中小规模室内环境
1.2 算法流程概述
Gmapping 的核心流程可以概括为:
- 运动预测:利用里程计数据更新粒子集合。
- 扫描匹配:将激光雷达数据与粒子携带的局部地图进行匹配。
- 观测更新:计算粒子权重并执行重采样。
- 地图更新:根据最优位姿更新全局栅格地图。
2. 核心算法原理
2.1 粒子滤波基础
粒子滤波是一种序贯蒙特卡洛方法,通过一组带权重的粒子来近似表示后验概率分布。
基本思想:
- 每个粒子代表机器人可能的位姿
- 粒子带有权重,表示该位姿的可信度
- 通过迭代更新粒子来跟踪机器人位姿
数学表示:
P(x_t | z_{1:t}, u_{1:t}) ≈ Σ w_t^{[i]} δ(x_t - x_t^{[i]})
其中:
x_t^{[i]} 是第 i 个粒子的位姿
w_t^{[i]} 是第 i 个粒子的权重
- δ 是狄拉克函数
2.2 Rao-Blackwellized 粒子滤波
Gmapping 采用 RBPF,将 SLAM 问题分解为:
- 位姿估计:使用粒子滤波
- 地图构建:在已知位姿的条件下,使用分析方法更新地图
这种分解大大降低了计算复杂度:
P(x_{1:t}, m | z_{1:t}, u_{1:t}) = P(m | x_{1:t}, z_{1:t}) × P(x_{1:t} | z_{1:t}, u_{1:t})
2.3 运动模型
Gmapping 使用概率运动模型预测粒子位置。实际代码中通常包含噪声项,简化逻辑如下:
x' = x + Δx + noise_x
y' = y + Δy + noise_y
θ' = θ + Δθ + noise_θ
其中噪声与运动量和参数(srr, srt, str, stt)相关。
2.4 扫描匹配(Scan Matching)
扫描匹配是 Gmapping 的核心技术,用于精确估计机器人位姿。
- 获取当前激光扫描数据
- 与粒子携带的局部地图进行匹配
- 找到使匹配度最高的位姿修正
score = Σ map[endpoint] × beam_weight
2.5 权重计算与重采样
weight[i] ∝ P(z_t | x_t^{[i]}, m^{[i]})
Gmapping 采用自适应重采样策略,只在粒子退化严重时才进行重采样:
N_eff = 1 / Σ(w^{[i]})²
if N_eff < threshold: resample()
3. Gmapping 的 ROS 实现
3.1 订阅的话题
| 话题 | 消息类型 | 说明 |
|---|
/scan | sensor_msgs/LaserScan | 激光雷达数据 |
/tf | tf/tfMessage | 坐标变换 |
odom → base_link:里程计变换
base_link → laser:激光雷达位置
3.2 发布的话题
| 话题 | 消息类型 | 说明 |
|---|
/map | nav_msgs/OccupancyGrid | 栅格地图 |
/map_metadata | nav_msgs/MapMetaData | 地图元数据 |
/tf | tf/tfMessage | map → odom 变换 |
3.3 提供的服务
| 服务 | 类型 | 说明 |
|---|
dynamic_map | nav_msgs/GetMap | 获取当前地图 |
4. Gmapping 参数详解
4.1 粒子滤波参数
particles: 30
resampleThreshold: 0.5
minimumScore: 0.0
- 小场景:30-50 个粒子
- 大场景:80-100 个粒子
- 实时性要求高:减少粒子数
4.2 激光雷达参数
maxUrange: 80.0
maxRange: 80.0
lskip: 0
4.3 运动模型参数
srr: 0.1
srt: 0.2
str: 0.1
stt: 0.2
- 值越大,表示里程计误差越大
- 轮式里程计较准:0.05-0.1
- 里程计不准:0.2-0.5
4.4 更新频率参数
linearUpdate: 1.0
angularUpdate: 0.5
temporalUpdate: -1.0
throttle_scans: 1
4.5 地图参数
delta: 0.05
xmin: -100.0
ymin: -100.0
xmax: 100.0
ymax: 100.0
occ_thresh: 0.25
4.6 扫描匹配参数
sigma: 0.05
kernelSize: 1
iterations: 5
lstep: 0.05
astep: 0.05
llsamplerange: 0.01
llsamplestep: 0.01
lasamplerange: 0.005
lasamplestep: 0.005
5. 实战:使用 Gmapping 建图
5.1 准备工作
sudo apt install ros-noetic-gmapping
sudo apt install ros-noetic-teleop-twist-keyboard
sudo apt install ros-noetic-map-server
5.2 创建 Gmapping Launch 文件
<launch>
<node pkg="gmapping" type="slam_gmapping" name="slam_gmapping" output="screen">
<param name="base_frame" value="base_link"/>
<param name="odom_frame" value="odom"/>
<param name="map_frame" value="map"/>
<param name="maxUrange" value="10.0"/>
<param name="maxRange" value="12.0"/>
<param name="lskip" value="0"/>
<param name="particles" value="50"/>
<param name="minimumScore" value="50"/>
<param name="srr" value="0.1"/>
<param name="srt" value="0.2"/>
<param name="str" value="0.1"/>
<param name="stt" value="0.2"/>
<param name="linearUpdate" value="0.5"/>
<param name="angularUpdate" value="0.436"/>
<param name="temporalUpdate" value="-1.0"/>
<param name="delta" value="0.05"/>
<param name="xmin" value="-50.0"/>
<param name="ymin" value="-50.0"/>
<param name="xmax" value="50.0"/>
<param name="ymax" value="50.0"/>
<param name="sigma" value="0.05"/>
<param name="kernelSize" value="1"/>
<param name="iterations" value="5"/>
<param name="lstep" value="0.05"/>
<param name="astep" value="0.05"/>
<param name="llsamplerange" value="0.01"/>
<param name="llsamplestep" value="0.01"/>
<param name="lasamplerange" value="0.005"/>
<param name="lasamplestep" value="0.005"/>
</node>
</launch>
5.3 使用 TurtleBot3 仿真实战
export TURTLEBOT3_MODEL=burger
roslaunch turtlebot3_gazebo turtlebot3_world.launch
可以使用 TurtleBot3 的内置配置,或者加载自定义 launch 文件:
roslaunch turtlebot3_slam turtlebot3_slam.launch slam_methods:=gmapping
roslaunch my_robot_slam gmapping.launch
roslaunch turtlebot3_gazebo turtlebot3_gazebo_rviz.launch
- Map:
/map
- LaserScan:
/scan
- TF
- RobotModel
rosrun teleop_twist_keyboard teleop_twist_keyboard.py
u: 前进,,: 后退
j: 左转,l: 右转
k: 停止
建图完成后,使用 map_server 工具保存:
rosrun map_server map_saver -f ~/maps/my_map
my_map.pgm:地图图像
my_map.yaml:地图配置
5.4 使用 rosbag 回放建图
rosbag play --clock recorded_data.bag
roslaunch gmapping.launch use_sim_time:=true
6. 建图效果优化
6.1 常见问题与解决方案
原因通常是里程计误差大或参数不匹配。可以尝试增加粒子数并调整运动模型参数:
particles: 80
srr: 0.2
srt: 0.3
str: 0.2
stt: 0.3
linearUpdate: 0.3
angularUpdate: 0.3
particles: 30
linearUpdate: 1.0
angularUpdate: 0.5
lskip: 1
这通常源于里程计累积误差,提高扫描匹配精度有助于缓解:
minimumScore: 100
iterations: 10
lstep: 0.02
astep: 0.02
rosrun tf tf_monitor
rosrun tf tf_echo odom base_link
rosrun tf tf_echo base_link laser
rostopic echo /scan
6.2 不同场景的参数建议
particles: 30
maxUrange: 10.0
delta: 0.05
linearUpdate: 0.5
angularUpdate: 0.3
particles: 50
maxUrange: 15.0
delta: 0.05
linearUpdate: 0.8
angularUpdate: 0.4
particles: 100
maxUrange: 20.0
delta: 0.1
linearUpdate: 1.0
angularUpdate: 0.5
7. Gmapping 的优缺点
7.1 优点
- ✅ 算法成熟,经过大量验证
- ✅ 实现简单,参数调整直观
- ✅ 对于中小场景效果良好
- ✅ 计算资源需求适中
- ✅ ROS 社区支持完善
7.2 缺点
- ❌ 不支持回环检测
- ❌ 大场景下粒子数需求高
- ❌ 依赖较准确的里程计
- ❌ 只支持 2D 激光 SLAM
- ❌ 无法处理动态环境
7.3 适用场景
| 适用 | 不适用 |
|---|
| 中小型室内环境 | 大规模室外环境 |
| 静态环境 | 高度动态环境 |
| 有可靠里程计 | 无里程计或里程计很差 |
| 实时建图 | 需要极高精度 |
8. 代码示例:程序中调用 Gmapping
8.1 获取地图数据
下面是一个 Python 脚本示例,演示如何订阅地图话题并通过服务获取地图:
import rospy
from nav_msgs.msg import OccupancyGrid
from nav_msgs.srv import GetMap
def map_callback(msg):
"""处理地图回调"""
width = msg.info.width
height = msg.info.height
resolution = msg.info.resolution
rospy.loginfo(f"地图大小:{width}x{height}, 分辨率:{resolution}m")
occupied = sum(1 for cell in msg.data if cell > 50)
free = sum(1 for cell in msg.data if 0 <= cell <= 50)
unknown = sum(1 for cell in msg.data if cell == -1)
rospy.loginfo(f"占据:{occupied}, 空闲:{free}, 未知:{unknown}")
def get_map_service():
"""通过服务获取地图"""
rospy.wait_for_service('dynamic_map')
try:
get_map = rospy.ServiceProxy('dynamic_map', GetMap)
response = get_map()
return response.map
except rospy.ServiceException as e:
rospy.logerr(f"服务调用失败:{e}")
return None
if __name__ == '__main__':
rospy.init_node('map_listener')
rospy.Subscriber('/map', OccupancyGrid, map_callback)
current_map = get_map_service()
rospy.spin()
8.2 判断建图完成度
import rospy
from nav_msgs.msg import OccupancyGrid
class MappingMonitor:
def __init__(self):
self.coverage_history = []
rospy.Subscriber('/map', OccupancyGrid, self.map_callback)
def map_callback(self, msg):
total_cells = len(msg.data)
known_cells = sum(1 for cell in msg.data if cell != -1)
coverage = known_cells / total_cells * 100
self.coverage_history.append(coverage)
if len(self.coverage_history) > 10:
recent = self.coverage_history[-10:]
if max(recent) - min(recent) < 0.5:
rospy.loginfo(f"建图已稳定,覆盖率:{coverage:.1f}%")
if __name__ == '__main__':
rospy.init_node('mapping_monitor')
monitor = MappingMonitor()
rospy.spin()
9. 总结
- 算法原理:基于 RBPF 的粒子滤波 SLAM
- 核心技术:运动模型、扫描匹配、权重计算、重采样
- 参数配置:详细解释了各类参数的含义和调整方法
- 实战演练:完整的建图流程和优化技巧
- 适用场景:明确了 Gmapping 的优缺点和适用范围
Gmapping 作为经典的 2D SLAM 算法,非常适合学习 SLAM 的入门者。掌握 Gmapping 后,后续可进一步探索更先进的 Cartographer 等算法。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- 随机西班牙地址生成器
随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online