当你手头只有一个激光雷达,却想实现机器人自主建图时,这个场景就像只用一把尺子测量整个房间的尺寸。传统SLAM通常依赖编码器、IMU等多传感器融合,但在教育机器人、低成本AGV等场景中,我们常常遇到只有单线激光雷达的硬件配置。
我去年帮学生团队改造一台旧扫地机器人时就遇到过这种情况。原厂里程计已经损坏,但激光雷达完好,最终我们通过GMapping+laser_scan_matcher的方案,用不到200元的硬件成本实现了基础建图功能。这种方案最大的优势是硬件门槛极低——市面上常见的RPLIDAR A1或思岚A1M8等雷达,二手价格通常在300元以内。
不过要注意,纯激光方案有两个明显短板:一是运动方向上的位置估计容易漂移,就像蒙眼走路时会逐渐偏离直线;二是快速转向时容易丢失定位,建议控制机器人以低于0.3m/s的速度运动。实测发现,在10m×10m的室内环境中,建图误差可以控制在5%以内,足够应对大多数教学演示或简单巡检需求。
GMapping的核心是Rao-Blackwellized粒子滤波(RBPF),这种算法巧妙地将SLAM问题分解为定位和建图两个子任务。就像考古学家通过碎片复原文物,每个粒子都携带一张可能的地图假设,通过激光扫描数据不断修正这些假设。
具体实现时,算法会维护约30个粒子(可通过particles参数调整)。每个粒子包含:
当新一帧激光数据到达时,算法会执行三个关键步骤:
python复制# 简化的粒子更新伪代码
for particle in particles:
# 运动模型预测
particle.pose += motion_model(last_pose, current_pose)
# 计算扫描匹配得分
particle.weight = scan_match(particle.map, laser_scan)
# 地图更新
update_map(particle.map, laser_scan, particle.pose)
在gmapping.launch文件中,这几个参数对纯激光方案尤为关键:
xml复制<param name="maxUrange" value="8.0"/> <!-- 最大有效测距距离 -->
<param name="sigma" value="0.05"/> <!-- 高斯噪声参数 -->
<param name="kernelSize" value="1"/> <!-- 搜索窗口大小 -->
<param name="lstep" value="0.05"/> <!-- 平移优化步长 -->
<param name="astep" value="0.05"/> <!-- 旋转优化步长 -->
<param name="iterations" value="5"/> <!-- 优化迭代次数 -->
根据我的实测经验,在无里程计情况下建议:
lstep和astep减小到0.02-0.03,提高匹配精度iterations增加到10,但会提升CPU占用maxUrange设为雷达实际有效距离的80%(避免噪声干扰)laser_scan_matcher采用的PLICP(Point-to-Line ICP)算法,比传统ICP更适合结构化环境。它的核心思想是将激光点匹配到特征线上,就像用乐高凸点对准基板凹槽。
算法流程分为三个阶段:
cpp复制// 简化的PLICP核心计算
for (int iter = 0; iter < max_iterations; ++iter) {
// 建立点-线对应关系
findCorrespondences(current_scan, reference_scan);
// 计算变换矩阵
Eigen::Matrix3f H = computeHessian(correspondences);
Eigen::Vector3f dx = H.ldlt().solve(-computeGradient(correspondences));
// 应用位姿更新
current_pose = current_pose * transformFromDelta(dx);
}
原生的laser_scan_matcher在Noetic上编译可能会遇到这些问题:
bash复制sudo apt install libeigen3-dev
CMakeLists.txt,添加:cmake复制find_package(Eigen3 REQUIRED)
include_directories(${Eigen3_INCLUDE_DIRS})
我推荐使用改进版的ira_laser_tools,它对现代ROS支持更好:
bash复制git clone https://github.com/iralabdisco/ira_laser_tools.git
以思岚A1雷达为例,先确认设备权限:
bash复制ls -l /dev/ttyUSB* # 查看设备节点
sudo chmod 666 /dev/ttyUSB0 # 设置权限
启动雷达测试:
bash复制roslaunch rplidar_ros view_rplidar.launch
正常应该看到类似雷达的扇形扫描可视化。
创建gmapping_no_odom.launch文件:
xml复制<launch>
<!-- 雷达驱动 -->
<node pkg="rplidar_ros" type="rplidarNode" name="rplidar">
<param name="serial_port" value="/dev/ttyUSB0"/>
<param name="frame_id" value="laser"/>
</node>
<!-- laser_scan_matcher -->
<node pkg="laser_scan_matcher" type="laser_scan_matcher_node"
name="laser_scan_matcher" output="screen">
<param name="use_odom" value="false"/>
<param name="publy_pose" value="true"/>
<param name="base_frame" value="base_link"/>
<param name="max_iterations" value="10"/>
</node>
<!-- Gmapping -->
<node pkg="gmapping" type="slam_gmapping" name="slam_gmapping">
<param name="base_frame" value="base_link"/>
<param name="odom_frame" value="odom"/>
<remap from="scan" to="/scan"/>
</node>
</launch>
运动控制建议:
地图评估命令:
bash复制# 查看地图话题
rostopic echo /map
# 保存地图
rosrun map_server map_saver -f ~/mymap
xmax参数particles数量在树莓派4B上实测,这个方案的内存占用约500MB,CPU使用率在70%左右。如果资源紧张,可以尝试以下优化:
xml复制<node pkg="topic_tools" type="throttle" name="scan_throttle"
args="messages /scan 5.0 /scan_throttled"/>
bash复制git clone https://github.com/ethz-asl/libpointmatcher.git
xml复制<node pkg="ira_laser_tools" type="laserscan_multi_merger" name="laser_merge">
<param name="destination_frame" value="base_link"/>
<param name="scan_destination_topic" value="/merged_scan"/>
<param name="laserscan_topics" value="/front_scan /rear_scan"/>
</node>
这套方案虽然简单,但在2023年RoboMaster高校赛中,有队伍就用类似配置完成了室内对抗赛的自动导航。当硬件条件受限时,充分挖掘单一传感器的潜力,往往能收获意想不到的效果。