在我们运行ROS开源功能包的时候,可能会遇到: undefined symbol类报错,如 slam_gmapping: undefined symbol: _ZN8GMapping 14 sampleGaussianEdm
一、报错原因分析
该问题一般是缺失或者没有找到该功能包所需的某个.so 动态链接库文件。首先,先排除一些比较简单且容易解决的原因,比如
1、删除该功能包对应到的编译文件,如build和devel文件夹中相关内容,然后重新编译,验证问题是否被解决
2、尝试检查是否存在版本不匹配问题,可以通过简单的更新功能包版本或者切换成匹配版本,来查看问题是否被解决
3、使用rosdep工具来检查是否缺失该功能包所需的依赖,若缺失,则安装后,验证问题是否被解决
4、检查功能包的CMakeLists.txt文件中设定的编译规则是否存在没有包含必要的库或者链接设置的问题,如果你是是下载的某些使用人数比较多的开源功能包,往往不会存在这个问题,可以跳过该步检查。
5、检查功能包所在的路径是否位于环境变量的路径中,若不在,则添加后,验证问题是否被解决。
如果,以上检查都没有问题,那么就需要进行科学的深入分析了,也就是本文的核心内容,如下所示:
1、使用c++filt 工具将报错信息中的符号化名称转换回我们可读的函数名或变量名
具体而言,在本文的例子中将报错信息undefined symbol: _ZN8GMapping14sampleGaussianEdm中的符号化名称,通过如下指令进行转换
c++filt _ZN8GMapping14sampleGaussianEdm
执行后,终端返回的信息如下
GMapping::sampleGaussian(double, unsigned long)
这表明运行报错的原因是系统在寻找 GMapping::sampleGaussian 函数的定义时,未能成功找到
【补充说明——c++filt 工具】:c++filt 是一个工具,用于解析 C++ 程序中的符号化(mangled)名称,将它们转换回人类可读的函数名或变量名。C++ 的符号化是一种机制,用于在编译时生成每个函数或变量的唯一名称,以此来支持诸如函数重载、命名空间等特性。对于给出的符号 _ZN8GMapping14sampleGaussianEdm,运行 c++filt 命令将解码这个符号化的名称,以便理解它代表的具体函数或变量。这在调试链接错误(如未定义符号错误)时特别有用,因为它可以帮助开发者确定缺失的确切函数或变量。
2、使用nm工具查找存在GMapping::sampleGaussian定义的动态库文件,来确定是否有正确的库文件包含了所示的函数定义
具体而言,先进入到报错的功能包所在工作空间存放.so动态链接文件的路径下,其一般在工作空间的/devel/lib文件夹下,如我的位于 /home/gly/catkin_ws/devel/lib/路径下,使用cd指令切换到该路径下
cd /home/gly/catkin_ws/devel/lib/
然后,在终端执行如下指令,来查找GMapping::sampleGaussian函数存在哪些动态链接库文件中,以及是否被定义
for so in *.so; doecho "Checking $so ..."nm -C $so | grep GMapping::sampleGaussian && echo "Found in $so"
done
执行后返回的结果如下:
虽然,返回了三个结果,但是其在 libgridfastslam.so 以及 libslam_gmapping_nodelet.so 文件中显示为未定义(“U”),在libutils.so文件中是有效定义的,这个libutils.so文件可能就是程序运行所需要的动态链接库文件。
3、借助GMapping::sampleGaussian所在cpp文件中其他函数,验证上一步找到的libutils.so文件是否就是导致程序运行报错的所需要的文件
通过vscode或者其他带有查找功能的文件管理软件,浏览该功能包,查找GMapping::sampleGaussian所在的cpp文件,本文中的例子在stat.cpp中,选择该文件中其他任意一个非内置函数,如本文的例子中选择的pf_ran_gaussian函数,如下图所示:
使用nm工具查找pf_ran_gaussian函数所在的动态库文件,即执行以下指令
for so in *.so; doecho "Checking $so ..."nm -C $so | grep GMapping::pf_ran_gaussian&& echo "Found in $so"
done
运行结果如下,可以发现其仅存在于libutils.so文件中,且被成功定义 (T)
非常好,现在基本上已经确定slam_gmapping: undefined symbol: _ZN8GMapping14sampleGaussianEdm 问题的原因出在libutils.so文件,现在该文件已经确定是存在,且其路径在ROS的环境变量中,那大概率就是文件冲突原因导致的了。
4、直接在整个计算机范围为搜索该文件libutils.so
使用以下指令查看链接库环境变量优先级
echo $LD_LIBRARY_PATH
结果如下所示:
可以发现确实是存在多个同名文件,/home/gly/motionplanning_ws中的内容是我近期新加入的,且其环境变量的优先级高于openslam_gmapping功能包所在的工作空间/home/gly/catkin_ws环境变量的优先级,这样就说的通,为啥gampping建图突然就不能用了,因为,我在一个具有更高优先级的工作空间环境变量中新加入的程序中也存在一个libutils.so文件,且与openslam_gmapping功能包所需的文件不是同一个文件,这样程序在运行时,优先搜索到了这个错误的同名链接文件,这个文件中当然没有所需的sampleGaussian等函数的相关内容。
二、解决方法
找到原因后,接下来的解决方法就很简单了,/home/gly/motionplanning_ws中的内容是我需要的,显然并不能采取删除来解决该问题,比较理想的方式是调整工作空间优先级,根据个人的情况,可以采用临时调整和长久调整两种模式
1、在调整之前还有两个非常重要的概念需要区分: ROS_PACKAGE_PATH 和LD_LIBRARY_PATH
LD_LIBRARY_PATH
和 ROS_PACKAGE_PATH
是两个不同的环境变量,它们在 UNIX 和 Linux 系统中用于不同的目的,尤其是在使用 ROS(Robot Operating System)时。
LD_LIBRARY_PATH
- 用途:
LD_LIBRARY_PATH
是一个环境变量,用于指定动态链接器搜索动态库(.so
文件)时的目录路径。当程序运行时,动态链接器会在这些路径中查找所需的动态库文件。 - 作用范围:它影响的是程序运行时的库加载行为,不仅限于ROS,适用于所有使用动态库的程序。
- 特点:修改
LD_LIBRARY_PATH
可以解决库版本冲突或确保程序使用特定版本的库,但是不当的使用可能会导致系统或其他程序的运行问题。
ROS_PACKAGE_PATH
- 用途:
ROS_PACKAGE_PATH
是一个特定于 ROS 的环境变量,用于指示 ROS 系统在哪些目录下查找 ROS 包。这个变量包含了一系列目录,ROS 工具(如roscd
,rospack
,rosrun
等)会在这些目录中搜索包。 - 作用范围:它仅影响 ROS 工具和运行时系统寻找和管理 ROS 包的行为,与动态链接库的加载无关。
- 特点:正确设置
ROS_PACKAGE_PATH
对于 ROS 节点和程序能够找到其依赖的包至关重要。它确保了 ROS 工具和运行时环境可以在你的工作空间中找到并正确使用 ROS 包。
总结
- LD_LIBRARY_PATH
关注的是程序运行时动态库的加载,它告诉动态链接器在哪些额外的目录中查找库文件。- ROS_PACKAGE_PATH
关注的是 ROS 环境中包的查找,它告诉 ROS 在哪些目录中查找 ROS 包。
两者都是环境变量,但服务于不同的目的,分别影响动态库的加载和ROS包的查找。在使用 ROS 进行开发时,正确配置这两个环境变量对于确保程序的正确编译和运行非常重要。
所以我们需要调节的事LD_LIBRARY_PATH,而不是ROS_PACKAGE_PATH!!!
2、方法一:临时调整
在你想要运行gmapping建图的终端执行以下指令(工作空间路径需要根据自己情况修改),来仅在该终端下将gmapping建图所在的工作空间
export LD_LIBRARY_PATH=/home/gly/catkin_ws/devel/lib:$LD_LIBRARY_PATH
这个命令将 /home/gly/catkin_ws/devel/lib 添加到 LD_LIBRARY_PATH 的开头,确保了动态链接器首先搜索这个目录。请注意,这种更改只对当前终端会话有效。关闭终端或开启新的会话都将丢失这个设置。
3、方法二:长久调整
如果你希望这个设置在所有会话中都有效,可以通过修改.bashrc文件中关于环境变量的配置,你可以选择手动打开该文件,在文件末尾加入如下语句,然后保存
export LD_LIBRARY_PATH=/home/gly/catkin_ws/devel/lib:$LD_LIBRARY_PATH
当然,你可以选择直接在终端执行如下语句,自动将其添加到.bashrc文件中
echo 'export LD_LIBRARY_PATH=/home/gly/catkin_ws/devel/lib:$LD_LIBRARY_PATH' >> ~/.bashrc
使用如下指令,分别打印出修改前后的动态链接库路径如下所示
然后,我成功运行了gmapping建图,如下所示,问题成功解决
4、除了上面介绍的删除冲突文件或者修改动态链接库优先级的方法外,其实也可能通过修改功能包的CMakeLists.txt文件中关于生成.so文件的命名相关的语句,然后重新编译,通过来改名来彻底解决同名冲突问题。
注:本文还存在另外一个记录了我探索和总结以上方法过程的版本,其更加详细,但概括总结性不如本文,其链接如下:
https://blog.csdn.net/qq_44339029/article/details/137290086