PythonでEV3のモータとセンサを制御してみよう

本記事は[ETロボコンAdvent Calendar 2016](http://qiita.com/advent-calendar/2016/etrobo) 12/22のエントリです。

---
こんにちは、はねうまです。

昨年まで、私達のチーム「銀河系はねうま」はJavaでETロボコンに参加してきましたが、
今年は心機一転、Pythonでの参加に挑戦してみました。

しかし、いざ挑戦しようと情報を調べても中々必要な情報に辿り着けませんでした。
そこで、これからPythonでLEGO Mindstorms EV3を動かしてみたい人の参考になればと思い、
この記事を書く事にしました。
今回の記事は利用者が多いと思われるため、Windowsを対象にしています。

また、今後はPythonの開発環境やev3devのクロスコンパイル環境の話などを投稿しようと思います。

#実行環境
OS:ev3dev 2016-10-17Release版 (DebianベースのLinux)
使用ライブラリ:[python-ev3dev 0.8.0](http://python-ev3dev.readthedocs.io/en/latest/) (**python-ev3**というライブラリがあるが、別物なので注意)
Pythonバージョン:Python 3.4.2
(python-ev3dev 0.8.0はPython2へは対応していないため、必ずPython3を使用すること)


#ev3devのインストール/起動
[参考][Getting Started with ev3dev-ev3dev.org](http://www.ev3dev.org/docs/getting-started/)

1.OSイメージをev3dev公式サイトからダウンロード
下記のリンク先で「Download for EV3」をクリックしてZIPファイルをダウンロードします。
[Downloads-ev3dev.org](http://www.ev3dev.org/download/)
(執筆時点のバージョンは2016-10-17版)

2.ダウンロードしたZIPファイルを展開
ev3devのイメージファイルが展開されます。
(2016-10-17版では、イメージファイル名は「ev3dev-jessie-ev3-generic-2016-10-17.img」)

3.イメージファイルをSDカードに書き込む
Win32DiskImagerを使用して、展開したイメージファイルをSDカードに書き込みます。
書き込み手順は以下のサイトに記載されているので、参考にしてください。
→[SD カードの準備-OpenRTM-aist](http://openrtm.org/openrtm/ja/content/sd-%E3%82%AB%E3%83%BC%E3%83%89%E3%81%AE%E6%BA%96%E5%82%99)

なお、対応しているSDカードは**<font color="Red">2GB~32GB</font>**の**<font color="Red">microSDもしくはmicroSDHC</font>**です。
microSDXCは対応していないため注意してください。


4.書き込んだSDカードをEV3に挿入し、EV3を起動する
ブートログが出力され、しばらく待つとメニュー画面が表示されます。
メニュー画面が表示されれば成功です。

#ev3devをネットワークに接続
EV3をネットワークに接続するには以下のように複数の方法があります。

- wifiドングルを使用して無線LAN接続
 →[EV3-Pythonプログラミング（ev3dev）環境　wifi接続手順-Afrel](http://www.afrel.co.jp/archives/16530)

- USB LANアダプタを使用して有線LAN接続
→バッファロー LUA3-U2-ATX にて動作確認済

- USBケーブル(Type A-mini B)を使用して接続
→教育版レゴ マインドストームEV3基本セットV2に付属のUSBケーブルにて動作確認済

- 内蔵Bluetoothを使用して接続
→動作未確認

※LAN接続する場合は、DHCPを利用できる環境、もしくは手動でIPアドレスを設定する必要があります。

#SSHでEV3にリモートログイン
SSHを利用してev3devにログインします。
ここではTeraTermを使用して接続しています。

1.TeraTerm を起動後、ホストにEV3のIPアドレスを入力し「OK」ボタンを押下する。
<img width="50%" alt="ssh_1.png" src="https://qiita-image-store.s3.amazonaws.com/0/154373/a1ac107b-a7e4-e82d-0c0d-4c39c31a94d8.png">
※IPアドレスはEV3本体の画面左上に表示されています。

2.セキュリティ警告のウィンドウが表示された場合は「続行」ボタンを押下する。
<img width="50%" alt="ssh_2.png" src="https://qiita-image-store.s3.amazonaws.com/0/154373/5b817304-c680-2582-2b8b-5bb926bc2f3e.png">

3.ユーザ名及びパスフレーズ (パスワード)を入力する。ID: **robot** / Pass: **maker**
<img width="50%" alt="ssh_3.png" src="https://qiita-image-store.s3.amazonaws.com/0/154373/824193a0-8e60-4085-3b0f-62adf1ea405e.png">

4.ログインが成功し、ターミナル上にev3devのロゴが表示される。
<img width="50%" alt="ssh_4.png" src="https://qiita-image-store.s3.amazonaws.com/0/154373/31d507f6-0b1c-743d-22ef-9d1412bc5ba8.png">

# python-ev3devのアップデート
ev3dev 2016-10-17Release版に入っているpython-ev3dev はバージョンが0.7.0のためアップデートを行います。
(python-ev3dev 0.8.0でセンサ周りのAPIが若干変わったため、以前のバージョンで実装したソースコードがある方は注意してください)

```
robot@ev3dev:~$ sudo apt-get update
robot@ev3dev:~$ sudo apt-get install --only-upgrade python3-ev3dev
```
※コマンドの途中でパスワードの入力を求められるため、makerと入力する

#サンプルプログラム
以下にモータ制御とセンサ制御のサンプルプログラムを記載します。
プログラムの文字コードはUTF-8で保存してください。

###モータ制御

```py3:motor.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import ev3dev.ev3 as ev3
import time

# モータクラスをインスタンス化
m = ev3.LargeMotor('outA')
# モータを3秒間回転
m.run_timed(time_sp=3000, speed_sp=500)

# 5回ループ
for i in range(0,5):
    # モータの状態を標準出力に表示
    print(m.state())
    # 1秒間スリープ
    time.sleep(1)
```


###センサ制御(カラーセンサ)

```py3:color.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import ev3dev.ev3 as ev3

# カラーセンサクラスをインスタンス化
c = ev3.ColorSensor('in1')

# 赤色LEDの光の反射強度を表示
print(c.reflected_light_intensity)
# センサモードを表示
print(c.mode)

# RGB値を表示
print(c.raw)
# センサモードを表示
print(c.mode)

# 取得した色を表示(0:無色, 1:黒, 2:青, 3:緑, 4:黄, 5:赤, 6:白, 7:茶)
print(c.color)
# センサモードを表示
print(c.mode)
```

###モータ＋センサ制御

```py3:motor_color.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import ev3dev.ev3 as ev3
import time

# モータクラスをインスタンス化
m = ev3.LargeMotor('outA')

# カラーセンサクラスをインスタンス化
c = ev3.ColorSensor('in1')

# モータを回転(stopをコールするまで回転し続ける)
m.run_forever(speed_sp=300)

# カラーセンサが黒を検出するまでループ
while c.color != 1:
    # 取得した色を表示(0:無色, 1:黒, 2:青, 3:緑, 4:黄, 5:赤, 6:白, 7:茶)
    print(c.color)
    # 1秒間スリープ
    time.sleep(1)
    
# モータを停止(停止後、モータの位置を固定するためstop_actionにholdを指定)
m.stop(stop_action="hold")
```

#サンプルプログラムの転送
ev3devはデフォルトでSFTPが利用できるため、Windowsからサンプルプログラムを転送する場合は、WinSCPを利用するのがおすすめです。
WinSCPの操作方法は割愛します。

接続に使用する設定は以下の通りです。
転送プロトコル:SFTP
ホスト名:EV3の画面左上に表示されているIPアドレス
ポート番号:22
ユーザ名:robot
パスワード:maker

#サンプルプログラムの実行
今回はサンプルプログラムのmotor.pyを例にします。

###ターミナル上から実行する場合

```
robot@ev3dev:~$ pwd
/home/robot
robot@ev3dev:~$ ls
color.py  motor.py  motor_color.py
robot@ev3dev:~$ python3 motor.py
```

実行結果

```
robot@ev3dev:~$ python3 motor.py
['running', 'stalled']
['running']
['running']
[]
[]
robot@ev3dev:~$ 
```
***
###EV3本体から起動する場合
EV3本体からプログラムを起動する場合は、事前に対象のプログラムに実行権を付与する必要があります。
実行権を付与するにはchmodコマンドを使用します。

```
robot@ev3dev:~$ pwd
/home/robot
robot@ev3dev:~$ ls -l
total 12
-rw-r--r-- 1 robot robot 522 Dec 21 07:55 color.py
-rw-r--r-- 1 robot robot 236 Dec 21 04:19 motor.py
-rw-r--r-- 1 robot robot 735 Dec 21 08:03 motor_color.py
robot@ev3dev:~$ chmod +x motor.py
robot@ev3dev:~$ ls -l
total 12
-rw-r--r-- 1 robot robot 522 Dec 21 07:55 color.py
-rwxr-xr-x 1 robot robot 236 Dec 21 04:19 motor.py
-rw-r--r-- 1 robot robot 735 Dec 21 08:03 motor_color.py
robot@ev3dev:~$
```

実行権の付与が完了後、EV3本体の画面上からFile Browserを選択してください。
カーソル移動はEV3本体の上下ボタン、決定は中央のボタンで行えます。

File Browserを開くと、最初に/home/robot配下が表示されるため、motor.pyを選択します。
正常に実行権が付与されている場合は、ファイル名の横にアスタリスクが表示されます。

ファイルを選択してしばらくするとプログラムが実行され、終了するとFile Browserの画面に戻ります。

実行結果

```
['running', 'stalled']
['running']
['running']
[]
[]
```

#問題点
開発にあたって気づいた問題点をいくつか列挙します。

 - 二輪の倒立振子ライブラリが提供されていない
 →ETロボコン2016公式ではプライマリークラスは参加不可とアナウンスされています。
[利用可能なプラットフォーム情報-ETロボコン2016](https://github.com/ETrobocon/etroboEV3/wiki)

 - カラーセンサのLEDが消灯できない
 →OSの仕様上、カラーセンサのLEDを消灯できないため、まいまい式のライントレースが行えません。

 - 連続でカラーセンサでRGB値を取得するとエラーになる
 →カラーセンサを使用していると、値取得時にエラーが発生する場合があります。
　Linuxのdmesgで確認すると、バッファーオーバーランのエラーが発生しているのと関係している？(原因等をご存知の方がいたら教えてください)

 - 処理速度が遅い
 →TOPPERSなどのRTOSと比較して処理速度が遅いため、ETロボコンの競技上不利になる可能性があります。


#API
モータクラスのAPIの一部の使用例を記載します。

 - [モータクラス](http://python-ev3dev.readthedocs.io/en/latest/motors.html#tacho-motor)

```py3:motorApiTest.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import ev3dev.ev3 as ev3
import time

l = ev3.LargeMotor('outA')  # EV3 インタラクティブサーボモーターL
m = ev3.MediumMotor('outB') # EV3 インタラクティブサーボモーターM

print(l.count_per_rot) # 1回転あたりのモータカウント

l.speed_sp = 200 # 目標スピードを設定(秒間200カウント)
print(l.speed_sp) # 設定されている目標スピード値を表示

l.run_forever() # モータを回転させる
m.run_forever(speed_sp=200) # 回転と同時にスピードを設定することも可能

time.sleep(2)

l.speed_sp = 300 # モータ回転速度を変更
l.run_forever()

m.speed_sp = -200 # モータを逆回転
m.run_forever()

time.sleep(2)

l.stop_action = 'hold' # 停止時のモータ状態を「固定」に設定
print(l.stop_action) # モータ停止後の動作を表示

l.stop() # モータを停止
m.stop(stop_action='brake') # モータを停止(停止時のモータ状態を「ブレーキ」に設定

print(l.position) # モータカウント値を表示

# モータを100カウント分回転
l.run_to_abs_pos(position_sp=l.position_sp+100) # 絶対値指定
m.run_to_rel_pos(position_sp=100) # 相対値指定
```