問題分析
自己寫的系統必須基於opencv 2.x,而ros kinetic自帶了opencv3的版本。於是在編譯時報錯:
/usr/bin/ld: CMakeFiles/xxxx.cpp.o: undefined reference to symbol '_ZN2cv6String10deallocateEv'
/opt/ros/kinetic/lib/x86_64-linux-gnu/libopencv_core3.so.3.3.1: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
CMakeFiles/xxxx/build.make:192: recipe for target '../xxxx' failed
而自己的系統裏 Pangolin 等一堆庫都基於2.x版本opencv了,不好再做調整,於是只好強行調整ros的opencv依賴來使其適應我的系統了。
ros中用到opencv的地方是從話題中讀取圖像,如 /camera/image_raw話題中抓取 rgb或depth圖像保存爲cv::Mat,所需用到的ros包是cv_bridge。
因此核心問題是: ros的包cv_bridge使用默認opencv3編譯,而自身系統使用opencv 2.x編譯,opencv的混合編譯帶來了錯誤。
問題解決方法
方法一共有兩種:
1) 淺依賴可以直接調整cv_bridge的cmake文件,詳細可參考:https://www.mobibrw.com/2017/8718
這個方法可以幫助我通過編譯,然而實際運行時會報內存錯誤。因此放棄選擇第二個方式,各位如果想省時間也可以試試這個方式,只需要改文件的幾行路徑。
注意: 改完cv_bridge的cmake文件後,一定要 rm -rf build 文件然後重新 cmake .. & make 纔會生效。
2)重度依賴,一勞永逸
直接將cv_bridge卸載掉,然後從源代碼重新編譯,詳細參考:https://blog.csdn.net/xiat5/article/details/79354573
下面是我的執行過程。
步驟
使用opencv 2.x版本重新編譯 cv_bridge。
1、 卸載 cv_bridge
使用指令卸載cv_bridge,
$ sudo apt-get remove --purge ros-kinetic-cv-bridge
這裏會出現提示說下列包會被一併卸載,不用慌,直接y卸載,實測不影響ros 通信等基本功能的使用 [ 這裏有一點提一下,可見文章最後 ]:
ros-kinetic-camera-calibration ros-kinetic-compressed-depth-image-transport
ros-kinetic-compressed-image-transport ros-kinetic-cv-bridge ros-kinetic-depth-image-proc ros-kinetic-desktop
ros-kinetic-desktop-full ros-kinetic-gazebo-plugins ros-kinetic-gazebo-ros-pkgs ros-kinetic-image-pipeline
ros-kinetic-image-proc ros-kinetic-image-publisher ros-kinetic-image-rotate ros-kinetic-image-transport-plugins
ros-kinetic-image-view ros-kinetic-perception ros-kinetic-rgbd-launch ros-kinetic-rqt-common-plugins
ros-kinetic-rqt-image-view ros-kinetic-simulators ros-kinetic-stereo-image-proc
ros-kinetic-theora-image-transport ros-kinetic-vision-opencv ros-kinetic-viz
2、 到github上clone 下來cv_bridge
gihub地址爲: https://github.com/ros-perception/vision_opencv
切換kinetic版本:
$ git checkout kinetic
3、 修改CMakeLists.txt文件指定爲你自己的opencv版本.
拿我的爲例子,我的opencv 2.4.11的build路徑爲: ~/opencv_2.4.11/build
於是我在 find_package(OpenCV)前加入一行::
set( OpenCV_DIR "~/opencv_2.4.11/build" )
建議可以使用message輸出一下opencv version或 opencv_include 目錄來確保cmake加載的自己版本的opencv.
3、最後
$ cmake .. && make
$ sudo make install
出現下列情況表示安裝成功
... -- Set runtime path of "/usr/local/lib/libcv_bridge.so" to "" |
4、回到自己最初需要用ros的代碼裏,對CMakeLists.txt中做一些調整:
set(cv_bridge_DIR /usr/local/share/cv_bridge/cmake)
find_package(cv_bridge)
include_directories(${cv_bridge_DIR})
target_link_libraries中將cv_bridge的庫文件鏈接上
${cv_bridge_LIBRARIES}
然後按順序編譯,大功告成!!!!
其他
1、so文件來源
一開始我檢查自己的CMakeLists.txt中,所有 target_link_libraries 的地方都沒找到opencv 3.x的引用,後來梳理了ros的性質,
rosbuild_add_executable() 指令會從 manifest.xml 中讀取包,而我的manifest.xml中包含了 cv_bridge
而cv_bridge自帶的 cmake文件中加載了 3.x的so文件.
2、 關於ros自帶包在CMakeLists.txt中的結構
這個過程其實是把 cv_bridge 當成了一個普通的庫來使用的。
而ros有一套體系來讓這些很簡便, 如 catkin components中直接加入 cv_bridge,其實底層和這個過程是一樣的。
所以這裏弄明白了,ros的底層依賴關係也差不多可以自己diy了。
4、關於刪除 cv_bridge 後的影響
這裏卸載cv_bridge後發生了ros gazebo不發佈話題的問題,其實仔細一想自帶的cv_bridge可以留着,然後通過在CMakeLists.txt中指定 cv_bridge_DIR的方式來選擇性調用cv_bridge版本。
5、 兩個版本的安裝路徑
opencv 2.4.11 檢查安裝路徑爲: /usr/local
ros-kinetic安裝了opencv3, 檢查安裝路徑爲:
lib: /opt/ros/kinetic/lib/x86_64-linux-gnu
include: /opt/ros/kinetic/include/opencv-3.3.1-dev