跳到主要内容ROS1 机器人 SLAM 系列(四):Gmapping 算法详解与实战 | 极客日志PythonAI算法
ROS1 机器人 SLAM 系列(四):Gmapping 算法详解与实战
ROS1 机器人 SLAM 系列(四):Gmapping 算法详解与实战 > 将深入讲解 Gmapping 算法的原理,并通过实战演示如何使用 Gmapping 进行 2D 激光 SLAM 建图。 Gmapping 算法简介 1.1 什么是 Gmapping? Gmapping 是一种基于**粒子滤波(Rao-Blackwellized Particle Filter, RBPF)**的 2D…
开源信徒76K 浏览 ROS1 机器人 SLAM 系列(四):Gmapping 算法详解与实战
本文将深入讲解 Gmapping 算法的原理,并通过实战演示如何使用 Gmapping 进行 2D 激光 SLAM 建图。
1. Gmapping 算法简介
1.1 什么是 Gmapping?
Gmapping 是一种基于**粒子滤波(Rao-Blackwellized Particle Filter, RBPF)**的 2D 激光 SLAM 算法。它由 Giorgio Grisetti 等人于 2007 年提出,是 ROS 中最经典、应用最广泛的 SLAM 算法之一。
- 基于粒子滤波的概率框架
- 适用于 2D 激光雷达
- 需要里程计信息
- 实现成熟,稳定可靠
- 适合中小规模室内环境
1.2 算法流程概述
- 里程计数据 -> 运动预测 (Motion Model)
- 粒子集合更新
- 激光雷达数据 -> 扫描匹配 (Scan Matching)
- 观测更新 (Sensor Model)
- 粒子权重计算
- 重采样 (Resample)
- 地图更新
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 运动模型
// 简化的运动模型
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
roslaunch turtlebot3_slam turtlebot3_slam.launch slam_methods:=gmapping
roslaunch my_robot_slam gmapping.launch
roslaunch turtlebot3_gazebo turtlebot3_gazebo_rviz.launch
rosrun teleop_twist_keyboard teleop_twist_keyboard.py
rosrun map_server map_saver -f ~/maps/my_map
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 获取地图数据
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 算法。
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- curl 转代码
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online