由于最近需要使用别人写好的库,所以特地来验证一下,在ros2中如何使用自定义的库。
1、文件结构
首先我有两个功能包、自定义消息
说明一下功能:vehicle功能包是每秒发布两个整数。
calculation功能包是订阅vehicle发布的话题,并计算两个整数的和。
那么好了,关于计算两个整数的和,我采用外部库来实现。那么首先你就得有库文件和头文件吧,有库你才可以调用。下面我来说一下我的库。关于生成动态库的步骤我就不说了。自行百度。
add.h 和 libaddlib.so是我所需要的,又因为,只有calculation功能包是需要调用这个库的,所以只关心calculation功能包就够了。
2、新建文件
在calculation功能包中新建,一个add.h文件,一个文件夹,下面包含libaddlib.so库文件。
下面是calculation功能包的结构:
另外一种表示方法更直观,可以看出calculation功能包中个文件的关系:
所以,只需要修改CMakeList.txt就可以了。
3、修改calculation功能包的CMakeList.txt(重点)
-
find_library
是 CMake 提供的命令,用于在系统上查找指定的库文件。ADD_LIB
是你将要在 CMake 中定义的一个变量,用于存储找到的库文件的路径。addlib
是你要查找的库文件的名称。PATHS ${CMAKE_CURRENT_SOURCE_DIR}/lib
指定了查找库文件的路径。${CMAKE_CURRENT_SOURCE_DIR}
是一个 CMake 变量,表示当前处理的 CMakeLists.txt 文件所在的目录的路径。这里指定在当前 CMake 文件所在目录下的lib
子目录中查找库文件。
总体而言,这行代码的目的是在 ${CMAKE_CURRENT_SOURCE_DIR}/lib
目录中查找名为 addlib
的库文件,并将找到的库文件的路径存储在 ADD_LIB
变量中。在后续的 CMake 文件中,你可以使用这个变量来确保链接你的可执行文件或库。
target_link_libraries
用于将目标(通常是一个可执行文件或库)与一个或多个库进行链接。
calculate_node
是你的可执行文件的名称或目标的名称。${ADD_LIB}
是你之前使用find_library
命令找到的库的路径,通过该变量告诉 CMake 链接器库的位置。addlib
是你要链接的库的名称。
这一行的目的是将可执行文件 calculate_node
与名为 addlib
的库进行链接。${ADD_LIB}
指定了库的路径,确保链接器能够找到它。这样,当你运行 calculate_node
时,系统将能够正确地定位并加载 addlib
库。
把CMakeList.txt修改成这样应该就可以了。
分享一下完整的calculation功能包中的cmakelist.txt
cmake_minimum_required(VERSION 3.5)
project(calculation)
# Default to C99
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 99)
endif()
# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(intadd_interfaces REQUIRED)
find_library(ADD_LIB addlib PATHS ${CMAKE_CURRENT_SOURCE_DIR}/lib)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# uncomment the line when a copyright and license is not present in all source files
#set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# uncomment the line when this package is not in a git repo
#set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
# 设置包含目录
include_directories(include)
add_executable(calculate_node src/calculate.cpp)
ament_target_dependencies(calculate_node
rclcpp
std_msgs
intadd_interfaces
)
target_link_libraries(calculate_node ${ADD_LIB} addlib)
install(TARGETS
calculate_node
DESTINATION lib/${PROJECT_NAME}
)
# 安装头文件
install(DIRECTORY include/ DESTINATION include/${PROJECT_NAME})
4、导入外部库的路径(非必须,报错的话看这步)
但是我在运行时又出现了问题,就是找不到这个库文件,所以要把库文件的路径添加到环境变量中去:
先看一下库文件的路径:pwd
在终端输入:
export LD_LIBRARY_PATH=/home/shnavi/ml/driverless/src/calculation/lib:$LD_LIBRARY_PATH
即:
5、效果如下
这样就可以了,试试效果:
启动calculation功能包来计算两数之和:
启动vehicle包,每隔一秒发送两个整数:
文中使用的话题是自定义消息:
6、头文件目录
这样就成功调用了自定义的库函数,下面分享一下头文件:
自定义库的头文件这样定义就可以了。
7、部分测试代码
秉持开源精神,所以把源文件分享一下,供大家测试:
calculate.cpp
#include "rclcpp/rclcpp.hpp"
#include "intadd_interfaces/msg/add.hpp"
#include "calculation/add.h" // 包含新创建的头文件
class Calculation : public rclcpp::Node
{
public:
Calculation(std::string name) : Node(name)
{
RCLCPP_INFO(this->get_logger(), "%s", name.c_str());
// 创建一个名称为"integer_subscriber"、队列大小为10的话题订阅者
subscriber_ = this->create_subscription<intadd_interfaces::msg::Add>(
"integer_publisher",
10,
[this](const intadd_interfaces::msg::Add::SharedPtr msg) {
// Callback function when receiving a message
int result = add_numbers(msg->first, msg->second); // Call the add_numbers function
RCLCPP_INFO(this->get_logger(), "Received: %d and %d, Sum: %d", msg->first, msg->second, result);
}
);
}
private:
rclcpp::Subscription<intadd_interfaces::msg::Add>::SharedPtr subscriber_;
};
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<Calculation>("vehicle");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
其中下面圈出来的就是库中的函数,直接调用就可以了。
vehicle.cpp
#include "rclcpp/rclcpp.hpp"
#include "intadd_interfaces/msg/add.hpp"
#include <random> // 包含随机数生成器
class VehicleNode:public rclcpp::Node
{
public:
//构造函数
VehicleNode(std::string name) : Node(name) //
{
//打印名字信息
RCLCPP_INFO(this->get_logger(),"%s",name.c_str());
// 创建一个名称为"integer_publisher"、队列大小为10的话题发布者
publisher_ = this->create_publisher<intadd_interfaces::msg::Add>("integer_publisher", 10);
// 使用定时器每秒发布两个随机整数
timer_ = this->create_wall_timer(
std::chrono::seconds(1),
[this]() {
auto message = intadd_interfaces::msg::Add();
// Generate and publish two random integers
message.first = generateRandomInteger();
message.second = generateRandomInteger();
RCLCPP_INFO(this->get_logger(), "Publishing random integers: %d and %d", message.first, message.second);
publisher_->publish(message);
}
);
}
private:
//声明一个发布者
rclcpp::Publisher<intadd_interfaces::msg::Add>::SharedPtr publisher_;
//声明一个定时器
rclcpp::TimerBase::SharedPtr timer_;
// 生成随机整数的辅助函数
int generateRandomInteger()
{
// 使用C++11的随机数引擎和分布
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> dist(1, 100); // 生成1到100之间的整数
return dist(gen);
}
};
int main(int argc,char** argv)
{
rclcpp::init(argc,argv);
auto node = std::make_shared<VehicleNode>("vehicle");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}