BehaviorTree.CPP v3从动态库加载叶节点

BehaviorTree.CPP v3从动态库加载叶节点

问题汇总

找不到生成的包含叶节点的动态库

terminate called after throwing an instance of 'BT::RuntimeError'
  what(): could not load library: libWaitActionBtNode.so: cannot open shared object file: No such file or directory

问题原因:
传递到BT::BehaviorTreeFactory::registerFromPlugin()函数中的路径并不是绝对路径。
解决办法:

  1. 传递动态库的绝对路径(推荐)
  2. 或使用相对于执行位置的相对路径。

无法定位其他第三方依赖库的符号

terminate called after throwing an instance of 'BT::RuntimeError'
  what(): could not load library: libWaitActionBtNode.so: undefined symbol: _ZN13rclcpp_action10ClientBase33get_number_of_ready_subscriptionsEv

问题原因:
在我的工程中,实际依赖了ROS2包rclcpp_action,但却没有在CMakeLists.txt中标明,这虽然没有在生成libWaitActionBtNode.so的时候报错,但是却在动态加载时出错。
解决办法:
CMakeLists.txt增加以下内容

find_package(rclcpp REQUIRED)
find_package(rclcpp_action REQUIRED)

让cmake查找依赖包,然后在add_library(WaitActionBtNode Shared src/action/WaitAction.cpp)后添加

ament_target_dependencies(WaitActionBtNode
    rclcpp
    rclcpp_action
)

找不到叶节点导出符号BT_RegisterNodesFromPlugin

ERROR loading library: can't find symbol [BT_RegisterNodesFromPlugin]

问题原因
BehaviorTree规定,定义叶节点so库,需要调用一个宏BT_REGISTER_NODES,原型如下

#define BT_REGISTER_NODES(factory)\
        static void BT_RegisterNodesFromPlugin(BT::BehaviorTreeFactory& factory)

在自定义的叶节点WaitAction.cpp文件中

BT_REGISTER_NODES(factory)
{
    BT::NodeBuilder builder = [](const std::string & strName, const BT::NodeConfiguration & config)
    {
    	return std::make_unique<WaitAction>(strName, config);
    };
    factory.registerBuilder<WaitAction>("wait", builder);
}

结果cmake构建完成之后竟然没有任何BT_RegisterNodesFromPlugin符号

$ objdump -T | grep BT_RegisterNodesFromPlugin
nothing...
$

观察BT_REGISTER_NODES的原型定义,发现,定义的BT_RegisterNodesFromPlugin竟然是一个static的函数,而且没有人调用它,编译器自动将其识别为无用的代码从而进行优化,所以生成.so时没有任何BT_RegisterNodesFromPlugin
查看libWaitActionBtNode.so生成的符号,没有发现任何BT_相关的导出符号

objdump -T libWaitActionBtNode.so | grep BT_
0000000000000000 g    DF .text 0000000000000000  Base         BT_RegisterNodesFromPlugin

去掉static关键字
不直接使用BT_REGISTER_NODES宏,而直接定义BT_RegisterNodesFromPlugin函数,去掉static关键字。

BT_RegisterNodesFromPlugin(BT::BehaviorTreeFactory& factory)
{
    BT::NodeBuilder builder = [](const std::string & strName, const BT::NodeConfiguration & config)
    {
    	return std::make_unique<WaitAction>(strName, config);
    };
    factory.registerBuilder<WaitAction>("wait", builder);
}

查看编译生成的动态库libWaitActionBtNode.so符号,发现了BT_相关的导出符号,但多了一些前缀和后缀

objdump -T libWaitActionBtNode.so | grep BT_
0000000000000000 g    DF .text 0000000000000000  Base         _Z26BT_RegisterNodesFromPluginRN2BT19BehaviorTreeFactoryE

增加extern "C"前置声明

extern "C" BT_RegisterNodesFromPlugin(BT::BehaviorTreeFactory& factory);
BT_RegisterNodesFromPlugin(BT::BehaviorTreeFactory& factory)
{
    BT::NodeBuilder builder = [](const std::string & strName, const BT::NodeConfiguration & config)
    {
    	return std::make_unique<WaitAction>(strName, config);
    };
    factory.registerBuilder<WaitAction>("wait", builder);
}

查看编译生成的动态库libWaitActionBtNode.so符号,发现了BT_相关的导出符号

objdump -T libWaitActionBtNode.so | grep BT_
0000000000000000 g    DF .text 0000000000000000  Base         BT_RegisterNodesFromPlugin

此时生成的动态库libWaitActionBtNode.so已经可以正常加载了。

正常解决办法
在bt_factory.h中有一个宏选项,会切换BT_REGISTER_NODES的定义

#if defined(__linux__) || defined __APPLE__
#define BT_REGISTER_NODES(factory)\
        extern "C" void  __attribute__((visibility("default")))BT_RegisterNodesFromPlugin(BT::BehaviorTreeFactory& factory)

所以在WaitAction.cpp源文件包含头文件“bt_factory.hpp”之前定义BT_PLUGIN_EXPORT宏可以解决该问题。

#define BT_PLUGIN_EXPORT
#include <bt_factory.hpp>

g++/gcc编译器已经预定了__linux__宏

$ aarch64-linux-gnu-g++ -E -DM -</dev/null | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1
#define linux 1
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值