ROS KineticでOpenCV3 x CUDA 8.0RCのロボット用GPGPU環境を構築する

# はじめに
ロボットにGPUを活用した並列計算を行うことによって、ニューラルネットワークや画像フィルタ処理の高速化が可能になります(特に行列計算の高速化)。
私が開発している生活支援ロボットHappy Miniは音声認識、画像認識にGPGPUを使用しています。
[Happy MiniのFacebookページ](https://www.facebook.com/happyrobotmini/?fref=ts)
![happy_mini.jpg](https://qiita-image-store.s3.amazonaws.com/0/54829/f2af6a98-0d1a-cff1-a899-faf0628fc583.jpeg)


GPGPUを使用する場合は、NVIDIA製のGPUをおすすめします。
OpenCV, PCL, Caffe, Torch, Chainer, TensorFlowなど様々な画像処理ライブラリ, ニューラルネットワークライブラリがNVIDIA GPU専用GPGPUフレームワークCUDAに対応しているからです。
ノートPC用のNVIDIA GPUでもCUDAが使用できます。GPUの消費電力を抑えたい方は、NVIDIA Jetson TK1がおすすめです。  

それでは、ロボット開発ミドルウエアROS上でCUDA対応画像処理ライブラリOpenCVが動くようにして、ロボット用GPGPU環境を構築してみましょう。
Ubuntu/Xubuntu/Lubuntu/Kubuntu 16.04, ROS Kinetic, OpenCV 3とCUDA 8.0RCで環境構築を行います。

## Ubuntu 16.04をインストールする  
ROS Kineticを使用するにはUbuntu 16.04を使用する必要があります。ROS KineticとUbuntu 16.04はどちらもLTS(Long Time Support)なので数年この環境でロボットの開発が可能です。
私はXUbuntu 16.04を使用しています。

Ubuntu 14.04を使用している方はROS Indigoで環境構築を行ってください。
ROS Kineticは今年の5月にリリースされました。
私もROS Kineticを先日使い始めたのですが、ROS Indigoの方がROS Kineticより安定している気がします。

## ROS Kineticをインストールする  
ROS Kineticを[ROS Wiki](http://wiki.ros.org/kinetic/Installation/Ubuntu)の手順にしたがってインストールしてください。


## CUDA 8.0RCを環境構築する  
下記の作業はどのディレクトリで実行しても動きますが、ホームディレクトリ下で作業するのが良いと思います。

```bash
cd /home/$(whoami)
```
###1. NVIDIAグラフィックスドライバーをインストールする  
CUDAを使うにはNVIDIAグラフィックスドライバーをインストールする必要があります。
GUI環境でUbuntu 16.04を使用している方は
```システム設定 -> ソフトウェアとアップデート -> 追加のドライバー```  　　
からNVIDIAのグラフィックスドライバーをインストールしてください。
###2. NVIDIAホームページからCUDA 8.0 RCをインストールする
https://developer.nvidia.com/cuda-downloads
からNVIDIAのCUDA 8.0RCパッケージをダウンロードしてください。
パッケージがダウンロードされたディレクトリ下で、

```bash
sudo dpkg -i cuda-repo-ubuntu1604-8-0-local_8.0.44-1_amd64.deb
sudo apt-get update
sudo apt-get install cuda
echo "export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/cuda-8.0/lib64" >> ~/.bashrc 
```

を実行してください。
```nvcc --version```
を実行してエラーがでなければ、CUDA 8.0RCの環境構築は完了です。  

## ROS Kinetic用CUDA 8.0 RC対応OpenCV3を環境構築する  
###1. CUDA 8.0 RC対応OpenCV3をダウンロードする  

```bash   
#CUDA 8.0RCが対応しているOpenCV 3.1をダウンロードします。
sudo apt-get install git
git clone -b 3.1.0-with-cuda8 https://github.com/daveselinger/opencv opencv
```
  

###2. ROSパッケージのソースをaptから取得できるように設定する  

```bash
#http://packages.ros.org/ros/ubuntuからROSパッケージのURLが取得できる
sudo echo "deb-src http://packages.ros.org/ros/ubuntu xenial main" >> /etc/apt/sources.list.d/ros-latest.list
sudo apt-get update
```

###3. ros-kinetic-opencv3のソースを取得する  

```bash
sudo apt-get source ros-kinetic-opencv3  
```

###4. ros-kinetic-opencv3がCUDA 8.0RCで動作するようにする  
opencv/modules/cudalegacy/src/graphcuts.cppのNVIDIA APIの互換性の問題のため、標準のOpenCV3ではCUDA 8.0RC使用時にエラーが発生します。
CUDA 8.0RCで動作するように修正されたgraphcuts.cppをros-kinetic-opencv3のcudalegacyモジュール下にコピーしましょう。

```bash  
cp opencv/modules/cudalegacy/src/graphcuts.cpp  ros-kinetic-opencv3-3.1.0/modules/cudalegacy/src/graphcuts.cpp
```

###5. aptから取得したソースから.debパッケージを生成する  
```bash
#ビルドツールをインストール
sudo apt-get install devscripts build-essential
cd ros-kinetic-opencv3-3.1.0
#ros-kinetic-opencv3-3.1.0をビルドするために必要な依存関係をインストール
sudo apt-get build-dep ros-kinetic-opencv3-3.1.0
#ros-kinetic-opencv3-3.1.0をビルドし、.debパッケージを生成
sudo dpkg-buildpackage -b -uc 
```
.debパッケージはros-kinetic-opencv3-3.1.0の1階層上のディレクトリに生成されます。
 

###6. 生成した.debパッケージをローカルaptレポジトリに登録する  
```bash
cd .. 
sudo mkdir /usr/src/deb
cp ros-kinetic-opencv3_3.1.0-16xenial_amd64.deb /usr/src/deb/
cd /usr/src/deb/  
sudo chmod a+wr /usr/src/deb
#aptレポジトリで管理するためにSourcesとPackagesディレクトリを生成。gzip圧縮を行っている。
sudo apt-ftparchive packages . | gzip -c9 > Packages.gz
sudo apt-ftparchive sources . | gzip -c9 > Sources.gz
#aptのローカルレポジトリの情報をros-latest.listに書き込む
sudo chmod a+wr /etc/apt/sources.list.d/ros-latest.list
sudo echo "deb file:/usr/src/deb ./" >> /etc/apt/sources.list.d/ros-latest.list
#ローカル環境のros-kinetic-opencv3をaptでインストールするため、ROSのaptレポジトリを一時的にコメントアウト
sudo sed -i -e "1,2s/^/#/g" /etc/apt/sources.list.d/ros-latest.list
sudo apt-get update
```

###7. CUDA8.0RCが動作するros-kinetic-opencv3をパッケージインストールする  
```bash
#元のros-kinetic-opencv3をアンインストール
sudo apt-get remove ros-kinetic-opencv3
#CUDA8.0RCが動作するros-kinetic-opencvをインストール
sudo apt-get install ros-kinetic-opencv3
#ros-kinetic-opencv3を依存関係とする他のROSパッケージをインストール
sudo apt-get install ros-kinetic-desktop-full
#ROSのaptレポジトリのコメントアウトをストリームエディタで元に戻す
sudo sed -i -e "s/#//g" /etc/apt/sources.list.d/ros-latest.list
```

## ※ROS Kinetic用CUDA 8.0 RC対応OpenCV3を環境構築する際の問題  
非常に複雑な手順を取らないとROS Kinetic用CUDA 8.0RC対応OpenCV3がインストールできませんでした。
このような複雑な手順を取らざるを得なかった理由を書きました。  

###1.ROS Kinetic用OpenCV3パッケージがCPUビルドしか対応していない
ROSをパッケージインストール時に、ros-kinetic-opencv3というOpenCV3のパッケージがインストールされます。 
ですが、このOpenCV3パッケージはCPUビルドされたOpenCVのバイナリが配布されたものです。そのため、OpenCV3のCUDA関連モジュールはインストールされていません。  
###2.公式のOpenCV3を使用してROSプロジェクトをビルドすることが出来ない
CUDA設定済みの[公式OpenCV3](https://github.com/opencv/opencv)をソースインストールし, 
CMakeでCUDA設定済みのOpenCV3を適用する設定を書いてもこの問題は解決できません。

このような設定を書いても、なぜかros-kinetic-opencv3の共有オブジェクトライブラリ(/opt/ros/kinetic/lib)とヘッダーファイル(/opt/ros/kinetic/include)がビルド時に先に評価されてしまいます。

CMakeを拡張したROS専用ビルドシステムcatkinかROSのエコシステムがこの原因を引き起こしているのではないかと思います。ROS Indigoから、CMakeで```find_package(OpenCV REQUIRED)```をしなくてもOpenCVのライブラリがリンク・インクルードできるようになっています。私はこの原因が重なったのではないかと思っています。

```cmake:CMakeLists.txt    
find_package(OpenCV 3 REQUIRED
NO_MODULE 
PATHS /usr/local 
NO_DEFAULT_PATH)
```
結果としてros-kinetic-opencv3のライブラリがリンクされてしまい、
ビルドが通っても「CUDAがサポートされていない」というランタイムエラーが発生します。

```text
OpenCV Error: No CUDA support (The library is compiled without CUDA support) in throw_no_cuda, 
file /tmp/binarydeb/ros-kinetic-opencv3-3.1.0/modules/core/include/opencv/core/private.cuda.hpp, 
line 97 terminate called after throwing an instance of 'cv::Exception' what(): 
/tmp/binarydeb/ros-kinetic-opencv3-3.1.0/modules/core/include/opencv2/core/private.cuda.hpp:97: 
error: (-216) The library is compiled without CUDA support in function throw_no_cuda 
```

ros-kinetic-opencv3をアンインストールしても、この問題は解決できません。
なぜなら、```cv_bridge```, ```image_transport```, ```image_view```など他の重要な画像関連のROSの20個のパッケージがros-kinetic-opencv3に依存していてアンインストールされてしまうからです。```cv_bridge```や```image_transport```はROSで画像処理プログラムを作るときに必須のライブラリです。
依存している20個のパッケージのソースビルドは大変です。
なぜか大半のパッケージがOpenCV2に依存しており、OpenCV3ではビルドが通りません。  

※詳しくは

```bash
apt-cache rdepends ros-kinetic-opencv3
```
を実行して、表示されたros-kinetic-opencv3の依存関係のあるパッケージのソースをGitHubやaptから取得し、読んでビルドしてください。



###3.標準のOpenCV3がCUDA 8.0RCに対応していない
標準のOpenCV3がCUDA 8.0RCに対応していません。
そこで、CUDA 8.0RCに対応したOpenCV3を使用する必要があります。
CUDA 8.0RC対応OpenCV3 Releaseバージョンは https://github.com/daveselinger/opencv の3.1.0-with-cuda8ブランチからダウンロードできます。

※詳しくはこちらのissueを見てください。
https://github.com/opencv/opencv/issues/6677