ROS快速入门教程06
20.SLAM基本介绍
SLAM(Simultaneous Localization and Mapping,即“同步定位与建图”)是移动机器人和增强现实等领域的核心技术。其主要目标是在未知环境中,机器人(或传感器平台)一边移动一边:
- 定位(Localization):估计自身在环境中的位置和姿态;
- 建图(Mapping):构建周围环境的地图模型。
两者相辅相成:准确的定位依赖于对环境地图的认识,而地图的生成又需要知道机器人的位置,因此需要同时进行。
20.1 SLAM 的基本流程
- 数据采集
- 传感器:激光雷达(LiDAR)、深度相机、普通摄像头、IMU(惯性测量单元)、超声波等。
- 特征提取与匹配
- 从传感器数据中提取“特征”(比如激光扫描的角点、图像中的关键点),并在连续帧或前后观测中进行匹配。
- 状态估计
- 使用滤波(如EKF、UKF)、粒子滤波(FastSLAM)、图优化(GraphSLAM)等方法,对机器人的位姿和地图特征一起进行联合估计。
- 地图更新
- 将新观测整合进地图,常见地图表现形式有栅格地图(Occupancy Grid)、点云地图、拓扑地图等。
- 回环检测(Loop Closure)
- 当机器人回到之前走过的地方,识别出“回环”并进行全局优化,修正累积漂移。
20.2 主要算法流派
流派 | 代表方法 | 特点 |
---|---|---|
滤波式 SLAM | EKF-SLAM、UKF-SLAM | 实时性好,算法框架简单;状态量随着特征增多线性增长,规模化受限。 |
粒子滤波 SLAM | FastSLAM 家族 | 用粒子表示机器人轨迹,地图可分布式维护;适合高维地图,但粒子数需调优。 |
图优化 SLAM | GraphSLAM、g2o、Ceres | 将轨迹与特征构建成图,收敛至全局最优;后端优化耗时较大,需做好稀疏化。 |
视觉 SLAM | PTAM、ORB-SLAM、LSD-SLAM | 只用相机即可建图,成本低;对光照与纹理依赖强,对大动态场景鲁棒性较差。 |
激光 SLAM | GMapping、Hector SLAM、Cartographer | 激光数据精度高,对照明不敏感;成本和电量需求较高。 |
20.3 在 ROS 中的典型实现
- gmapping:基于粒子滤波的 2D 栅格地图构建,入门常用。
- hector_slam:无需里程计,依赖高频激光;对快速运动场景适应性好。
- cartographer(Google):图优化结合扫描匹配,支持 2D/3D,精度与效率兼备。
- ORB_SLAM2/3:常见视觉 SLAM 包,支持单目/双目/RGB-D,提供回环检测与全局优化。
使用时通常需要配置对应节点(如 slam_gmapping
、cartographer_node
、orb_slam2
),并将激光或相机话题、里程计话题正确 remap,调节参数(如分辨率、窗口大小、回环检测频率等)以获得稳定的建图效果。
20.4 应用场景
- 自主导航:机器人扫地机、配送机器人在室内环境中自主行走。
- 自动驾驶:结合 GPS 外,还可用 LiDAR SLAM 实时感知周围。
- 增强/虚拟现实:在室内外构建稠密点云,实现虚拟物体与现实世界的精确叠加。
- 测绘与考古:无人机搭载激光雷达进行地形建模。
21.ROS中实现SLAM建图
实现思路:机器人通过激光雷达驱动节点发布一个雷达数据话题/scan
,而SLAM节点订阅该话题就能获取测距数值。SLAM节点发布地图/map
话题,生成地图消息。再使用Rviz界面显示地图的形状。
21.1 hector_mapping
sudo apt install ros-noetic-hector-mapping
roslaunch wpr_simulation wpb_stage_slam.launch
rosrun hector_mapping hector_mapping #newone
rviz #newone
rosrun rqt_robot_steering rqt_robot_steering #可以通过该软件包控制移动
21.2 通过launch文件启动Hector_Mapping的建图功能
cd catkin_ws/src/
catkin_create_pkg slam_pkg roscpp rospy std_mgs
mkdir launch
cd launch
vim hector.launch
<launch><!-- 载入 机器人 和 SLAM 的仿真场景 --><include file="$(find wpr_simulation)/launch/wpb_stage_slam.launch"/><!-- Hector SLAM --><node pkg="hector_mapping" type="hector_mapping" name="hector_mapping"><param name="tf_map_scanmatch_transform_frame_name" value="base_footprint" /></node><!-- Rviz 显示 --><node pkg="rviz" type="rviz" name="rviz" args="-d $(find wpr_simulation)/rviz/slam.rviz"/><!-- 运动控制 --><node pkg="rqt_robot_steering" type="rqt_robot_steering" name="rqt_robot_steering"/></launch>
catkin_make
roslaunch slam_pkg hector.launch
21.3 Hector_Mapping建图的参数设置
学习hector_mapping的更多参数,可以打开wiki网站进行搜索都有哪些参数。
这里重新编写hector.launch文件学习参数
<launch><!-- 第一个 Hector_Mapping 建图节点 --><group ns="slam_1"><node pkg="hector_mapping" type="hector_mapping" name="hector_mapping_1"><param name="map_update_distance_thresh" value="0.4"/><param name="map_update_angle_thresh" value="0.9" /><param name="map_pub_period" value="0.2" /><param name="map_frame" value="slam_1/map" /><param name="base_frame" value="slam_1/base_footprint" /><param name="odom_frame" value="slam_1/odom" /></node></group><!-- 第二个 Hector_Mapping 建图节点 --><group ns="slam_2"><node pkg="hector_mapping" type="hector_mapping" name="hector_mapping_2"><param name="map_update_distance_thresh" value="0.1"/><param name="map_update_angle_thresh" value="0.1" /><param name="map_pub_period" value="0.2" /><param name="map_frame" value="slam_2/map" /><param name="base_frame" value="slam_2/base_footprint" /><param name="odom_frame" value="slam_2/odom" /></node></group><!-- **************************** 分割线 **************************** --><!-- 载入 SLAM 的仿真场景 --><include file="$(find gazebo_ros)/launch/empty_world.launch"><arg name="world_name" value="$(find wpr_simulation)/worlds/slam_simple.world"/><arg name="paused" value="false"/><arg name="use_sim_time" value="true"/><arg name="gui" value="true"/><arg name="recording" value="false"/><arg name="debug" value="false"/></include><!-- 载入 1号机器人 --><include file="$(find wpr_simulation)/launch/wpb_slam_template.launch"><arg name="robot_namespace" value="slam_1" /> <arg name="local_x" value="0" /> <arg name="local_y" value="-0.3" /> <arg name="local_yaw" value="0" /> </include><!-- 载入 2号机器人 --><include file="$(find wpr_simulation)/launch/wpb_slam_template.launch"><arg name="robot_namespace" value="slam_2" /> <arg name="local_x" value="0" /> <arg name="local_y" value="0.3" /> <arg name="local_yaw" value="0" /> </include><!-- 运动控制 --><node pkg="rqt_robot_steering" type="rqt_robot_steering" name="rqt_robot_steering"/><!-- 速度话题分流 --><node pkg = "topic_tools" type = "relay" name = "relay_1" args="/cmd_vel /slam_1/cmd_vel" /><node pkg = "topic_tools" type = "relay" name = "relay_2" args="/cmd_vel /slam_2/cmd_vel" /></launch>
22. 初识ROS的TF系统
机器人移动建图原理:机器人一边移动一边建图。为了方便描述机器人和这个地图坐标系远点的空间关系,我们分别定义两个坐标系,首先是地图坐标系,原点在机器人建图的初始位置,坐标轴方向遵循ROS的右手法则,命名为map
。机器人坐标系在机器人底盘中心,坐标轴方向遵循ROS的右手法则,命名为base_footprint
。base_footprint
为父坐标系,map
为子坐标系。那么描述机器人的空间位置,则可以说父坐标系相对于子坐标系的空间偏移量xyz
tf2_msgs消息格式结构
22.1 里程计的介绍
里程计(Odometry)指的是机器人利用自身运动传感器(如轮速编码器、IMU、视觉相机等)对自身位姿(位置和朝向)增量变化进行估计的过程。它常作为 SLAM 中运动预测(Motion Model)的输入,帮助滤波或图优化方法预测下一时刻的位置,然后与观测(传感器测量)一同进行融合校正。
1. 里程计的来源
- 轮式里程计(Wheel Odometry)
- 利用轮子上的编码器(Encoder)测量转动圈数,再根据轮子半径与轴距计算出线速度和角速度。
- 优点:实现简单、计算量小;
- 缺点:易受轮胎打滑、地面不平整影响,累计误差(漂移)随时间线性增长。
- 惯性里程计(Inertial Odometry)
- 利用 IMU(加速度计+陀螺仪)对加速度和角速度进行积分得到位移与姿态变化。
- 优点:高频率更新,对快速运动响应好;
- 缺点:噪声和偏置导致二次积分误差爆炸式增长。
- 视觉里程计(Visual Odometry, VO)
- 通过对连续图像帧(单目、双目或 RGB-D)中提取特征点并匹配,估计相机的运动变换。
- 优点:拒绝轮滑、对外部环境敏感度高;
- 缺点:对光照和纹理依赖大,计算量相对较高。
- 激光里程计(LiDAR Odometry)
- 基于激光扫描点云的配准算法(如 ICP、NDT)来估计帧间变换。
- 优点:精度高、不受光照影响;
- 缺点:对场景几何特征要求较高,实时性和计算资源要求大。
2. 里程计在 SLAM 中的作用
- 预测运动
SLAM 算法通常分为预测(Prediction)和校正(Correction)两步。里程计提供的位姿增量用于预测机器人下一时刻的位姿先验。 - 构建运动模型
在滤波式 SLAM(如 EKF-SLAM、FastSLAM)中,里程计噪声模型决定了状态协方差的增长;在图优化 SLAM(如 g2o、Ceres)中,则是构建运动边(odometry edge)的残差项。 - 回环检测辅助
虽然回环检测更多依赖观测(特征或扫描匹配),但精准的里程计能缩小搜索范围,提高回环匹配速度与正确率。
3. 局限与改进
- 漂移累积
所有纯里程计方法都会随时间累积漂移,短时间内可以较准,长时间运行误差会越来越大。 - 多传感器融合
- 将轮式/惯性/视觉/激光里程计与环境观测(SLAM 的 Mapping 部分)融合,可显著抑制漂移。
- 常见做法有扩展卡尔曼滤波融合(EKF Fusion)、滑动窗口优化(Windowed BA)或因子图优化(Factor Graph)。
- 里程计校准
- 定期标定编码器、IMU 的偏置,并在线估计传感器健康状态;
- 视觉里程计中进行相机内外参标定。
- 去耦与冗余
- 多种里程计相互校验,比如将视觉里程计与轮式里程计融合,检测轮滑或视觉失效;
- 在关键时刻启用备用传感器,保证系统鲁棒性。
23. 使用Gmapping进行SLAM建图
gMapping 是 ROS 生态中最常用的 2D 激光 SLAM(同步定位与建图)开源实现之一,基于 Rao–Blackwellized 粒子滤波(RBPF)算法,能够在未知环境中实时构建栅格地图。
23.1算法原理
- Rao–Blackwellized 粒子滤波
gMapping 将机器人的轨迹作为粒子滤波器的随机变量,用一组粒子 (x_{1:t}k)k=1M_{k=1}M 来表示可能的历史轨迹。
对每个粒子,都维护一份局部地图(占据栅格),并通过激光观测更新该地图的概率。 - 粒子权重计算
- 根据运动模型(里程计增量)对每个粒子进行预测;
- 用当前激光扫描对每个粒子的地图进行似然评估,计算权重;
- 根据权重进行重采样,保留最有可能的轨迹和地图。
- 地图合并
最终输出由权重最高的粒子所维护的地图,或对多粒子地图进行加权融合以提高鲁棒性。
23.2 主要特点
- 实时性:在普通笔记本/嵌入式平台上即可运行,适合室内移动机器人。
- 低成本传感器:仅需 2D 激光雷达和轮式里程计(或里程计+IMU)。
- 易于集成:作为 ROS 包
slam_gmapping
,与roslaunch
、tf
、rviz
等工具无缝对接。 - 可调参数丰富:分辨率、粒子数、重采样阈值等,可根据场景和算力灵活调节。
23.3 快速上手
-
安装
sudo apt-get install ros-<distro>-slam-gmapping
-
运行示例
在已有激光和里程计话题的机器人系统中,可以启动:roslaunch slam_gmapping slam_gmapping.launch \scan:=/base_scan \odom:=/odom \map_update_interval:=5.0
scan
:激光雷达的话题名odom
:里程计话题map_update_interval
:地图更新周期(秒)
-
可视化
rosrun rviz rviz
在 RViz 中添加 “Map” 及 “LaserScan” 显示,实时查看建图效果。
23.4 关键参数调优
参数 | 含义 | 建议范围 |
---|---|---|
particles | 粒子数量 | 30 – 200 |
xmin /ymin /xmax /ymax | 地图边界(m) | 根据环境大小设定 |
delta | 栅格分辨率(m) | 0.02 – 0.1 |
sigma | 激光测量噪声标准差 | 0.01 – 0.1 |
kernelSize | 占据概率核大小 | 1 – 3 |
resampleThreshold | 重采样阈值(有效粒子比例) | 0.5 – 0.8 |
lsigma /ogain | 激光局部平滑与增益,用于改善小环境的匹配 | 默认或微调 |
- 增加
particles
可提高鲁棒性,但占用更多 CPU。 - 缩小
delta
可提升地图精度,但会放大噪声并增加计算量。 resampleThreshold
决定何时触发重采样,过低会导致样本贫化,过高会过早重采样。
23.5 优缺点
- 优点
- 算法成熟、社区活跃;
- 对里程计噪声和部分动态障碍有一定容忍;
- 可与 ROS 中其他包(如 AMCL、move_base)配合做导航。
- 缺点
- 粒子滤波的粒子数随环境复杂度需求增加,CPU 占用较高;
- 对大尺度或户外场景支持有限,仅适合平面、结构化环境;
- 没有回环检测模块,对长期建图漂移校正能力弱。
运行建图仿真环境
roslaunch wpr_simulation wpb_stage_robocup.launch
rostopic list #查看话题
rostopic echo /scan --noarr
rosrun rqt_tf_tree rqt_tf_tree
rosrun gmapping slam_gmapping
rosrun rviz rviz
rosrun wpr_simulation keyboard_vel_ctrl
24.使用Gmapping进行SLAM建图
cd catkin_ws/src/
catkin_create_pkg slam_pkg roscpp rospy std_msgs
mkdir launch
vim launch/gmapping.launch
<launch><!-- 载入 机器人 和 RoboCup@Home 的仿真场景 --><include file="$(find wpr_simulation)/launch/wpb_stage_robocup.launch"/><!-- Gmapping --><node pkg="gmapping" type="slam_gmapping" name="slam_gmapping"/><!-- Rviz --><arg name="rvizconfig" default="$(find wpr_simulation)/rviz/slam.rviz" /><node name="rviz" pkg="rviz" type="rviz" args="-d $(arg rvizconfig)" required="true" /><!-- 手柄控制 --><node respawn="true" pkg="joy" type="joy_node" name="joy_node" ><param name="dev" type="string" value="/dev/input/js0" /><param name="deadzone" value="0.12" /></node><param name="axis_linear" value="1" type="int"/><param name="axis_angular" value="0" type="int"/><param name="scale_linear" value="0.5" type="double"/><param name="scale_angular" value="1" type="double"/><node pkg="wpr_simulation" type="teleop_js_node" name="teleop_js_node"/></launch>