ROS2 CmakeList如何正确引入第三方.so文件

在使用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.cpp

5 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.cpp

6 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文件库。

若本文有任何错误还请读者指出!

你可能感兴趣的:(Ros2记录,大数据)