在使用ros2开发的时候难免会需要引入第三方的厂家提供的.so文件库。注意这里说的并不是自己写的cpp文件转成.so然后再自己的工程项目中使用。例如经常使用的ICANCmd.h头文件和libCanCmd.so库还有onnx推理的时候onnxruntime_c_api.h头文件和libonnxruntime.so库文件。ros1中使用如下代码添加:
target_link_libraries(${PROJECT_NAME}_node
${CMAKE_CURRENT_SOURCE_DIR}/lib/libonnxruntime.so # 微软公司的runntime库
)
但在ROS2中直接使用这种方式添加的话会报找不到 xx.so文件库。这是因为ROS2的.so需要将库引入到指定的文件路径下才能使用。
下面以电机CAN包为例一步步实现电机的.so库调用
mkdir can_test_ws # 创建can_test_ws工作空间
cd can_test_ws # 进入到can_test_ws
mkdir src # 创建src文件
cd src
创建一个test_can包依赖rclcpp 节点名称为can_node
ros2 pkg create test_can --build-type ament_cmake --dependencies rclcpp --node-name can_node
使用tree查看下文件结构
└── src
└── test_can
├── CMakeLists.txt
├── include
│ └── test_can
├── package.xml
└── src
└── can_node.cpp5 directories, 3 files
添加一个lib文件 拷贝libCanCmd.so文件并且在include/test_can文件中添加一个ICANCmd.h头文件(和libCanCmd.so配套由厂家提供)和一个motorControl.h使用文件和一个motorControl.cpp实现,添加完以后如下: (这里并没有按照hpp头文件cpp源文件的C++创建方式,而是沿用了ICANCmd.h的C语言写法。)
└── src
└── test_can
├── CMakeLists.txt
├── include
│ └── test_can
│ ├── ICANCmd.h
│ └── motorControl.h
├── lib
│ └── libCanCmd.so
├── package.xml
└── src
├── can_node.cpp
└── motorControl.cpp6 directories, 7 files
编译
colcon build
source install/setup.bash
ros2 run test_can can_node
输出 hello world test_can package
到这里为止都并没有使用.so文件
我们打开can_node.cpp,添加自己写的motorControl.h文件
#include
int main(int argc, char ** argv)
{
(void) argc;
(void) argv;
printf("hello world test_can package\n");
return 0;
}
motorControl.h文件文件中使用了ICANCmd.h文件
#ifndef __MOTER_CONTROL_H__
#define __MOTER_CONTROL_H__
#include "ICANCmd.h" // CAN通信头文件
#include
#include // 主要使用exit(0) 退出程序
#include // uninx系统主要用usleep功能
#include // 智能指针
#define __countof(a) (sizeof(a)/sizeof(a[0])) // 定义宏
#define D_PI 2 * acos(0.0) // math库中计算弧度的反余弦
// 接受信号结构体
typedef struct {
bool Run;
DWORD ch;
}rcv_thread_arg_t;
int Init_CAN(); // 初始化CAN 经过 CAN_DeviceOpen CAN_ChannelStart CAN_GetDeviceInfo 后成功返回0
void PDO_map(); // PDO映射通过PDO传输数据
void *send_func(DWORD id, char nDataLen, unsigned int *arryData); // 发送功能
void disableMotor(); // 电机失能
void enableMotor(); // 电机使能
void CloseCAN(); // 关闭通道后在关闭CAN 使用了 CAN_ChannelStop CAN_DeviceClose
void fastStopMotor(); // 电机快速停止
void motorAtVelMode(); // 速度指令
void ReadLoadRate(); // 读取电机的负债率
void SigintHandler(int sig);
void Set_DownCurrent(); // 设定最小电流
void Set_UpCurrent(); // 设置最大电流
void MotorFaultClear(); // 驱动器故障清除
void StartNodeCommunication(); // 开始连接
void StopNodeCommunication(); // 停止连接
void SendSync(); // 发送80帧同步
void Motor_modelSet(); // 发送伺服电机模式
void createPthread(); // 创建线程
void Stop_Rec_pthread(); // 关闭线程
#endif // end __MOTER_CONTROL_H__
can_node.cpp中添加#include "test_can/motorControl.h" ,由于motorControl.h中的使用到了ICANCmd.h,这个头文件需要调用一个闭源的libCanCmd.so,直接编译会报错未定义:
/usr/bin/ld: CMakeFiles/can_node.dir/src/can_node.cpp.o: in function `main':
can_node.cpp:(.text+0x14): undefined reference to `Init_CAN()'
#include
#include "test_can/motorControl.h" // 添加自定义电机头文件
int main(int argc, char ** argv)
{
(void) argc;
(void) argv;
Init_CAN(); // CAN 通信初始化
printf("hello world test_can package\n");
return 0;
}
出现这种情况是我们头文件motorControl.h中申明的Init_CAN()虽然在motorControl.cpp文件中实现了,但是ROS2并没有办法直接找到这个实现,因此需要配置下CMakeLists.txt,修改的地方已经给出。
cmake_minimum_required(VERSION 3.5)
project(test_can)
# 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()
# ************* 以下内容为手动添加部分 *****************
include_directories(include) # 添加项目中的include文件夹路径
add_executable(can_node
src/can_node.cpp
src/motorControl.cpp
)
# ************* 以下内容为手动添加结束 *****************
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
# add_executable(can_node src/can_node.cpp) # 手动注释
target_include_directories(can_node PUBLIC
$
$)
ament_target_dependencies(
can_node
"rclcpp"
)
install(TARGETS can_node
DESTINATION lib/${PROJECT_NAME})
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()
编译后不出意外还是会报错:
Starting >>> test_can
--- stderr: test_can
/usr/bin/ld: CMakeFiles/can_node.dir/src/motorControl.cpp.o: in function `Init_CAN()':
motorControl.cpp:(.text+0x123): undefined reference to `CAN_DeviceOpen'
/usr/bin/ld: motorControl.cpp:(.text+0x196): undefined reference to `CAN_ChannelStart'
/usr/bin/ld: motorControl.cpp:(.text+0x1b9): undefined reference to `CAN_GetDeviceInfo'
/usr/bin/ld: CMakeFiles/can_node.dir/src/motorControl.cpp.o: in function `send_func(unsigned int, char, unsigned int*)':
motorControl.cpp:(.text+0x5eb): undefined reference to `CAN_ChannelSend'
/usr/bin/ld: CMakeFiles/can_node.dir/src/motorControl.cpp.o: in function `receive_func(void*)':
motorControl.cpp:(.text+0x6b8): undefined reference to `CAN_ChannelReceive'
/usr/bin/ld: motorControl.cpp:(.text+0x7d7): undefined reference to `CAN_GetErrorInfo'
/usr/bin/ld: CMakeFiles/can_node.dir/src/motorControl.cpp.o: in function `CloseCAN()':
motorControl.cpp:(.text+0x891): undefined reference to `CAN_ChannelStop'
/usr/bin/ld: motorControl.cpp:(.text+0x8a3): undefined reference to `CAN_ChannelStop'
/usr/bin/ld: motorControl.cpp:(.text+0x8b0): undefined reference to `CAN_DeviceClose'
/usr/bin/ld: CMakeFiles/can_node.dir/src/motorControl.cpp.o: in function `Stop_Rec_pthread()':
motorControl.cpp:(.text+0xc3f): undefined reference to `CAN_ChannelStop'
/usr/bin/ld: motorControl.cpp:(.text+0xc4c): undefined reference to `CAN_DeviceClose'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/can_node.dir/build.make:146: can_node] Error 1
make[1]: *** [CMakeFiles/Makefile2:132: CMakeFiles/can_node.dir/all] Error 2
make: *** [Makefile:141: all] Error 2
---
Failed <<< test_can [0.20s, exited with code 2]
Summary: 0 packages finished [0.29s]
1 package failed: test_can
1 package had stderr output: test_can
这一步是我们已经可以通过motorControl.h调用motorControl.cpp中的函数,但是我们不能使用ICANCmd.h关联的.so文件库导致无法实例化电机驱动。
还需要继续修改CMakeLists.txt添加如下一段
target_link_libraries(can_node
${PROJECT_SOURCE_DIR}/lib/libCanCmd.so
)
到这一步编译是不会报错的但是执行:
ros2 run test_can can_node
会报 /can_test_ws/install/test_can/lib/test_can/can_node: error while loading shared libraries: libCanCmd.so: cannot open shared object file: No such file or directory的错误
继续修改CMakeLists.txt(⊙﹏⊙)
install(
DIRECTORY lib/ # 发现你项目中的lib中所有的文件
DESTINATION lib/${PROJECT_NAME} # 拷贝到install目录中
)
install(TARGETS can_node
RUNTIME DESTINATION lib/${PROJECT_NAME} # 程序运行的时候调用install中的路径
)
再次编译执行即可调用,至此已经完成了C++版本的ROS2调用第三方.so文件库。
若本文有任何错误还请读者指出!