上一篇我已经把速腾Helios-16P的ROS驱动搞定,点云能够稳定地在RVIZ里显示出来。本来以为接下来就是装个包、跑个launch的事,结果真正上手编译Lego-loam时,才发现坑比想象中多。群里不少朋友也是卡在这一步:源码拉下来了,编译报错,或者压根不知道怎么把这个老项目接到Helios-16P上;还有人图省事,直接用CodeBlocks打开源码点“编译运行”,结果自然是各种“无法编译运行”。这篇就把Lego-loam的编译与运行完整过一遍,包括环境版本怎么选、依赖怎么装、CGI坑怎么避、Helios-16P的传感器参数怎么改,以及RVIZ里出现常见异常时怎么排查。适合正在用速腾16线雷达、想跑通LeGO-LOAM或者类似LOAM系算法的朋友。
1. 编译前的环境准备:版本、依赖与雷达数据源
1.1 系统版本和ROS发行版怎么选
Lego-loam是2016-2018年前后比较活跃的项目,官方README里写的还是Ubuntu 16.04 + ROS Kinetic。但实际使用中,我建议优先选择Ubuntu 18.04 + ROS Melodic。这不是怀旧,而是因为整个Lego-loam的依赖链和ROS接口都是那个时代的东西,在Melodic上编译最省心。
如果有朋友非要用Ubuntu 20.04 + ROS Noetic,也不是完全不行,但你要有心理准备:Noetic默认的OpenCV是4.2,而很多Lego-loam分支里硬编码了OpenCV 3的接口;同时Noetic的cv_bridge也是基于OpenCV4编译的,一旦代码里混用了OpenCV3和OpenCV4的头文件,轻则警告,重则链接阶段直接崩。我自己实测在Noetic上要额外改不少地方,对于想快速建图的人来说不值得。
另外还要确认gcc版本。Melodic对应的gcc 7,可以顺利支持Lego-loam默认的C++11/14编译参数。如果你用Noetic的gcc 9,编译老代码时偶尔会遇到一些STL接口差异,需要额外加-D_GLIBCXX_USE_CXX11_ABI=0之类的参数,很烦。
我目前长期在用的验证环境如下:
| 组件 | 版本 |
|---|---|
| 操作系统 | Ubuntu 18.04.5 LTS 64位 |
| ROS发行版 | Melodic Morenia |
| 雷达驱动 | rslidar_sdk(Ubuntu 18.04适配版) |
| 雷达型号 | 速腾Robosense Helios-16P |
| 编译工具 | catkin_make(不要用catkin build) |
| 视觉/点云库 | PCL 1.8.1、OpenCV 3.2.0(Melodic自带) |
| GTSAM | 4.0.0-alpha2(源码编译) |
不建议在Windows或Mac上尝试,Lego-loam的依赖基本都是Linux生态下的,非Linux环境编译成本极高。
1.2 依赖安装清单:不要一股脑全装
安装依赖时,最常见的错误是看到网上教程就让apt install一堆东西,结果装了个OpenCV 4或者Eigen 3.4,反而和代码不兼容。我习惯从项目实际需求反推,Lego-loam编译依赖的核心库只有这几个:
- PCL(点云库),提供PointCloud类型、体素滤波、平面分割等核心功能
- Eigen,矩阵运算
- OpenCV,用于特征/图像相关处理部分
- GTSAM,用于后端图优化
- rosdep自动解析出的ROS包依赖,比如roscpp、tf、cv_bridge、pcl_ros等
在Melodic上,可以用一行命令把大部分依赖解决:sudo apt install ros-melodic-pcl-ros ros-melodic-cv-bridge ros-melodic-tf2-eigen libopencv-dev libeigen3-dev libpcl-dev。
GTSAM建议单独说。虽然Ubuntu软件源里有libgtsam-dev,但版本往往比较新,Lego-loam调用的部分接口和老版本行为不同,编译期就报错。稳妥做法是下载GTSAM 4.0.0-alpha2源码,按官方README编译安装。这个包编译时间比较长,建议后面编译章节单独处理。如果已经有系统GTSAM并且能过编译,也可以先用着,但出问题时优先卸载重装alpha2。
sudo apt update sudo apt install ros-melodic-pcl-ros ros-melodic-cv-bridge ros-melodic-tf2-eigen \ libopencv-dev libeigen3-dev libpcl-dev1.3 先把Helios-16P的数据送进ROS再谈建图
很多朋友编译RVIZ半天,最后发现雷达驱动压根没起来,话题里没有点云。LeGO-LOAM做的只是订阅点云话题和IMU话题,它自己不会去读雷达硬件,所以编译前最好先把雷达驱动跑通。
速腾Helios-16P的驱动通常叫rslidar_sdk。编译方式跟着官方README一步步走即可,核心就是把点云话题发布出来。不同版本的驱动发布的话题名不一定相同,有的默认是/rslidar_points,有的是/points_raw。启动后先确认一下:
source devel/setup.bash roslaunch rslidar_sdk start.launch # 另开终端 rostopic list | grep rslidar rostopic hz /rslidar_points正常情况下rostopic hz会稳定输出10Hz或20Hz左右的频率,说明数据流通正常。如果这里频率为0,不用继续往下折腾LeGO-LOAM,先解决驱动问题。
另外,LeGO-LOAM的建图效果对IMU十分依赖。Helios-16P是否带内置IMU取决于具体硬件配置。如果驱动没有发布/imu/data或者类似话题,地图大概率会出现严重漂移。这一点我在后面“参数适配”章节会详细讲,但你要先确认手头设备有没有IMU话题。
2. 源码下载与catkin_make编译的完整流程
2.1 创建工作空间并拉取代码
Lego-loam是一个标准的ROS功能包,源码要放在catkin工作空间的src目录下。这里建议新建一个干净工作空间,不要和雷达驱动混在同一个工作空间里编,否则依赖冲突很难查。
mkdir -p ~/lego_ws/src cd ~/lego_ws/src git clone https://github.com/RoboticsBCN/lego-loam.git如果git clone速度很慢或者失败,可以直接从GitHub页面下载zip包,解压到~/lego_ws/src下,并把目录名改成lego-loam。注意检查目录里面是否有package.xml和CMakeLists.txt,如果解压多了一层文件夹,要手动把内层文件挪出来。
2.2 编译时为什么卡在GTSAM
网上很多教程让直接catkin_make,如果你是第一次编译,大概率会卡在这行:
-- Found GTSAM: /usr/local/lib/libgtsam.so如果你的系统里没装GTSAM,cmake会直接报找不到。装了系统GTSAM(比如1.7或4.2版本)时,编译可能通过,但运行时图优化会出现莫名崩溃,或者地图不更新。所以我建议先手动编译GTSAM 4.0.0-alpha2。
wget https://github.com/borglab/gtsam/archive/4.0.0-alpha2.zip unzip gtsam-4.0.0-alpha2.zip cd gtsam-4.0.0-alpha2 mkdir build && cd build cmake .. make -j2 sudo make install编译GTSAM时不要开-j8,这玩意的内存消耗不小,笔记本8G内存开多线程很容易编译到一半进程被杀。用-j2慢一点,但稳。装完后可以用ldconfig -p | grep gtsam确认so库已经加载。
2.3 编译Lego-loam的正确姿态:不要全核硬刚
GTSAM装好之后,回到工作空间编译:
cd ~/lego_ws catkin_make -j1先解释为什么我用-j1而不是catkin_make -j4。LeGO-LOAM里有很多模板头文件,尤其是GTSAM相关的优化部分,编译时内存峰值能到7GB以上。如果机器只有8GB物理内存,开-j4会直接把内存吃满,然后OOM killer把编译进程杀掉,屏幕上一堆internal compiler error: Killed。
如果你是16G以上内存机器,可以开-j2或-j4,但第一次建议还是保守一点。还有一种常见做法是增加swap文件,我在这篇里直接给出可复制的命令:
sudo fallocate -l 8G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile加上swap之后,即使内存只有8G,也基本能扛住一次完整编译。但要注意,swap只是兜底,长期依赖swap会导致编译速度明显下降。
2.4 编译成功后的验证
当终端出现类似Summary: 1 package finished的提示,说明编译完成。接下来要确认环境变量已经指向当前工作空间:
source ~/lego_ws/devel/setup.bash rospack find lego_loamrospack find lego_loam如果能打印出包路径,说明ROS已经认识这个功能包了。这时候不要急着跑,先看一眼生成的可执行文件:
ls ~/lego_ws/devel/lib/lego_loam/正常情况下能看到lego_loam这个可执行文件,这是后续launch真正调起的建图主程序。
3. 别用CodeBlocks打开ROS工程硬编译:构建入口要搞对
3.1 “CodeBlocks无法编译运行”的根本原因
Lego-loam编译相关的问题里,有相当一部分人不是卡在源码,而是卡在“用什么工具编译”。最近经常看到有人问:用CodeBlocks打开LeGO-LOAM目录下的CMakeLists.txt,点“编译运行”会不会报错,或者直接显示无法编译运行。
先说结论:用CodeBlocks直接打开ROS功能包并编译,大概率失败。这不是CodeBlocks本身不行,而是ROS功能包的构建不是普通CMake工程。ROS依赖ROS_PACKAGE_PATH、CMAKE_PREFIX_PATH这些环境变量去定位roscpp、cv_bridge、pcl_ros等依赖包。你在终端里先source /opt/ros/melodic/setup.bash,再source ~/lego_ws/devel/setup.bash,这些环境变量才会注入到当前会话。而CodeBlocks从图形界面启动时,通常不会继承你在终端里手动设置的环境变量,所以它打开CMakeLists.txt后,find_package(roscpp)这步就找不到库,后续编译必然失败。
另外,ROS功能包还有catkin_package()宏、package.xml里的依赖声明,这些都需要catkin工具在构建前解析并生成环境钩子。CodeBlocks并不认识catkin的这套逻辑,它只会把CMakeLists.txt当成一个独立CMake工程处理,自然无法正确完成编译运行。
3.2 在IDE里正确使用Lego-loam源码的方式
我平时也会用IDE看代码,但只是把它当代码浏览器,不是构建工具。正确做法是:
- 用VS Code或Qt Creator打开
~/lego_ws目录用来阅读和编辑源码; - 所有编译行为都在终端里执行
catkin_make; - 如果需要调试,就在终端里先source环境,再用gdb或者VS Code的Remote调试功能附加到LeGO-LOAM进程。
如果你实在喜欢IDE的“一键运行”,可以用VS Code配置task调用catkin_make,但不能直接点CodeBlocks的“运行”按钮。还有一个方向是Qt Creator,它支持加载CMake工程,并且可以在界面里设置CMake参数,比如-DCMAKE_BUILD_TYPE=Release。但前提依然是你先通过某种方式给Qt Creator注入ROS环境变量,比如从已经source过的终端里启动Qt Creator。
source /opt/ros/melodic/setup.bash source ~/lego_ws/devel/setup.bash qtcreator即便如此,LeGO-LOAM这种老项目在IDE里也能出现各种重定向问题,建议新手不要折腾。
3.3 判断ROS环境有没有生效的快速方法
很多时候报错信息说找不到ros/ros.h或者找不到lego_loam,不是代码问题,而是环境变量没生效。在编译前可以快速验证:
echo $ROS_PACKAGE_PATH echo $CMAKE_PREFIX_PATH如果是空或者没有出现/opt/ros/melodic,说明当前终端没source过ROS。这时候即使你在CodeBlocks里手动配置CMake变量,也很容易漏掉某个路径。反而在终端里老老实实执行source再编译,是最可控的。
4. 针对速腾Helios-16P的传感器与话题参数适配
4.1 理解N_SCAN、Horizon_SCAN与角分辨率的关系
LeGO-LOAM启动时有两组最关键参数直接决定建图是否正常,这就是N_SCAN和Horizon_SCAN。字面上看,N_SCAN是雷达线束数,Horizon_SCAN是水平一圈扫描点数。
Helios-16P是16线雷达,所以N_SCAN=16写死没问题。Horizon_SCAN则需要根据水平角分辨率算:一圈完整360°,若分辨率是0.2°,就是360 / 0.2 = 1800;若是0.1°,就是360 / 0.1 = 3600。速腾Helios-16P常见的配置是0.2°水平分辨率,但有些场景你可能在驱动里改了扫描配置,或者雷达固件本身就是0.1°模式。如果Horizon_SCAN设置和实际点云不一致,LeGO-LOAM会扫描异常,建出的地图一片破碎。
确定雷达实际分辨率的办法,可以从驱动配置里读,也可以录一帧点云,数一个回环里水平角增量。我更推荐前者:直接看rslidar_sdk配置文件中的angle相关参数,一般默认值就是当前固件分辨率。
4.2 修改launch文件还是utility.h
LeGO-LOAM的参数有两种修改位置。一种是源码里的include/utility.h,里面有大量全局变量定义;另一种是launch/run.launch,通过rosparam方式传参。实测下来,launch文件中的参数优先级更高,会覆盖utility.h里的默认值,所以优先改launch。
我改后的核心参数如下,路径在lego_loam/launch/run.launch:
<param name="/use_sim_time" value="false"/> <param name="sensor" value="1" type="int"/> <param name="N_SCAN" value="16" type="int"/> <param name="Horizon_SCAN" value="1800" type="int"/> <param name="groundRemoval" value="true" type="bool"/> <param name="loopClosureEnableFlag" value="true" type="bool"/>其中sensor=1对应Velodyne VLP-16风格的数据组织方式。Helios-16P虽然是速腾品牌,但点云组织结构同样是16线、单回波、水平旋转扫描,数据模型和VLP-16类似,所以sensor保持1即可。如果你在驱动里配置了多回波或自定义线序,那可能需要看看具体点云解析后再调,但默认情况下不需要改。
4.3 修改点云话题和IMU话题
LeGO-LOAM默认订阅的话题是/velodyne_points和/imu/data,而rslidar_sdk发布的话题一般是/rslidar_points或/points_raw。不改话题名的话,启动后LeGO-LOAM会一直等数据,RVIZ里什么都没有。
打开include/utility.h,找到这两行并改成对应话题:
extern const string pointCloudTopic = "/rslidar_points"; extern const string imuTopic = "/imu/data";如果驱动发布的IMU话题是/rslidar_imu,就写成/rslidar_imu。注意IMU话题名称不要拼错,LeGO-LOAM内部用MessageFilter同步点云和IMU,一旦话题名不对,会一直打印等待IMU数据,建图进程不会前进。
没有IMU数据时怎么办?这个必须说得直接一点:LeGO-LOAM的漂移补偿和位姿优化都依赖IMU观测来做帧间约束,如果完全没有IMU,地图会在遇到小坡度或长时间直线运动时迅速发散。带IMU的Helios-16P可以直接用;如果设备没有IMU模块,建议先做一个简易IMU模拟节点,用点云配准结果推算角速度,但这属于进阶玩法,编译运行阶段不展开。
4.4 TF坐标系的坑:先解决base_link到camera_init的框架
LeGO-LOAM启动时会发布一系列TF变换,但雷达驱动通常只发布点云话题,不会主动提供base_link这个坐标系。RVIZ中要把固定坐标系设为camera_init,同时在系统里需要存在base_link -> laser之类的静态TF。最省事的做法是在launch里加一个静态变换,比如:
<node pkg="tf" type="static_transform_publisher" name="base_to_laser" args="0 0 0 0 0 0 base_link laser 100" />如果你的雷达安装位置相对于车体有偏移,就把前三个数改成实际外参。没有这个TF,LeGO-LOAM的TF树不完整,点云显示时经常整个地图乱飞。
5. 实机运行流程与RVIZ建图验证
5.1 完整启动过程分几步
设备上电、IP配置正常之后,按这个顺序启动:
# 终端1:启动雷达驱动 source ~/rslidar_ws/devel/setup.bash roslaunch rslidar_sdk start.launch # 终端2:启动LeGO-LOAM source ~/lego_ws/devel/setup.bash roslaunch lego_loam run.launch # 终端3:启动RVIZ rviz如果你希望RVIZ里直接看到LeGO-LOAM提供的地图话题,不需要手动加载机器人模型。在RVIZ左侧Display面板,把Fixed Frame设置为camera_init,然后点击Add -> By topic,添加/laser_cloud_map和/laser_cloud_surround两个PointCloud2话题,Size调成0.05左右,颜色选一个顺眼的。
5.2 移动雷达时要慢,它不是实时SLAM的万金油
LeGO-LOAM毕竟是基于帧间匹配的建图算法,雷达移动速度过快、旋转过猛的时候,帧间匹配很容易崩。我第一次实机测试时,拿着雷达在室内快速转圈,地图直接扭成麻花。后来改为沿走廊缓慢推进,转弯时先把角速度降下来,地图质量明显改善。
手持设备时还要注意一个细节:LeGO-LOAM对初始姿态比较敏感,启动时雷达尽量保持水平稳定几秒钟,让它把第一帧作为基准。如果启动瞬间雷达头是歪的,后面地图很可能整体倾斜。可以先静止几秒再开始移动,实测下来比起来就跑稳很多。
5.3 地图输出在哪里看与保存
运行过程中,/laser_cloud_map就是累计地图点云,/laser_cloud_surround是当前帧附近的地图,适合实时观察。如果你想保存现场数据用于离线分析,建议用rosbag把地图和原始点云一起录制:
rosbag record /rslidar_points /imu/data /laser_cloud_map /laser_cloud_surround录完之后可以用pcl_ros的bag_to_pcd工具把/laser_cloud_map导出成PCD文件,或者直接在后续离线处理里回放bag。不要迷信LeGO-LOAM自带的保存地图方式,很多修改版本里这个功能要么没开放要么路径容易写乱,用rosbag导PCD是最可靠可控的路径。
6. 编译和运行阶段的高频异常与排查记录
6.1 RVIZ里始终没有任何点云
首先确认LeGO-LOAM是否真的接收到了点云。终端里看有没有类似Registered points的打印信息,如果没有,说明订阅的话题名不对。用rostopic echo /laser_cloud_map -n1去验证地图话题是否有输出。如果是空的,返回去检查utility.h和run.launch中的pointCloudTopic。
还有一种情况,LeGO-LOAM内部时间同步不通过。雷达点云时间戳是雷达驱动给的,如果驱动里启用了sim time或者时间戳跳变,MessageFilter会一直等待,界面卡在初始化。用rostopic hz /rslidar_points看频率是不是稳定,再看rostopic echo /rslidar_points/header里的时间戳是否单调递增。
6.2 地图重影、拖影、点云错位
这通常是IMU话题没有正确同步。常见表现是后半段地图出现重影,或者墙面变成两三层错开的片。排查方向不是急着调算法参数,而是先看IMU数据的频率和坐标系。LeGO-LOAM默认假设IMU的Z轴与重力方向对齐,如果设备安装角度不对,重力分量会投影到X/Y上,导致姿态估计错误。
在不装配IMU的情况下,地图拖影几乎是必然的。我曾经试过关掉IMU同步,纯靠点云配准,短时间还不错,但走动超过几十米后漂移便无法控制。所以如果条件允许,一定要把IMU接上,而不是省掉。
6.3 编译时虽然通过了,但运行时一直提示找不到.so
这属于典型的动态库路径问题。LeGO-LOAM链接了自定义安装的GTSAM库,如果你把GTSAM装到了/usr/local/lib,但它不在系统库搜索路径里,运行时就会报类似error while loading shared libraries: libgtsam.so.4。
解决办法是更新动态库缓存:
sudo ldconfig如果还不行,就用export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH,再重新启动launch。
6.4 运行一段时间后LeGO-LOAM节点自动崩溃
我遇到过多次,主要原因是内存持续增长,最终被系统杀掉。LeGO-LOAM的全局地图会持续累积点云,如果不做降采样或限制地图范围,长时间运行后内存会越来越大。在utility.h里可以调节降采样相关参数,也可以定期清空地图或者重启节点。
还有一个容易被忽略的原因:点云话题里偶发NaN点。速腾驱动在极端环境下可能输出无效点,LeGO-LOAM遇到NaN点后优化模块会计算出非有限数值,直接触发段错误。解决办法是在驱动侧或话题上游过滤掉NaN点,用pcl_ros的PassThrough或自定义filter节点都可以。
6.5 switch和swap的旧事重申
这部分原本放在编译章节,但运行阶段也可能遇到。长时间跑图时用free -h监控内存,如果swap使用率一直很高,说明机器物理内存吃紧。建议笔记本用户外接电源,并把其他大内存程序全关掉。LeGO-LOAM不是那种“内存占用恒定”的程序,地图规模越大,内存越高,这一点要有预期。
# 监控内存和CPU htop # 监控话题速率 rostopic hz /laser_cloud_map7. 编译运行之后还能怎么折腾
Lego-loam跑通只是第一步。接下来很多人会问:地图出来之后,能不能直接用于导航?答案是不能直接当成二维栅格地图用,LeGO-LOAM生成的是三维点云地图,导航用的costmap需要先做地面分割和投影,或者改用Lego-loam输出的二维可通行区域版本,那又是另一个话题。
另外一个方向是把输出的点云地图做后端优化,比如加入回环检测,或者把建图结果保存成PCD后,再用其他全局优化工具做一次细化。我在实际项目里,通常会把LeGO-LOAM当实时前端,把地图记录下来,离线用ICP或NDT再配一次,得到的地图干净很多,尤其是室内走廊这种几何特征单调的环境。
最后分享一个我在编译过程中记住的教训:不要让工具链绑架你。老项目依赖麻烦,就固定在一个稳定环境里吃熟,不要为了追新系统而把时间耗在改源码兼容性上。Ubuntu 18.04 + Melodic + GTSAM 4.0.0-alpha2这套组合,我到现在还在用,不是因为旧,而是因为稳定可复现。