ROS on python3 (pyenv, venv)の環境構築

ROSをpython3で使用したかったため環境構築をしました。
基本的には[参考](#参考)の通りに進めたのですが、そのままの手順では失敗することがあったことと、pythonは仮想環境上のものを使用したかったので、改めて環境構築手順をまとめました。

# 参考
[rospy on Python3](https://ros.youtalk.jp/2018/12/10/python3-rospy.html)
[公式チュートリアル](http://wiki.ros.org/Installation/Source)

# 環境
ubuntu : 16.04
ros    : kinetic
python : 3.6.8

# python仮想環境
pythonのバージョンは`pyenv`で、そのバージョン上での仮想環境は`venv`で構築しました。
これらの使い方は他に詳しいサイトがたくさんあるので省略します。
なお、rosの実行の際に共有ライブラリが必要になるので`pyenv`でpythonをインストールするときはオプションをつけて

```
$ env PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install 3.6.8
```
のようにインストールしてください。

私は`pyenv`と`venv`をそれぞれ以下のディレクトリに構築したため、説明ではこのディレクトリを使用します。

```
~/.pyenv
~/.venv
```


# ros環境構築
以後の環境構築の際は全てpythonの仮想環境上で行ってください

## pythonパッケージのインストール

```
$ pip install -U trollius rosdep rosinstall-generator wstool rosinstall defusedxml netifaces
$ pip install --upgrade setuptools
```
[参考](#参考)の手順より多いですが、これだけインストールしないとビルドおよび実行時にエラーがでました。
また、もし途中で`enum34`が見つからない、といったエラーが出た場合は、

```
$ pip uninstall enum34
$ pip install empy
```
とする必要があります。

## rosのビルド
最初に以下を実行します。

```
$ rosdep init
$ rosdep update
```
次にrosのソースコードを取得します。今回は最低限必要な`ros_comm`だけです。
また、ここではrosを`~/ros_kinetic`ディレクトリでビルドします。

```
$ mkdir ~/ros_kinetic
$ cd ~/ros_kinetic
$ rosinstall_generator ros_comm --rosdistro kinetic --deps > kinetic-ros_comm.rosinstall
$ wstool init -j8 src kinetic-ros_comm.rosinstall
```
wstoolの実行後、私の環境では以下のエラーが発生しました。いくつかのパッケージの取得に失敗しただけだったので、直接githubからクローンしました。

```
ERROR in config: Error processing 'geneus' : [geneus] Checkout of https://github.com/tork-a/geneus-release.git version release/kinetic/geneus/2.2.6-0 into ~/ros_kinetic/src/geneus failed.
Error processing 'gennodejs' : [gennodejs] Checkout of https://github.com/RethinkRobotics-release/gennodejs-release.git version release/kinetic/gennodejs/2.0.1-0 into ~/ros_kinetic/src/gennodejs failed.
Error processing 'ros/rosboost_cfg' : [ros/rosboost_cfg] Checkout of https://github.com/ros-gbp/ros-release.git version release/kinetic/rosboost_cfg/1.14.4-0 into ~/ros_kinetic/src/ros/rosboost_cfg failed.
```
```
$ cd src
$ git clone https://github.com/tork-a/geneus-release.git geneus
$ cd geneus
$ git checkout release/kinetic/geneus/2.2.6-0
(以下略)
```

## rosのビルド
私の環境では`rospack`、`roslz4`のビルド時に

```
Could NOT find PythonLibs (missing: PYTHON_LIBRARIES PYTHON_INCLUDE_DIRS)
```
というエラーが発生し

```
find_package(PythonLibs)
```
に失敗しました。そのため、最初にそれぞれのパッケージの`CMakeLists.txt`を修正します。

```cmake:CMakeLists.txt

set(Python_ADDITIONAL_VERSIONS "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}")   # 削除
find_package(PythonLibs "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}" REQUIRED) # 削除

set(PYTHON_LIBRARIES ~/.pyenv/versions/3.6.8/lib/libpython3.6m.so)  # 追加
set(PYTHON_INCLUDE_DIRS ~/.pyenv/versions/3.6.8/include/python3.6m) # 追加
set(PYTHON_INCLUDE_PATH ~/.pyenv/versions/3.6.8/include/python3.6m) # 追加
```
本来であれば`find_package()`に成功するように修正するべきですが、できませんでした。
もし分かる方がいたら教えていただきたいです。

最後にビルドします。エラーが出るため上記[参考](#参考)の手順に加えて`-DSETUPTOOLS_DEB_LAYOUT=OFF`オプジョンを追加しました。

```
$ cd ~/ros_kinetic
$ ./src/catkin/bin/catkin_make_isolated --install -DCMAKE_BUILD_TYPE=Release -DSETUPTOOLS_DEB_LAYOUT=OFF
```
エラーが出なければ成功です。最後に

```
$ source　install_isolated/setup.bash
```
をしておきます。

## rosのワークスペース作成および実行
ワークスペースの作成と実行は通常の手順と同じです。
私はrosをインストールしたディレクトリとは別にワークスペースを作成し、実行しました。

```
$ mkdir -p ~/catkin_ws/src
$ cd ~/catkin_ws/src
$ catkin_init_workspace
$ cd ..
$ catkin_make
$ source devel/setup.bash
```
ただし、実行前にpythonライブラリのパスを通しておく必要があります。

```
export PYTHONPATH=~/.pyenv/versions/3.6.8/lib/python3.6/site-packages
```
動作確認のため[以前作成したパッケージ](https://qiita.com/hiro_han/items/5989b52054fb05455fb4)に

```
import sys

rospy.loginfo(sys.version)
```
を追加して実行したところpython 3.6が呼び出されていることを確認しました。

## おまけ
rosの実行時に複数のコンソールでpythonの仮想環境を立ち上げたりrosの設定をするのが面倒なので以下のようなスクリプトを作り、コンソールを立ち上げ後このスクリプトを実行すればいいだけにしています。

```sh:ros_setup.sh
source ~/.venv/ros/bin/activate
export LD_LIBRARY_PATH=~/.pyenv/versions/3.6.8/lib:$LD_LIBRARY_PATH
export PYTHONPATH=~/.pyenv/versions/3.6.8/lib/python3.6/site-packages
source ~/ros_kinetic/devel_isolated/setup.bash
source ~/catkin_ws/devel/setup.bash
export ROS_HOSTNAME=192.168.xxx.xxx
export ROS_MASTER_URI=http://${ROS_HOSTNAME}:11311
export ROS_IP=`hostname -I`
```