EV3を動かしてみる (4) 〜 センサの値を読む 〜

ロボティクス入門ゼミ 教材 2017-11-24

シェルからセンサの値を読む

EV3の標準キットには、タッチセンサ、カラーセンサ、超音波センサ、 ジャイロセンサが含まれている。 これらに加え、モータ自身も「角度センサ」として使用することができる。

センサの細かな仕様については、
http://docs.ev3dev.org/projects/lego-linux-drivers/en/ev3dev-jessie/sensor_data.html (ev3devのドキュメント)
を見るとよい。

センサは、1〜4のどのポートに接続してもかまわない。 また、モータと同様に、センサ類自身やそれら値などは、Linuxでは 「ディレクトリ」や「ファイル」として表されている。

まずセンサを接続していない状態で、次のようなセンサのディレクトリに 移動してみる。

robot@ev3dev:~$ cd /sys/class/lego-sensor/

センサを接続していない場合、このディレクトリが空であることを ls で確認する。

robot@ev3dev:/sys/class/lego-sensor$ ls

何も表示されければOK。

タッチセンサ

次にタッチセンサを接続する。 以下の例ではポート3に接続している(どのポートでもOK)。 接続後、再度 ls で確認。

robot@ev3dev:/sys/class/lego-sensor$ ls
sensor1

新たに sensor1 というディレクトリができている。 sensor1の"1" という数字はポートの番号とは関係なく、 接続した順番にセンサに割り振られる。 実際に、一度センサを外し、再度接続すると sensor2 になる。

次にこのディレクトリに移動してどのようなファイルがあるか見てみる。 (cd s まで入力して tabキーを押せば残りは補完してくれるはず。)

robot@ev3dev:/sys/class/lego-sensor$ cd sensor1

ファイル一覧。

robot@ev3dev:/sys/class/lego-sensor/sensor1$ ls
address          decimals     mode        subsystem   value1  value6
bin_data         device       modes       text_value  value2  value7
bin_data_format  direct       num_values  uevent      value3
command          driver_name  poll_ms     units       value4
commands         fw_version   power       value0      value5

以下、いくつかのファイルの中身を cat コマンドで見てみる。

robot@ev3dev:/sys/class/lego-sensor/sensor1$ cat address
in3

adress には接続ポートが格納されている。ポート3に接続しているので in3 になっている。

個々のセンサには「モード」があり、センサによっては複数のモードを切り替えて使用できる。 どのようなモードが指定可能であるかは modes というファイルに記述されている。 また現在のモードは、mode を見れば分かる

robot@ev3dev:/sys/class/lego-sensor/sensor1$ cat modes
TOUCH
robot@ev3dev:/sys/class/lego-sensor/sensor1$ cat mode
TOUCH

タッチセンサの場合は TOUCH というモードのみ。

タッチセンサの値は、value0 に格納されている。 タッチセンサを押していない場合は 0 である。

robot@ev3dev:/sys/class/lego-sensor/sensor1$ cat value0
0

今度はタッチセンサを押しながらvalue0の値を表示させてみる。

robot@ev3dev:/sys/class/lego-sensor/sensor1$ cat value0
1

タッチセンサが押されていれば value0 の値は 1 になる。

タッチセンサの場合、値は value0 の一つのみである。 value1からvalue7までのファイルは、ファイルとしては存在しているが、 これらの値を読むことはできない。 実際に、value1の値を読もうとするとエラーになる。

robot@ev3dev:/sys/class/lego-sensor/sensor1$ cat value1
cat: value1: No such device or address

ちなみに、値の数は num_values に格納されている。

robot@ev3dev:/sys/class/lego-sensor/sensor1$ cat num_values
1

カラーセンサ

次に、カラーセンサも接続してみる。以下の例ではポート2に接続している。

一つ上のディレクトリに戻って、ファイル一覧を表示させる。

robot@ev3dev:/sys/class/lego-sensor/sensor1$ cd ..
robot@ev3dev:/sys/class/lego-sensor$ ls
sensor1  sensor2

センサが二つになっているのが分かる。

タッチセンサの時と同様に、中身を見てみる。

robot@ev3dev:/sys/class/lego-sensor$ cd sensor2
robot@ev3dev:/sys/class/lego-sensor/sensor2$ ls
address          decimals     mode        subsystem   value1  value6
bin_data         device       modes       text_value  value2  value7
bin_data_format  direct       num_values  uevent      value3
command          driver_name  poll_ms     units       value4
commands         fw_version   power       value0      value5

ディレクトリの構造自体はタッチセンサと同じである。 以下、address, mode, modes の値を表示してみる。

robot@ev3dev:/sys/class/lego-sensor/sensor2$ cat address
in2
robot@ev3dev:/sys/class/lego-sensor/sensor2$ cat mode
COL-REFLECT
robot@ev3dev:/sys/class/lego-sensor/sensor2$ cat modes
COL-REFLECT COL-AMBIENT COL-COLOR REF-RAW RGB-RAW COL-CAL
robot@ev3dev:/sys/class/lego-sensor/sensor2$ cat num_values
1

上のように、デフォルトのモードは COL-REFLECT になっている。

このモードでは赤い光を発してその反射で明るさを測定している。 値はパーセント表示で 0〜100までの値を取る。

robot@ev3dev:/sys/class/lego-sensor/sensor2$ cat value0
1

白い紙の上にカラーセンサを当てて値を読んでみる。

robot@ev3dev:/sys/class/lego-sensor/sensor2$ cat value0
76

紙からの距離によって値は代わるが、70〜80辺りの値が得られるはずである。

モードを REF-RAW に変更して値を読んでみよう。

robot@ev3dev:/sys/class/lego-sensor/sensor2$ echo REF-RAW > mode
robot@ev3dev:/sys/class/lego-sensor/sensor2$ cat num_values
2
robot@ev3dev:/sys/class/lego-sensor/sensor2$ cat value0 value1
373
703
robot@ev3dev:/sys/class/lego-sensor/sensor2$ cat value0 value1
634
662

これらの「生の」値が何を示しているのか仕様書からは正確に分からなかったが、 白い部分を測ると value0とvalue1はそれぞれ200と700くらいになり、 黒い部分を測るとそれぞれ600代半ばの値になる。

モードを RGB-RAW にすれば、色のRGBの値が得られる。

robot@ev3dev:/sys/class/lego-sensor/sensor2$ echo RGB-RAW > mode
robot@ev3dev:/sys/class/lego-sensor/sensor2$ cat num_values
3
robot@ev3dev:/sys/class/lego-sensor/sensor2$ cat value0 value1 value2
394
413
249

赤い部分を測定すると value0 が value1 と value2 に対して大きくなる。 いろいろな色を測ってみよう。

モードを COL-COLOR にすれば、7つの色を判別する。

robot@ev3dev:/sys/class/lego-sensor/sensor2$ echo COL-COLOR > mode
robot@ev3dev:/sys/class/lego-sensor/sensor2$ cat num_values
1
robot@ev3dev:/sys/class/lego-sensor/sensor2$ cat value0
6

得られる数字は次の色を表す。

0 none
1 black
2 blue
3 green
4 yellow
5 red
6 white
7 brown

超音波センサ

同様に超音波センサの値を読んでみる。 以下の例ではポート1に接続。

robot@ev3dev:/sys/class/lego-sensor/sensor2$ cd ..
robot@ev3dev:/sys/class/lego-sensor ls
sensor1  sensor2  sensor3
robot@ev3dev:/sys/class/lego-sensor$ cd sensor3
robot@ev3dev:/sys/class/lego-sensor/sensor3$ ls
address          decimals     mode        subsystem   value1  value6
bin_data         device       modes       text_value  value2  value7
bin_data_format  direct       num_values  uevent      value3
command          driver_name  poll_ms     units       value4
commands         fw_version   power       value0      value5

ポートとモードを確認。

robot@ev3dev:/sys/class/lego-sensor/sensor3$ cat address
in1
robot@ev3dev:/sys/class/lego-sensor/sensor3$ cat mode
US-DIST-CM
robot@ev3dev:/sys/class/lego-sensor/sensor3$ cat modes 
US-DIST-CM US-DIST-IN US-LISTEN US-SI-CM US-SI-IN US-DC-CM US-DC-IN

デフォルトの US-DIST-CM は、連続的に距離を測り mm の単位で値が得られる。 それに対し、US-SI-CM は一度きり(single)の測定。 また US-DIST-IN と US-SI-IN は、距離を0.1インチ(2.54mm)の単位で得る。 US-LISTEN にすると距離は測らず赤いLEDを点滅させる。 US-DC-CM と US-DC-IN は US-DIST-CM と US-DIST-IN と同じように連続して距離を測定しているが、違いはよくわからない。

robot@ev3dev:/sys/class/lego-sensor/sensor3$ cat value0
150

この場合、対象物までの距離は15cm。また255cm以上離れた場合は常に2550という値になる。

ジャイロセンサ

ジャイロセンサは 以下の例ではポート4に接続。

robot@ev3dev:/sys/class/lego-sensor/sensor3$ cd ..
robot@ev3dev:/sys/class/lego-sensor ls
sensor1  sensor2  sensor3 sensor4
robot@ev3dev:/sys/class/lego-sensor$ cd sensor4
robot@ev3dev:/sys/class/lego-sensor/sensor3$ ls
address          decimals     mode        subsystem   value1  value6
bin_data         device       modes       text_value  value2  value7
bin_data_format  direct       num_values  uevent      value3
command          driver_name  poll_ms     units       value4
commands         fw_version   power       value0      value5

ポートとモードを確認。

robot@ev3dev:/sys/class/lego-sensor/sensor4$ cat address
in4
robot@ev3dev:/sys/class/lego-sensor/sensor4$ cat mode
GYRO-ANG
robot@ev3dev:/sys/class/lego-sensor/sensor4$ cat modes 
GYRO-ANG GYRO-RATE GYRO-FAS GYRO-G&A GYRO-CAL

デフォルトのモードでは現在の角度がvalue0で得られる。

robot@ev3dev:/sys/class/lego-sensor/sensor4$ cat value0
62

少し角度を変えて測定してみる。

モードを GYRO-RATE にすると角速度(度/秒)が得られる。

robot@ev3dev:/sys/class/lego-sensor/sensor4$ echo GYRO-RATE > mode
robot@ev3dev:/sys/class/lego-sensor/sensor4$ cat value0
0

動かしていない場合や、平行移動している場合は 0 となる。

1秒間で半周くらいのスピードで回転させると180くらいの値になる。

robot@ev3dev:/sys/class/lego-sensor/sensor4$ cat value0
192

もちろん回転させている途中で測る必要がある。

モードを GYRO-G&A にすると現在の角度と角速度が同時に得られる。

robot@ev3dev:/sys/class/lego-sensor/sensor4$ echo GYRO-G\&A > mode
robot@ev3dev:/sys/class/lego-sensor/sensor4$ cat value0 value1
34
0

シェルのコマンドで & を入力する場合には、\& と入力しなければならないことに注意する (シェルで &マークは、コマンドをバックグランドで実行する、という意味になるので 通常の文字として扱うためには前に \ が必要。)

pythonスクリプトでセンサを使う

python-ev3 ではセンサー各種のクラスが用意されている。

タッチセンサ

次の例は、ポート3にタッチセンサを接続して、タッチセンサの値を1秒おきに出力するスクリプト。 センサの値は value() というメソッドで取得できる。

#!/usr/bin/env python3
from ev3dev.ev3 import *
from time import sleep

ts = TouchSensor('in3')     # 接続ポートを指定してインスタンスを作る
  
if __name__ == '__main__':

    while True:
        print(ts.value())   # value()で値が取得できる 
        sleep(1)

この例では while True: の無限ループがあるのでプログラムがいつまでたっても終了しない。 強制的に終了するには、Contol + c を押す (コントロールキーを押しながら c を押す)。

カラーセンサ

次にポートに接続したカラーセンサの値を表示してみる。

#!/usr/bin/env python3
from ev3dev.ev3 import *
from time import sleep

ts = TouchSensor('in3')
cs = ColorSensor('in2')       # カラーセンサのインスタンスを作る
cs.mode = 'COL-REFLECT'       # modeを'COL-REFLECT'に設定
  
if __name__ == '__main__':

    while ts.value() == 0:    # ts.value()が0の間だけ繰り返し
        print (cs.value())
        sleep(1)

この例ではタッチセンサを停止ボタンとして使用し、タッチセンサが押されていれば while ループから抜けるようにしている。こうしておけば Control + c でプログラムを強制終了しなくてよい。

またこの例ではセンサのモードが'COL-REFLECT'なので値は一つだけであるが、 モードを 'RGB-RAW' にすると red,green,blue の3つの値が取得できる。 この場合、value(0), value(1), value(2) でそれぞれの値が取得できる。

#!/usr/bin/env python3
from ev3dev.ev3 import *
from time import sleep

ts = TouchSensor('in3')
cs = ColorSensor('in2')
cs.mode = 'RGB-RAW'              # センサのモードを指定
  
if __name__ == '__main__':

   while ts.value() == 0:
        print (cs.value(0),cs.value(1),cs.value(2))
        sleep(1)

超音波センサ

【練習問題】 超音波センサのクラスは UntrasonicSensor() で与えられる。 上の例を参考に、一秒おきに超音波センサの値を表示するプログラムを作成しなさい。

超音波センサを使って、前にある物体との距離を20cmに保つロボットを作ってみる。 具体的には、実際の距離を測り、その距離が20cmからずれていれば、 そのずれに比例したスピードで前進または後進するようにする。 ただし、スピードの範囲は -700 〜 700 となるようにする。 つまり、 距離が10cm以下の時に最大のスピード700で後進、 距離が30cm以上の時に最大のスピード700で前進、となるように設定する。

#!/usr/bin/env python3
from ev3dev.ev3 import *
from time import sleep

mL = LargeMotor('outA')
mR = LargeMotor('outB')
ts = TouchSensor('in3')
us = UltrasonicSensor('in1')

if __name__ == '__main__':

    mL.reset()
    mR.reset()

    while ts.value() == 0:

        sp = (us.value() - 200) * 7        # 距離からスピードを計算してspに代入 (前進は正値、後進は負値)
        sp =  700 if sp >  700 else sp  # spの値が700を超えたらspを700に設定
        sp = -700 if sp < -700 else sp  # spの値が-700より小さい時はspを-700に設定

        mL.run_forever(speed_sp=sp)
        mR.run_forever(speed_sp=sp)
        sleep(0.001)

    mL.stop(stop_action="coast")          # whileループを抜けた後、モータを止める
    mR.stop(stop_action="coast")

この例では sp の上限と下限を設定するために次のような条件演算子を使った。

  x = a if cond else b    # 条件condが成り立つ時には x に a を代入、そうでない場合は bを代入

通常のブロック形式では次のようになる。

        if sp > 700:
            sp = 700
        if sp < -700:
            sp = -700

ジャイロセンサ

【練習問題】 ジャイロセンサのクラスは GyroSensor() で与えられる。 上の例を参考に、一秒おきに超音波センサの値を表示するプログラムを作成しなさい。

【練習問題】 一定の半径で円を描いて動くロボットを作り、ちょうど1周したことをジャイロセンサで検知して停止するロボットを作りなさい。