科学的分析和解决ROS运行中产生的undefined symbol类报错的方法

   在我们运行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_PATHROS_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


在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/588979.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在 Windows 中安装部署并启动连接 MongoDB 7.x(命令行方式启动、配置文件方式启动、将启动命令安装为系统服务实现开机自启)

MongoDB 的下载 下载地址:https://www.mongodb.com/try/download/community 这里需要对 MongoDB 的版本号说明一下: MongoDB 版本号的命名规则是 x.y.z,当其中的 y 是奇数时表示当前的版本为开发版,当其中的 y 是偶数时表示当前的…

函数参数缺省和内联函数【C++】

文章目录 函数参数缺省函数参数缺省的条件和要求 内联函数内联函数的工作原理内联函数的定义方法内联函数的要求解决方法:直接在.h中定义内联函数的函数体 内联函数再Debug模式下默认是不展开的 函数参数缺省 顾名思义:可以少传一个/多个参数给函数&…

科东软件参加广州机器人产业联盟举办先进工业母机专家研讨会

工业母机是“制造机器的机器”,具有基础性、通用性、战略性特征,包括了减材切削机床、等材成形装备、增材制造装备及其控制系统等,是衡量国家工业水平和竞争力的重要标志。广东省作为全球知名的制造业基地,非常重视高端装备领域工…

2024年第三期丨全国高校大数据与人工智能师资研修班邀请函

2024年第三期 杭州线下班 数据采集与机器学习实战(Python) 线上班 八大专题 大模型技术与应用实战 数据采集与处理实战(Python&八爪鱼) 大数据分析与机器学习实战(Python) 商务数据分析实战&…

AI数据质量监控:数据质量的关键KPI都有哪些?

AI所支持的程序或设备成功实施的关键取决于用于训练模型的数据。使用低质量训练数据会导致模型训练不良,从而可能需要额外时间和预算进行重新训练和测试。为防止这种情况,最佳方法是在模型训练过程中实施质量检查。需要注意的是,不同质量度量…

LVS几种模式介绍

备注:这篇真的是水文,不看也罢。 LVS,linux virtual server,可提供IP网络层的负载均衡。 其主要模式主要有以下几种: LVS-NAT 主要通过网络地址转换,修改目的IP实现。Network Address Translation LVS-…

JS封装网页进入/退出全屏功能,兼容各大主流浏览器

1、演示 2、封装进入全屏函数 mozRequestFullScreen:兼容Firefox webkitRequestFullscreen:兼容 Chrome、Safari、Opera msRequestFullscreen:兼容:IE/Edge const enter () > {const element document.documentElementif (el…

32-数据处理:如何高效处理应用程序产生的数据?

如何更好地进行异步数据处理。 一个大型应用为了后期的排障、运营等,会将一些请求数据保存在存储系统中 。例如:应用将请求日志保存到 Elasticsearch 中,方便排障;网关将 API 请求次数、请求消息体等数据保存在数据库中&#xff…

.net 6 swagger Failed to load API definition

为什么会出现这样的问题? 因为swagger用的是restful 规则同一个路径下面,只有一个Get、Post、Put 如果你控制器下面有多个HttpGet、HttpPost、HttpPut 请求就会报错 正确方式,在控制器上添加路由[Route("api/[controller]/[action]")] 或…

算法练习第四十二天|01背包问题、416. 分割等和子集

一些背包问题 01背包问题 题目描述 小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自…

蓝桥杯嵌入式学习笔记(9):RTC程序设计

目录 前言 1. RTC介绍 2. 使用CubeMx进行源工程配置 3. 代码编程 3.1 准备工作 3.2 进行bsp_rtc.h编写 3.3 进行bsp_rtc.c编写 3.4 main.c编写 3.4.1 头文件引用 3.4.2 变量声明 3.4.3 子函数声明 3.4.4 函数实现 3.4.5 main函数编写 4. 代码实验 5. 总结 前言 因本人备赛蓝…

如何使用 Python 本地客户端操作读写云服务器 Redis 缓存数据库详细教程(更新中)

Redis 基本概述 Redis(Remote Dictionary Server)是一个开源的使用 ANSI C 语言编写的、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。它通常被称为数据结构服务器,因为值(value…