[ev3dev×Python]   カラーセンサ

この記事はPythonでev3を操作してみたい人のための記事です。
今回はカラーセンサを使っていろいろな操作をしていきたいと思います。


#目次
0 . 用意するもの
1 . カラーセンサのプログラム





#0.用意するもの
◯ ev3(タンク) とカラーセンサ、タッチセンサ
◯ パソコン(VSCode)
◯ bluetooth
◯ microSD
◯ [資料](https://buildmedia.readthedocs.org/media/pdf/python-ev3dev/latest/python-ev3dev.pdf)(これをみながら進めていくのがオススメです。)



#1.カラーセンサのプログラム(資料p.33)







###1-0 . カラーセンサが色を検出したら〜するプログラム 

○カラーセンサが赤に反応したら"赤が検出されました"と発声するプログラムです。

```colorsensor00.py
#!/usr/bin/env python3
from ev3dev2.sensor.lego import ColorSensor
from ev3dev2.sound import Sound

#インスタンスの生成
cs = ColorSensor()
spkr = Sound()

#無限ループ
while True:
    #オブジェクトcsが持つフィールド(変数)colorにアクセスする
    #変数の値が5ならば
    if cs.color == 5:
        #オブジェクトspkrが持つメソッド(関数)speak()にアクセスする
        spkr.speak('Red is detected')
```

**Point** : 色はあらかじめ数字と対応する形で定義されていて、プログラム内で色は数字として扱われます。

color
Color detected by the sensor, categorized by overall value.
• 0: No color
• 1: Black
• 2: Blue
• 3: Green
• 4: Yellow
• 5: Red
• 6: White
• 7: Brown




###1-1 . ライントレースのプログラム①

○カラーセンサが検知した光の反射率によって場合分けして、ラインに沿って走るプログラムです。

```colorsensor01.py
#!/usr/bin/env python3
from ev3dev2.motor import MoveTank,OUTPUT_A,OUTPUT_B
from ev3dev2.sensor.lego import ColorSensor

#インスタンスの生成
tank_drive = MoveTank(OUTPUT_A,OUTPUT_B)
cs = ColorSensor()

#無限ループ
while True:
    #オブジェクトcsが持つフィールド(変数)reflected_light_intensityにドットを使ってアクセスする
    #もし、反射光の値が15未満ならば、
    if cs.reflected_light_intensity < 15:
        tank_drive.on(10,0)
    else:
        tank_drive.on(0,10)
```

**Point** : ライントレース
黒と白では光の反射率が異なります。
白は光をよく反射して、黒は光をあまり反射しません。
反射率が異なることを利用して、白いフィールド上で黒のラインを検出しながら走るのが一般的なライントレースです。





###1-2 . ライントレースのプログラム②

○カラーセンサを2個使ったライントレースのプログラムです

```colorsensor02.py
#!/usr/bin/env python3
from ev3dev2.motor import MoveTank,OUTPUT_A,OUTPUT_B
from ev3dev2.sensor.lego import ColorSensor
from ev3dev2.sensor import INPUT_1,INPUT_2

#インスタンスの生成
#カラーセンサを2つ使う
tank_drive = MoveTank(OUTPUT_A,OUTPUT_B)
cs_1 = ColorSensor(INPUT_1)
cs_2 = ColorSensor(INPUT_2)

while True:
    if cs_1.reflected_light_intensity > 15:
        #カラーセンサの反射光の値がどちらも15以上ならば
        if cs_2.reflected_light_intensity > 15:
            tank_drive.on(10,10)
        #カラーセンサ(1)の反射光の値のみが15以上ならば
        else:
            tank_drive.on(10,0)
    else:
        #カラーセンサ(2)の反射光の値のみが15以上ならば
        if cs_2.reflected_light_intensity > 15:
            tank_drive.on(0,10)
        #カラーセンサの反射光の値がどちらも15以下ならば
        else:
            tank_drive.on(-10,10)
```


**Point** : プログラムのイメージ

cs_1 が白を検出 and cs_2 が白を検出: 直進 
cs_1 が白を検出 and cs_2 が黒を検出: 右へ進む 
cs_1 が黒を検出 and cs_2 が白を検出: 左へ進む 
cs_1 が黒を検出 and cs_2 が黒を検出: その場で回転(ラインを探索)




###1-3 . ライントレースのプログラム③

○PID制御を利用したライントレースのプログラムです。

```colorsensor03.py
#!/usr/bin/env python3
from ev3dev2.motor import OUTPUT_A, OUTPUT_B, MoveTank, SpeedPercent, follow_for_ms
from ev3dev2.sensor.lego import ColorSensor

#インスタンスの生成
tank = MoveTank(OUTPUT_A, OUTPUT_B)
tank.cs = ColorSensor()

#例外処理を行うtry-exceptを使う
try:
    #follow_line()関数のルールに従って引数を定義していく
    tank.follow_line(
    kp=11.3, ki=0.05, kd=3.2,
    speed=SpeedPercent(30),
    follow_for=follow_for_ms,
    ms=4500
    )
#例外があった場合にそれをキャッチする
except LineFollowErrorTooFast:
    tank.stop()
    #例外が発生したかどうかを判定したいだけで、その例外を処理するつもりがなければ
    #単純な形式の raise 文を使って例外を再送出させることができる
    raise

```

**Point** : PID制御
PID制御は、出力値が目標値に近づくように、入力値を制御する、フィードバック制御の一種

出力値 : 光の反射率
目標値 : ライン際での光の反射率
入力値 : モーターの回転スピード
(カラーセンサ1つでライントレースする場合)

**Point** : follow_line(kp, ki, kd, speed, target_light_intensity=None, follow_left_edge=True, white=60,off_line_count_max=20, sleep_time=0.01, follow_for=<function follow_for_forever>,**kwargs)

PID line follower
kp, ki, and kd は PID定数です。

**Point** : PID制御についての参考記事

[ライントレースカーをPID制御する](https://stemship.com/arduino-jissen-pid/)

**Point** : pythonの例外処理についての参考記事

[Pythonの例外処理（try, except, else, finally）](https://note.nkmk.me/python-try-except-else-finally/)





###1-4 . リストと色を照合するプログラム

○タッチセンサを押しながら色をかざします。その時にかざした色がcolor_listに存在する色ならば、*"~色が検出されました"*と表示されます。
かざした色がcolor_listに存在しなければ*"その色はカラーリストに存在しません"*と表示されます。

```colorsensor04.py
#!/usr/bin/env python3
from ev3dev2.sensor.lego import ColorSensor,TouchSensor
from ev3dev2.display import Display

#インスタンスの生成
cs = ColorSensor()
ts = TouchSensor()
dsp = Display()
color_list = [1,2,4,5]

while True:
    dsp.update()
    #もしタッチセンサが押された時
    if ts.is_pressed:
        #検知した色が、事前に用意したリスト(color_list)に含まれているならば
        if cs.color in color_list:
            #~色が検知されましたと表示する
            dsp.text_pixels(cs.color_name  + ' is detected',True,0,52,font = 'helvB' + '12')
        #検知した色がリスト内にない色ならば、
        else:
            #そのような色はリストの中にありませんと表示する
            dsp.text_pixels('No such color in the color_list',True,0,52,font = 'helvB' + '10')
    #タッチセンサを押していないならば
    else:
        #色をセンサにかざしてくださいと表示する
        dsp.text_pixels('please set the color !!!!',True,0,52,font = 'helvB' + '12') 
```

**Point** : 
```if a in b:
　　処理```


**要素aがb(今回はリスト)に含まれるならば、処理を実行する**というプログラム

参考記事
[Pythonのin演算子でリストなどに特定の要素が含まれるか判定]
(https://note.nkmk.me/python-in-basic/)








###1-5 . 色をリストに保存していくプログラム

○空の`color_list`に、タッチセンサを押した時にかざしていた色を追加していきます。
追加し終わったらリストの先頭から順番に、色に紐づけられた処理を実行していきます。

```colorsensor05.py
#!/usr/bin/env python3
from ev3dev2.motor import OUTPUT_A,OUTPUT_B,MoveTank
from ev3dev2.sensor.lego import ColorSensor,TouchSensor
from ev3dev2.sound import Sound
from time import sleep

#インスタンスの生成
cs = ColorSensor()
ts = TouchSensor()
spkr = Sound()
tank_drive = MoveTank(OUTPUT_A,OUTPUT_B)
color_list = []

#4回繰り返す
for i in range(4):
    #タッチセンサが押されるまで待機する
    ts.wait_for_bump()
    sleep(0.1)
    #カラーセンサにかざされている色を、リストに追加する
    color_list.append(cs.color)
    #検知した色を確認するために、その色の名前を発声させる
    spkr.speak(cs.color_name)

#リストにある要素を1つずつ取り出していく
for c in color_list:
    #命令の実行間隔を調整する
    sleep(1)
    #もし、検知した色が黒ならば、
    if c == 1:
        #音声とともに左に方向転換する
        spkr.speak('turn left')
        tank_drive.on_for_rotations(0,50,2)
    #もし、検知した色が青ならば、
    if c == 2:
        #音声とともに前進する
        spkr.speak('go forward')
        tank_drive.on_for_rotations(50,50,2)
    #もし、検知した色が黄ならば、
    if c == 4:
        #音声とともに右に方向転換する
        spkr.speak('turn right')
        tank_drive.on_for_rotations(50,0,2)
    #もし、検知した色が赤ならば、
    if c == 5:
        #音声とともに後進する
        spkr.speak('go back')
        tank_drive.on_for_rotations(-10,-10,2)
```



**Point** : append()
リストの末尾に要素を追加できる 

参考記事
[Pythonでリスト（配列）に要素を追加するappend, extend, insert](https://note.nkmk.me/python-list-append-extend-insert/)










#最後に

読んで頂きありがとうございました！！
次回は超音波センサについて書いていきたいと思います！

より良い記事にしていきたいので
　◯こうした方がわかりやすい
　◯ここがわかりにくい
　◯ここが間違っている
　◯ここをもっと説明して欲しい
などの御意見、御指摘のほどよろしくお願い致します。