ROS講座108 ROSでDatabase(mongo)を使う

# 環境
この記事は以下の環境で動いています。

| 項目 | 値 |
|:-:|:-:|
| CPU | Core i5-8250U |
| Ubuntu | 16.04 |
| ROS | Kinetic |
| python | 2.7.12 |
| mongodb| 2.6.10 |

インストールについては[ROS講座02 インストール](https://qiita.com/srs/items/e0e0a9dc3f94c2d3348e)を参照してください。
またこの記事のプログラムはgithubにアップロードされています。[ROS講座11 gitリポジトリ](https://qiita.com/srs/items/25a56a6abda41dcf396e)を参照してください。

# 概要
ROSでは情報は基本的にrosbagに保存しますがこれは後から人間が見返すデバッグログ的な物であり、実行時に再利用するデータベース的になデータは各ノードが保存するか自前でファイルをwrite, readするしかありません。ここで出てくるものがDataBaseで、ROSではmongodbというDataBaseとつながるパッケージが公開されています。

## mongodbの構成
mongodbは`database -> collection -> document`という構成をしています。

* documentは実際のデータの実態で、excelでの1行に当たります。
* collectionはdocumentを複数まとめたもの
* batabaseはcollectionを複数まとめたもの

# インストール
```shell:mongodb_storeのインストール
sudo apt install ros-kinetic-mongodb-store
```

# mongodb_storeで書き込みをしてみる

## DataBaseの実行
起動時にデータベースのファイルを置くディレクトリを決めます。空のディレクトリを作成します。存在しないディレクトリを指定するとエラーになります。

```shell:mongodb_storeの実行
mkdir /tmp/test_db
roslaunch mongodb_store mongodb_store.launch db_path:=/tmp/test_db port:=27017
```

## 書き込みノードの実行

```python:web_lecture/scripts/mongo_pose_write.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import rospy
from mongodb_store.message_store import MessageStoreProxy
from geometry_msgs.msg import Pose, Point, Quaternion

if __name__ == '__main__':
    rospy.init_node("mongo_pose_write")

    msg_store = MessageStoreProxy(database="srs", collection="pose1")
    p = Pose(Point(0, 1, 2), Quaternion(0, 0, 0, 1))

    try:
        p_id = msg_store.insert_named("named_pose", p)
        p_id = msg_store.insert(p)

    except rospy.ServiceException, e:
        print "Service call failed: %s"%e
```

* `MessageStoreProxy(database="srs", collection="pose1")`で書き込みをするためのオブジェクトを生成します。ここでdatabas名とcollection名を指定します。
* `msg_store.insert_named("named_pose", p)`でデータを書き込みます。

```shell:書き込みノードの実行
rosrun web_lecture mongo_pose_write.py
```

## 読み込みノードの実行

```python:web_lecture/scripts/mongo_pose_read.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import rospy
import mongodb_store_msgs.srv as dc_srv
import mongodb_store.util as dc_util
from mongodb_store.message_store import MessageStoreProxy
from geometry_msgs.msg import Pose, Point, Quaternion
import StringIO

if __name__ == '__main__':
    rospy.init_node("mongo_pose_write")

    msg_store = MessageStoreProxy(database="srs", collection="pose1")

    try:
        for item in msg_store.query(Pose._type):
            print item[0].position

    except rospy.ServiceException, e:
        print "Service call failed: %s"%e
```

* `msg_store.query(Pose._type)`でデータベースの中からpose型のデータを抽出してきます。もと詳しくクエリを入れることもできます。
* 結果はリストで帰ってきます。要素の中の0番目はROSのメッセージ型になっています。1番目にはメタ情報が入っています。

```shell:読み込みノードの実行
rosrun web_lecture mongo_pose_read.py 
```

```text:実行結果
x: 0.0
y: 1.0
z: 2.0
x: 0.0
y: 1.0
z: 2.0
```

# mongoでデータを読む
ROSのインターフェースではなく直接databaseを読むこともできます。

`mongo`とコマンドを起動するとmongoのコンソールにつながります。

* `show dbs`でdatabaseの一覧を見れます。
* `use {database名}`でそのdatabaseを使用します。
* `show collections`でcollectionの一覧を見れます。
* `db.{collection名}.find()`でそのcollectionの中のデータの全体を見れます。

# コメント
すでに裏でmongodbが立ち上がっているとport27017が抑えられているので、roslaunchしてもエラーになります。
`systemctl status mongodb.service`でserviceが立ち上がっていないか見ましょう。立ち上がっている場合は`sudo systemctl disable mongodb.service && sudo systemctl stop mongodb.service`で停止しましょう。

# 参考
[mongodb_store](https://wiki.ros.org/mongodb_store)

# 目次ページへのリンク
[ROS講座の目次へのリンク](https://qiita.com/srs/items/5f44440afea0eb616b4a)