ROS1机器人SLAM系列(四):Gmapping算法详解与实战

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 算法流程概述

Gmapping算法流程

里程计数据

运动预测
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问题分解为:

  1. 位姿估计:使用粒子滤波
  2. 地图构建:在已知位姿的条件下,使用分析方法更新地图

这种分解大大降低了计算复杂度:

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的核心技术,用于精确估计机器人位姿。

步骤

  1. 获取当前激光扫描数据
  2. 与粒子携带的局部地图进行匹配
  3. 找到使匹配度最高的位姿修正

匹配得分计算

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 订阅的话题

话题消息类型说明
/scansensor_msgs/LaserScan激光雷达数据
/tftf/tfMessage坐标变换

必需的TF变换

  • odombase_link:里程计变换
  • base_linklaser:激光雷达位置

3.2 发布的话题

话题消息类型说明
/mapnav_msgs/OccupancyGrid栅格地图
/map_metadatanav_msgs/MapMetaData地图元数据
/tftf/tfMessagemap → odom变换

3.3 提供的服务

服务类型说明
dynamic_mapnav_msgs/GetMap获取当前地图

4. Gmapping参数详解

4.1 粒子滤波参数

# 粒子数量(核心参数)particles:30# 默认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# 移动1米更新一次# 角度运动更新阈值angularUpdate:0.5# 旋转0.5弧度更新一次# 时间更新阈值temporalUpdate:-1.0# -1表示禁用时间更新# 处理周期throttle_scans:1# 每处理1帧扫描

4.5 地图参数

# 地图分辨率delta:0.05# 每个栅格0.05米# 地图大小(初始)xmin:-100.0ymin:-100.0xmax:100.0ymax: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 准备工作

确保已安装必要的包:

# 安装Gmappingsudoaptinstall ros-noetic-gmapping # 安装键盘控制sudoaptinstall ros-noetic-teleop-twist-keyboard # 安装地图服务sudoaptinstall ros-noetic-map-server 

5.2 创建Gmapping Launch文件

创建 gmapping.launch

<launch><!-- Gmapping节点 --><nodepkg="gmapping"type="slam_gmapping"name="slam_gmapping"output="screen"><!-- 基本参数 --><paramname="base_frame"value="base_link"/><paramname="odom_frame"value="odom"/><paramname="map_frame"value="map"/><!-- 激光雷达参数 --><paramname="maxUrange"value="10.0"/><paramname="maxRange"value="12.0"/><paramname="lskip"value="0"/><!-- 粒子滤波参数 --><paramname="particles"value="50"/><paramname="minimumScore"value="50"/><!-- 运动模型参数 --><paramname="srr"value="0.1"/><paramname="srt"value="0.2"/><paramname="str"value="0.1"/><paramname="stt"value="0.2"/><!-- 更新频率 --><paramname="linearUpdate"value="0.5"/><paramname="angularUpdate"value="0.436"/><paramname="temporalUpdate"value="-1.0"/><!-- 地图参数 --><paramname="delta"value="0.05"/><paramname="xmin"value="-50.0"/><paramname="ymin"value="-50.0"/><paramname="xmax"value="50.0"/><paramname="ymax"value="50.0"/><!-- 扫描匹配参数 --><paramname="sigma"value="0.05"/><paramname="kernelSize"value="1"/><paramname="iterations"value="5"/><paramname="lstep"value="0.05"/><paramname="astep"value="0.05"/><paramname="llsamplerange"value="0.01"/><paramname="llsamplestep"value="0.01"/><paramname="lasamplerange"value="0.005"/><paramname="lasamplestep"value="0.005"/></node></launch>

5.3 使用TurtleBot3仿真实战

步骤1:启动仿真环境

# 设置机器人型号exportTURTLEBOT3_MODEL=burger # 启动Gazebo仿真 roslaunch turtlebot3_gazebo turtlebot3_world.launch 

步骤2:启动Gmapping

# 使用TurtleBot3的Gmapping配置 roslaunch turtlebot3_slam turtlebot3_slam.launch slam_methods:=gmapping # 或使用自定义配置 roslaunch my_robot_slam gmapping.launch 

步骤3:启动RViz可视化

# 使用TurtleBot3的RViz配置 roslaunch turtlebot3_gazebo turtlebot3_gazebo_rviz.launch # 在RViz中添加以下显示:# - Map: /map# - LaserScan: /scan# - TF# - RobotModel

步骤4:控制机器人建图

# 键盘遥控 rosrun teleop_twist_keyboard teleop_twist_keyboard.py # 控制键:# u i o# j k l# m , .## i: 前进 ,: 后退# j: 左转 l: 右转# k: 停止

步骤5:保存地图

# 建图完成后保存 rosrun map_server map_saver -f ~/maps/my_map # 会生成两个文件:# - my_map.pgm:地图图像# - my_map.yaml:地图配置

5.4 使用rosbag回放建图

如果有录制的数据包,可以离线建图:

# 播放数据包 rosbag play --clock recorded_data.bag # 启动Gmapping(需要使用仿真时间) roslaunch gmapping.launch use_sim_time:=true 

6. 建图效果优化

6.1 常见问题与解决方案

问题1:地图有重影/不清晰

原因:里程计误差大或参数不匹配

解决方案:

# 增加粒子数particles:80# 调整运动模型参数srr:0.2srt:0.3str:0.2stt:0.3# 降低更新阈值linearUpdate:0.3angularUpdate:0.3

问题2:建图速度慢

原因:粒子数过多或更新过于频繁

解决方案:

# 减少粒子数particles:30# 增加更新阈值linearUpdate:1.0angularUpdate:0.5# 降采样激光数据lskip:1

问题3:地图有漂移

原因:里程计累积误差

解决方案:

# 提高扫描匹配精度minimumScore:100iterations:10# 减小搜索步长lstep:0.02astep:0.02

问题4:无法建图/地图为空

检查项:

# 检查TF树 rosrun tf tf_monitor # 检查必需的TF变换是否存在 rosrun tf tf_echo odom base_link rosrun tf tf_echo base_link laser # 检查激光数据 rostopic echo /scan 

6.2 不同场景的参数建议

小型室内环境(< 100㎡)

particles:30maxUrange:10.0delta:0.05linearUpdate:0.5angularUpdate:0.3

中型室内环境(100-500㎡)

particles:50maxUrange:15.0delta:0.05linearUpdate:0.8angularUpdate:0.4

大型环境(> 500㎡)

particles:100maxUrange:20.0delta:0.1linearUpdate:1.0angularUpdate:0.5

7. Gmapping的优缺点

7.1 优点

  • ✅ 算法成熟,经过大量验证
  • ✅ 实现简单,参数调整直观
  • ✅ 对于中小场景效果良好
  • ✅ 计算资源需求适中
  • ✅ ROS社区支持完善

7.2 缺点

  • ❌ 不支持回环检测
  • ❌ 大场景下粒子数需求高
  • ❌ 依赖较准确的里程计
  • ❌ 只支持2D激光SLAM
  • ❌ 无法处理动态环境

7.3 适用场景

适用不适用
中小型室内环境大规模室外环境
静态环境高度动态环境
有可靠里程计无里程计或里程计很差
实时建图需要极高精度

8. 代码示例:程序中调用Gmapping

8.1 获取地图数据

#!/usr/bin/env python3import rospy from nav_msgs.msg import OccupancyGrid from nav_msgs.srv import GetMap defmap_callback(msg):"""处理地图回调""" width = msg.info.width height = msg.info.height resolution = msg.info.resolution rospy.loginfo(f"地图大小: {width}x{height}, 分辨率: {resolution}m")# 统计占据、空闲、未知栅格 occupied =sum(1for cell in msg.data if cell >50) free =sum(1for cell in msg.data if0<= cell <=50) unknown =sum(1for cell in msg.data if cell ==-1) rospy.loginfo(f"占据: {occupied}, 空闲: {free}, 未知: {unknown}")defget_map_service():"""通过服务获取地图""" rospy.wait_for_service('dynamic_map')try: get_map = rospy.ServiceProxy('dynamic_map', GetMap) response = get_map()return response.mapexcept rospy.ServiceException as e: rospy.logerr(f"服务调用失败: {e}")returnNoneif __name__ =='__main__': rospy.init_node('map_listener')# 方法1:订阅话题 rospy.Subscriber('/map', OccupancyGrid, map_callback)# 方法2:调用服务 current_map = get_map_service() rospy.spin()

8.2 判断建图完成度

#!/usr/bin/env python3import rospy from nav_msgs.msg import OccupancyGrid classMappingMonitor:def__init__(self): self.coverage_history =[] rospy.Subscriber('/map', OccupancyGrid, self.map_callback)defmap_callback(self, msg): total_cells =len(msg.data) known_cells =sum(1for cell in msg.data if cell !=-1) coverage = known_cells / total_cells *100 self.coverage_history.append(coverage)# 判断是否稳定(建图基本完成)iflen(self.coverage_history)>10: recent = self.coverage_history[-10:]ifmax(recent)-min(recent)<0.5: rospy.loginfo(f"建图已稳定,覆盖率: {coverage:.1f}%")if __name__ =='__main__': rospy.init_node('mapping_monitor') monitor = MappingMonitor() rospy.spin()

9. 总结

本文详细介绍了Gmapping算法:

  1. 算法原理:基于RBPF的粒子滤波SLAM
  2. 核心技术:运动模型、扫描匹配、权重计算、重采样
  3. 参数配置:详细解释了各类参数的含义和调整方法
  4. 实战演练:完整的建图流程和优化技巧
  5. 适用场景:明确了Gmapping的优缺点和适用范围

Gmapping作为经典的2D SLAM算法,非常适合学习SLAM的入门者。掌握Gmapping后,下一篇文章将介绍更先进的Cartographer算法。


系列导航:

版权声明:本文为原创文章,转载请注明出处

Read more

多云与混合云架构下的 WebSQL 统一访问平面设计

多云与混合云架构下的 WebSQL 统一访问平面设计

混合云架构下的“网络碎片化”痛点 随着企业业务的全球化与基础设施的演进,“混合云”与“多云”已经成为大型企业的标准配置。一个典型的中大型互联网架构可能包含:部署在本地 IDC 物理机上的核心账务 MySQL、运行在阿里云 VPC 内的边缘业务 PolarDB,以及托管在 AWS 上的海外业务 RDS。 当系统正常运行时,这种多云架构能够提供极高的可用性。但在发生跨域故障,研发人员需要排查一条贯穿多云的业务链路时,数据库层面的**“网络碎片化”**痛点便暴露无遗。 为了执行几个简单的 SELECT 语句对比两边的数据,开发人员不得不在各种内网 VPN、堡垒机客户端、以及各家云厂商自带的网页终端之间来回切换。这种割裂的体验不仅极其痛苦、耗费大量排障时间,而且在跨网段的数据查询中,往往伴随着专线带宽被挤占以及明文传输的安全风险。 一、 终结 VPN 噩梦:控制面与数据面分离的底层重构 在多云环境下,传统的“开通 VPN 打通所有网络”或者“

By Ne0inhk
C++ 方向 Web 自动化测试入门指南:从概念到 Selenium 实战

C++ 方向 Web 自动化测试入门指南:从概念到 Selenium 实战

🔥草莓熊Lotso:个人主页 ❄️个人专栏: 《C++知识分享》《Linux 入门到实践:零基础也能懂》 ✨生活是默默的坚持,毅力是永久的享受! 🎬 博主简介: 文章目录 * 前言: * 一. 自动化测试基础:先搞懂"为什么"和"做什么" * 1.1 自动化测试的核心目标:回归测试 * 1.2 自动化测试分类:别把 “不同自动化” 混为一谈 * 1.3 自动化测试金字塔:如何分配测试资源? * 二. Web 自动化测试核心:环境搭建与驱动管理 * 2.1 核心组件原理:三者如何协同工作? * 2.2 环境搭建:3 步搞定依赖安装

By Ne0inhk
【前端实战】Axios 错误处理的设计与进阶封装,实现网络层面的数据与状态解耦

【前端实战】Axios 错误处理的设计与进阶封装,实现网络层面的数据与状态解耦

目录 【前端实战】Axios 错误处理的设计与进阶封装,实现网络层面的数据与状态解耦 一、为什么网络错误处理一定要下沉到 Axios 层 二、Axios 拦截器 interceptors 1、拦截器的基础应用 2、错误分级和策略映射的设计 3、错误对象标准化 三、结语         作者:watermelo37         ZEEKLOG优质创作者、华为云云享专家、阿里云专家博主、腾讯云“创作之星”特邀作者、火山KOL、支付宝合作作者,全平台博客昵称watermelo37。         一个假装是giser的coder,做不只专注于业务逻辑的前端工程师,Java、Docker、Python、LLM均有涉猎。 --------------------------------------------------------------------- 温柔地对待温柔的人,包容的三观就是最大的温柔。 --------------------------------------------------------------------- 【前

By Ne0inhk
Flutter for OpenHarmony: Flutter 三方库 flutter_cors 应对鸿蒙 Web 与混合开发中的跨域挑战(网络兼容方案)

Flutter for OpenHarmony: Flutter 三方库 flutter_cors 应对鸿蒙 Web 与混合开发中的跨域挑战(网络兼容方案)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在进行 OpenHarmony 的跨平台开发时,我们不仅开发原生 HAP,有时也会涉及 Flutter Web 或是在鸿蒙端侧运行 Webview 混合应用。这时,一个经典的“拦路虎”就会出现:CORS (跨源资源共享) 限制。当你的 Web 端尝试访问一个未配置跨域头部的后端 API 时,请求会被浏览器拦截,报错信息极其晦涩。 虽然 CORS 主要是后端的工作,但 flutter_cors 提供了一种客户端视角的辅助工具。它通过工具化手段帮助开发者分析、绕过或生成跨域适配规则,是保证鸿蒙跨平台 Web 项目顺利运行的调试利器。 一、跨域访问逻辑模型 CORS 是一种浏览器的安全保护机制,它在请求发出前先进行“预检(Preflight)

By Ne0inhk