问题
海康威视的相机SDK,包含大量的动态库,如果在它之上开发Linux应用程序,则不但编译时要链接那些动态库,运行时也要确保那些动态库存放在/lib
、/usr/lib
等目录,或存放在环境变量LD_LIBRARY_PATH
里指定自定义目录下,否则程序一运行就会报错,找不到动态库。
- 编译时的链接问题好解决,将动态库都放到源码的lib目录下即可。
- 运行时的加载库问题不好解决, 不论是将动态库拷贝到系统目录,还是设置环境变量,都很不方便,而且容易忘。
上述不便在更换开发调试环境时尤其显著,想象下,你将代码check到另一台机器上,编译通过,但运行报错,很影响效率啊!
ROS的解决方案
对于普通程序,或许上面的麻烦只能通过安装程序来解决,但ROS
有更好的方法。
节点代码目录结构
以我之前开发的qp_camera_server
节点举例,该节点将相机封装成ROS节点
,并向外暴露若干ROS服务
,例如拍照
、设置预置点
、调用预置点
等,这样机器人主控节点就可以导航到巡检点后,调它来拍照了。
qp_camera_server/
├── CMakeLists.txt
├── include
│ └── HCNetSDK.h
├── launch
│ ├── infra_cam.launch
│ └── vis_cam.launch
├── lib
│ ├── HCNetSDKCom
│ │ ├── libHCAlarm.so
│ │ ├── libHCCoreDevCfg.so
│ │ ├── libHCDisplay.so
│ │ ├── libHCGeneralCfgMgr.so
│ │ ├── libHCIndustry.so
│ │ ├── libHCPlayBack.so
│ │ ├── libHCPreview.so
│ │ ├── libHCVoiceTalk.so
│ │ ├── libStreamTransClient.so
│ │ ├── libSystemTransform.so
│ │ ├── libanalyzedata.so
│ │ ├── libiconv.so.2
│ │ └── libiconv2.so
│ ├── libAudioRender.so
│ ├── libHCCore.so
│ ├── libNPQos.so
│ ├── libPlayCtrl.so
│ ├── libSuperRender.so
│ ├── libcrypto.so
│ ├── libcrypto.so.1.0.0
│ ├── libhcnetsdk.so
│ ├── libhpr.so
│ ├── libssl.so
│ └── libssl.so.1.0.0
├── package.xml
└── src
└── qp_cam_server.cpp
节点CMakeLists.txt配置
这块的配置主要是方便catkin_make
连接动态库,但也对后面的运行有帮助。改动主要是新增2条cmake
指令:
- 额外增加一个link_directories指令,指示编译器可在哪些目录搜索动态库
- 增加一个target_link_libraries指令,指示编译器需要连接哪些动态库
cmake_minimum_required(VERSION 2.8.3)
project(qp_camera_server)
## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
qp_bot_msgs
roscpp
std_msgs
)
###################################
## catkin specific configuration ##
###################################
catkin_package(
)
###########
## Build ##
###########
## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
include
${catkin_INCLUDE_DIRS}
)
## Specify directories of libraries
link_directories(
lib
lib/HCNetSDKCom
)
## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
add_executable(${PROJECT_NAME} src/qp_cam_server.cpp)
## Add cmake target dependencies of the executable
## same as for the library above
add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
## Specify libraries to link a library or executable target against
target_link_libraries(${PROJECT_NAME}
${catkin_LIBRARIES}
hcnetsdk
HCCore
AudioRender
crypto
hpr
NPQos
PlayCtrl
ssl
SuperRender
analyzedata
HCAlarm
HCCoreDevCfg
HCDisplay
HCGeneralCfgMgr
HCIndustry
HCPlayBack
HCPreview
HCVoiceTalk
iconv2
StreamTransClient
SystemTransform
)
用ROS命令启动节点
工程用catkin_make
编译通过后,直接用rosrun命令启动节点即可,不会有找不到动态库
的报错
rosrun qp_camera_server qp_camera_server
上面rosrun命令的第一个参数是ROS包名
,第二个参数是ROS可执行程序名
,ROS节点名
不显式指定的话跟第二个参数相同。
作为对比,如果执行
/path/to/qp_camera_server
则会报找不到动态库
之类的错误。
总结
为何会这样?
猜测rosrun
命令作为启动器,在启动可执行程序前,将CMakeLists.txt
里指定的动态库搜索路径添加到环境变量LD_LIBRARY_PATH
里了,所以操作系统加载可执行程序时就能找到动态库了。
这样做还有个好处,系统可以存在多个版本的动态库,互不影响。