[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.36)







###1-0 . 指定した角度だけ方向転換するプログラム①

○直進して90度方向転換することを4回繰り返すプログラムです。

```gyrosensor00.py
#!/usr/bin/env python3
from ev3dev2.sensor.lego import GyroSensor
from ev3dev2.display import Display
from time import sleep

#インスタンスの生成
dsp = Display()
gy = GyroSensor()

#4回繰り返す
for i in range(4):
    #2回転直進する
    tank_drive.on_for_rotations(10,10,2)
    #ジャイロセンサの値を0にする
    gy.reset()
    #ジャイロセンサの値が90度変化するまで方向転換し続ける。
    tank_drive.on(5,0)
    gy.wait_until_angle_changed_by(90)
exit()
```

**Point** : wait_until_angle_changed_by(delta,direction_sensitive=False)

指定した角度分変化するまで待機する関数。
第一引数には変化の幅(変位)を入力する。
direction_sensitiveは、回転の方向を区別するか否かを決められる。

例えば、delta = -90にした場合、
direction_sensitive = Trueならば、ジャイロセンサの値が-90になるまで待機する。
(回転の方向を区別する)
direction_sensitive = Falseならば、ジャイロセンサの値が90もしくは-90になるまで待機する。
(回転の方向を区別しない)







###1-1 . 指定した角度だけ方向転換するプログラム②

○30度方向転換するプログラムです。

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

# インスタンスの生成
tank = MoveTank(OUTPUT_A, OUTPUT_B)
#ジャイロセンサのインスタンスをクラスMoveTankの中の変数gyroに代入する
tank.gyro = GyroSensor()
# 現在のジャイロセンサの値を0に設定する
tank.gyro.calibrate()

# 30度回転する
tank.turn_degrees(
speed=SpeedPercent(5),
target_angle=30
)
```


**Point** : turn_degrees(speed, target_angle, brake=True, error_margin=2, sleep_time=0.01)
→タンク専用の関数で、ジャイロセンサを使って、その場で指定された角度方向転換するまで待機する。
error_margin は、target_angleと実際の方向転換との誤差をどのくらい許容するかを決められる。








###1-2 . 角度を表示するプログラム


○ジャイロセンサの値を表示するプログラムです。

```gyrosensor02.py
#!/usr/bin/env python3
from ev3dev2.display import Display
from ev3dev2.sensor.lego import GyroSensor
from time import sleep

dsp = Display()
gy = GyroSensor()

#無限ループ
while True:
    dsp.update()
    #常にジャイロセンサの値を取得したいので
    #変数 ang はループの中でつくる。
    ang = gy.angle
    #ジャイロセンサの角度を文字に変換して、単位と一緒に表示する
    dsp.text_pixels(str(ang) + 'cm',True,0,52,font = 'helvB' + '24')
    #処理を軽くするために間隔を空けて文字を取得する
    sleep(0.1)
```

**Point** : angle
ジャイロセンサの数値を格納している変数。










###1-3 . 並列処理のプログラム


○直進して90度方向転換することを4回繰り返すプログラムです。
そして、走行と同時にジャイロセンサの値を表示します。

```gyrosensor03.py
#!/usr/bin/env python3
from ev3dev2.motor import OUTPUT_A,OUTPUT_B,MoveTank
from ev3dev2.display import Display
from ev3dev2.sensor.lego import GyroSensor
from time import sleep
from threading import Thread

#インスタンスの生成
tank_drive = MoveTank(OUTPUT_A,OUTPUT_B)
dsp = Display()
gy = GyroSensor()

#自分で関数をつくる(定義する)
def angle_display():
    # loop = True の間は繰り返される
    # 角度を表示するプログラム
    while loop:
        dsp.update()
        gy = gy.angle
        dsp.text_pixels(str(angle) + 'cm',True,0,52,font = 'helvB' + '24')
        sleep(0.1)

#loopを真にしておく
loop = True
#クラスThreadのインスタンス t をつくる
#どのスレッドを開始するかを選択する
t = Thread(target = angle_display)
#インスタンスを使ってスレッドを開始する
t.start()

#4回繰り返す
for i in range(4):
    #2回転直進する
    tank_drive.on_for_rotations(10,10,2)
    #ジャイロセンサの値を0にする
    gy.reset()
    #ジャイロセンサの値が90度変化するまで方向転換し続ける。
    tank_drive.on(5,0)
    gy.wait_until_angle_changed_by(90)
tank_drive.stop()
#loop を Falseにすることで、スレッドを終了させる
loop = False
```



**Point** : 並列処理に関する参考記事
[Multithreading](https://sites.google.com/site/ev3python/learn_ev3_python/threads)
[スレッドで実装する](https://qiita.com/tchnkmr/items/b05f321fa315bbce4f77#:~:text=%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%81%A8%E3%81%AF,%E3%82%92%E8%A1%8C%E3%81%86%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%9B%E3%82%93%E3%80%82&text=%E3%81%9D%E3%81%AE%E4%B8%A6%E5%88%97%E5%87%A6%E7%90%86%E3%82%92%E8%A1%8C%E3%81%86,%E3%81%AE%E4%BD%BF%E7%94%A8%E3%81%AB%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82)

**Point** : 関数に関する参考記事
[Pythonで関数を定義・呼び出し（def, return）](https://note.nkmk.me/python-function-def-return/)








###1-4 . 並行処理のプログラム


○直進して90度方向転換することを4回繰り返すプログラムです。
そして、走行と同時にジャイロセンサの値を表示します。

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

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

#新しいプロセスで子のプログラムを実行する
subprocess.Popen(['python3','sub.py'])

for i in range(4):
    #2回転直進する
    tank_drive.on_for_rotations(10,10,2)
    #ジャイロセンサの値を0にする
    gy.reset()
    #ジャイロセンサの値が90度変化するまで方向転換し続ける。
    tank_drive.on(5,0)
    gy.wait_until_angle_changed_by(90)
#Popenクラスのインスタンス proc をつくる
proc = subprocess.Popen(['python3','sub.py'])
#procを使って、子プロセスをkillする
proc.kill()
exit()
```

```sub.py
from ev3dev2.sensor.lego import GyroSensor
from ev3dev2.display import Display
from time import sleep

dsp = Display()
us = GyroSensor()

#無限ループ
while True:
    dsp.update()
    #常にジャイロセンサの値を取得したいので
    #変数 ang はループの中でつくる。
    ang = gy.angle
    #ジャイロセンサの角度を文字に変換して、単位と一緒に表示する
    dsp.text_pixels(str(ang) + 'cm',True,0,52,font = 'helvB' + '24')
    #処理を軽くするために間隔を空けて文字を取得する
    sleep(0.1)
```

**Point** :main.pyからsub.pyを呼び出して並行処理するプログラム。
(処理が重い)
**Point** : 
**subprocess.Popen()**
プロセスを生成するだけで，終了を待たない
→開始して放置
**Popen.terminate()**
子プロセスを停止します。
**Popen.kill()**
子プロセスを kill します。

**Point** : 並行処理に関する参考記事
[Pythonにおける並行処理プログラミング入門](https://myenigma.hatenablog.com/entry/2016/04/09/184215)
[subprocessについてより深く（3系，更新版）]
(https://qiita.com/HidKamiya/items/e192a55371a2961ca8a4#subprocesspopen%E3%81%AB%E3%82%88%E3%82%8B%E8%87%AA%E7%94%B1%E5%BA%A6%E3%81%AE%E9%AB%98%E3%81%84%E5%87%A6%E7%90%86)
[17.5. subprocess — サブプロセス管理(公式)]
(https://docs.python.org/ja/3.5/library/subprocess.html)



###1-5 . PID制御を用いて走行するプログラム

何があっても角度を0にキープしようとしながら走行するプログラムです。
PID制御を使って、ジャイロセンサの値を目標値に近づけようとします。

```gyrosensor05.py
from ev3dev2.motor import OUTPUT_A, OUTPUT_B, MoveTank, SpeedPercent, follow_for_ms
from ev3dev2.sensor.lego import GyroSensor

# インスタンスの生成
tank = MoveTank(OUTPUT_A, OUTPUT_B)
#クラスGyroSensorのインスタンスをクラスMoveTankの中の変数gyroに代入する
tank.gyro = GyroSensor()
# 現在のジャイロセンサの値を0にする
tank.gyro.calibrate()

try:
#4500ms(4.5秒)の間のみ
# ジャイロセンサの値が常に目標値(この場合は0)になるように走行を開始する
    tank.follow_gyro_angle(
    kp=11.3, ki=0.05, kd=3.2,
    speed=SpeedPercent(30),
    target_angle=0,
    follow_for=follow_for_ms,
    ms=4500
    )
#例外が発生した場合にどうするかを決定する
except FollowGyroAngleErrorTooFast:
    tank.stop()
    raise
```


**Point** : follow_gyro_angle(kp, ki, kd, speed, target_angle=0, sleep_time=0.01, follow_for=<function follow_for_forever>, **kwargs)

kp, ki, kd は PID定数.
speedはロボットのスピード
target_angle は維持したい角度

**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/)



#最後に

読んで頂きありがとうございました！！
次回はssh controlについて書いていきたいと思います！

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