#8 超音波・ジャイロ・タッチセンサを使おう【もっと!後輩たちのためのEV3rt講座】

# 目次

| 章 | タイトル | 内容 |
| :-: | :-: | :-: |
| 1 | [はじめに](#1-はじめに) | 今回やることの説明 |
| 2 | [センサの設定](#2-センサの設定) | センサの設定方法について説明 |
| 3 | [超音波センサを使う](#3-超音波センサを使う) | 超音波センサの使い方について説明 |
| 4 | [ジャイロセンサを使う](#4-ジャイロセンサを使う) | ジャイロセンサの使い方について説明 |
| 5 | [タッチセンサを使う](#5-タッチセンサを使う) | タッチセンサの使い方について説明 |
| 6 | [まとめ](#6-まとめ) | 今回のまとめ |

# 1. はじめに

前回センサの使い方第一弾として、カラーセンサ(EV3カラーセンサとHiTechnincカラーセンサ)の使い方を紹介しました。

今回はEV3基本キットに含まれる残りのセンサである「超音波センサ」「ジャイロセンサ」「タッチセンサ」の3種類の使い方を解説していきます。

![sensors.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/16cd8bcf-8403-2928-ca9c-e0feebb8f2bd.png)

前回のカラーセンサの使い方が理解できていれば、今回はすんなり進むかと思います。
是非マスターしていきましょう。

例によって、センサ系のAPIの説明は以下のページに書かれています。
本記事はそれをかみ砕いて、EV3ソフトでの例も交えながら紹介していきます。

[EV3RT C API Reference / 各種センサ](https://www.toppers.jp/ev3pf/EV3RT_C_API_Reference/group__ev3sensor.html)

# 2. センサの設定

EV3インテリジェントブロックのセンサポートの説明、及び接続設定関数`ev3_sensor_config`の説明は既に前回行っているので省略します。
必要に応じて前回の記事👇の2章を見直してみてください。

https://qiita.com/koushiro/items/86c8855c85c5a6658030#2-%E3%82%BB%E3%83%B3%E3%82%B5%E3%81%AE%E8%A8%AD%E5%AE%9A

## センサの定数

さて、前回も説明しましたが、センサの種類を表す定数は以下のように「列挙型」により定義されています。

```c
typedef enum {
    NONE_SENSOR = 0,
    ULTRASONIC_SENSOR,
    GYRO_SENSOR,
    TOUCH_SENSOR,
    COLOR_SENSOR,
    INFRARED_SENSOR,
    HT_NXT_ACCEL_SENSOR,
    HT_NXT_COLOR_SENSOR,
    NXT_TEMP_SENSOR,
    TNUM_SENSOR_TYPE
} sensor_type_t;
```

このうち、今回使用するのは以下の二つです。

| センサタイプ | 定数 | 値 |
| :-: | :-: | :-: |
| 超音波センサ | ULTRASONIC_SENSOR | 1 |
| タッチセンサ | GYRO_SENSOR | 2 |
| タッチセンサ | TOUCH_SENSOR | 3 |

よって、接続設定は以下のように行います。

*Ex 1. 各種センサの接続設定*
```c
void main_task(intptr_t unused)
{
  ev3_sensor_config(EV3_PORT_1, ULTRASONIC_SENSOR);
  ev3_sensor_config(EV3_PORT_2, GYRO_SENSOR);
  ev3_sensor_config(EV3_PORT_3, TOUCH_SENSOR);
}
```

(ここでは、ポート1に超音波センサ、ポート2にジャイロセンサ、ポート3にタッチセンサを接続したものとします。)

# 3. 超音波センサを使う

超音波センサは、センサから発信した超音波を対象物に当て、跳ね返ってくるまでの時間を計測して距離を算出するセンサです。

![layout2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/88f6be85-bc81-dae2-ba85-feeb46497a9b.png)

出典: [アフレル / EV3超音波センサーの使用例](https://afrel.co.jp/technology-info/information-ev3/ev3-hardware/ev3-ultrasonic-sensor/15765/)

使い方としては、「対象物までの距離の算出」と「対象物が存在するかの判定」の2つが用意されています。

## 対象物までの距離の算出

はじめに、測定対象物までの距離を算出する関数を紹介します。

EV3ソフトでは以下👇のブロックにあたります。

![ultra_dis.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/34339e73-1742-8f8c-e3f5-798434b4a66c.png)

```c
int16_t ev3_ultrasonic_sensor_get_distance(sensor_port_t port);
```

関数名は`ev3_ultrasonic_sensor_get_distance`で、引数として **「ポート」** を指定します。
すると、戻り値として **「対象物までの距離(単位はセンチメートル)」** が返ってきます。

簡単な使用例も示しておきましょう。

*Ex 2. 超音波センサで測った距離を変数に格納する*

```c
void main_task(intptr_t unused)
{
  int distance;

  ev3_sensor_config(EV3_PORT_1, ULTRASONIC_SENSOR);

  distance = ev3_ultrasonic_sensor_get_distance(EV3_PORT_1);
}
```

とても簡単に使えるので良いですね。

## 対象物が存在するかの判定

続いて、測定対象物が存在するか判定する関数を紹介します。
EV3ソフトで言うところの、以下👇の「存在」モードにあたります。

![ultra_is.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/e01b0d1f-0c9c-7065-48da-2520d2846f3f.png)

```c
bool_t ev3_ultrasonic_sensor_listen(sensor_port_t port);
```

関数名は`ev3_ultrasonic_sensor_listen`で、引数として **「ポート」** を指定します。
戻り値として、対象物があれば`True`、無ければ`False`が返ってきます。

例えば、以下のように対象物があるかどうかで条件分岐を行うことが出来ます。

*Ex 3. 測定対象物があるかの条件分岐*

```c
void main_task(intptr_t unused)
{
  ev3_sensor_config(EV3_PORT_1, ULTRASONIC_SENSOR);

  if(ev3_ultrasonic_sensor_listen(EV3_PORT_1)){
    ev3_lcd_draw_string("Detect an object !!", 10, 10);
  }
  else{
    ev3_lcd_draw_string("No object.", 10, 10);
  }
}
```

対象物があれば「オブジェクトを検知した」ことをインテリジェントブロックのLCDに表示します。
なかった場合は「オブジェクトがない」と表示します。

こちらの関数もとても簡単に使えるので、有用かと思います。
ただし、どれくらいの距離で検知するのかについては、事前に調査が必要かと思いますので、注意して使いましょう。

# 4. ジャイロセンサを使う

ジャイロセンサは、一つの軸における回転角を取得するセンサです。
下の図のように、赤丸を軸として矢印の方向の回転を検知します。
時計回り方向が正、反時計回り方向が負の値となります。

![lyout.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/705bb854-7ceb-fa1a-4211-a8dd03528e2f.png)

出典: [アフレル / EV3ジャイロセンサーの使用例](https://afrel.co.jp/technology-info/information-ev3/ev3-hardware/ev3-gyro-sensor/15639/)

ジャイロセンサでは「角度(角位置)の測定」と「角速度の測定」が出来ます。
又、角度には基準が必要です。そのため、角度を`0`にリセットすることも出来ます。
これらを順番に説明していきます。

## 角度(角位置)のリセット

はじめに、角度(角位置)のリセットについてです。
角度の算出は、初めに決めた基準をもとに算出されます。
ジャイロセンサがインテリジェントブロックに接続されたタイミングで最初のリセットは行われますが、任意のタイミングでリセットを行いたい場合は次の関数を実行することで値を`0`にすることが出来ます。

EV3ソフトでは以下👇のブロックにあたります。

![gyro_reset.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/5ef19844-580b-1674-4301-945ac8a6b36a.png)

```c
ER ev3_gyro_sensor_reset(sensor_port_t port);
```

関数名は`ev3_gyro_sensor_reset`で、ジャイロセンサの角度(角位置)を`0`にリセットする関数です。
引数として **「ポート」** を指定します。

この関数と次に紹介する角度測定関数を組み合わせることで、ロボットがどれだけ回転したかを測定することが出来るようになります。

## 角度(角位置)の測定

次に、角度(角位置)の測定方法です。

EV3ソフトでは以下👇のブロックにあたります。

![gyro_angle.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/603773ef-2632-a7da-ad07-a06152d577ba.png)

```c
int16_t ev3_gyro_sensor_get_angle(sensor_port_t port);
```

関数名は`ev3_gyro_sensor_get_angle`で、ジャイロセンサで角度(角位置)を得る関数です。
引数として **「ポート」** を指定すると、戻り値として **「角度(角位置)」(整数値)** が返ってきます。

では、この関数を用いてロボットを回転させる例を示します。

*Ex 4. ジャイロセンサを使って、ロボットを90度回転させる*

```c
void main_task(intptr_t unused)
{
  ev3_motor_config(EV3_PORT_A, LARGE_MOTOR);
  ev3_motor_config(EV3_PORT_D, LARGE_MOTOR);

  ev3_sensor_config(EV3_PORT_2, GYRO_SENSOR);

  ev3_gyro_sensor_reset(EV3_PORT_2);

  while(ev3_gyro_sensor_get_angle(EV3_PORT_2)<90){
    ev3_motor_set_power(EV3_PORT_A, 50);
    ev3_motor_set_power(EV3_PORT_D, -50);
  }

  ev3_motor_stop(EV3_PORT_A, true);
  ev3_motor_stop(EV3_PORT_D, true);
}
```

初めに角度のリセットを行い、その後は「角度が90度より小さいか」という条件判定のもとwhileループを回します。
ループ中ではロボットが回転するようにモータパワーを与えます。
ロボットが90度以上回転するとループを抜け、モータは停止します。

## 角速度の測定

最後に角速度の測定方法を説明します。

EV3ソフトでは以下👇のブロックにあたります。

![gyro_omega.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/5f2c5abc-c639-44d1-d897-eab0e9534377.png)

```c
int16_t ev3_gyro_sensor_get_rate(sensor_port_t port);
```

関数名は`ev3_gyro_sensor_get_rate`で、ジャイロセンサで角速度を得る関数です。
引数として **「ポート」** を指定すると、戻り値として **「角速度」(整数値)** が返ってきます。単位は「度/秒」です。

使い方ですが、回転を検知する例を示します。

*Ex 5. ジャイロセンサを使って回転を検知する*

```c
#include "ev3api.h"
#include "app.h"
#include <stdio.h>
#include <stdlib.h> //abs関数を使用するために必要

void main_task(intptr_t unused)
{
  ev3_sensor_config(EV3_PORT_2, GYRO_SENSOR);

  if(abs(ev3_gyro_sensor_get_rate(EV3_PORT_2))>=5){
    ev3_lcd_draw_string("Rotate!!", 10, 10);
  }
  else{
    ev3_lcd_draw_string("Not Rotate", 10, 10);
  }
}
```

上記の例は角速度を取得し、「1秒間に5度」以上の割合で回転していた場合、回転を検知してLCDに「Rotate」と表示します。
回転していないと判断した場合も、LCDに「Not Rotate」と表示します。

尚、if分の条件式に`abs`という関数を用いていますが、これは値の絶対値を返すものです。
回転の方向に関わらず回転を検知したいので、絶対値関数により角速度を正方向に補正しています。

実用例についてですが、ジャイロセンサには有名な不具合として「ジャイロドリフト」という症状があります。
これは、実際には回転していないにも関わらず、角度の値が一定の割合で増え続ける不具合です。
(詳しくはご自身でお調べ下さい)

このジャイロドリフトの検知に、角速度の取得は有用かもしれませんね。

# 5. タッチセンサを使う

タッチセンサはその名の通り、センサが押されているか否かを判定するものです。
使い方も非常に簡単なので、さらっと説明してしまいます。

EV3ソフトでは以下👇のブロックにあたります。

![touch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/45b048d5-cff7-fd93-ebba-cf7440f802d9.png)

```c
bool_t ev3_touch_sensor_is_pressed(sensor_port_t port);
```

関数名は`ev3_touch_sensor_is_pressed`で、タッチセンサが押されているかどうかを判定する関数です。
引数として **「ポート」** を指定すると、戻り値として押されていれば`True`が、押されていなければ`False`が返ってきます。

では、その判定方法の例を示します。

*Ex 6. タッチセンサが押されているかの条件分岐*

```c
void main_task(intptr_t unused)
{
  ev3_sensor_config(EV3_PORT_3, TOUCH_SENSOR);

  if(ev3_touch_sensor_is_pressed(EV3_PORT_3)){
    ev3_lcd_draw_string("Pressed !!", 10, 10);
  }
  else{
    ev3_lcd_draw_string("Not Pressed.", 10, 10);
  }
}
```

# 6. まとめ

今回は超音波、ジャイロ、タッチセンサの使い方について説明していきました。
前回紹介したカラーセンサと併せてロボコンで良く使うセンサかと思いますので、しっかり使えるようにしていきましょう。

これにてセンサの紹介を終わり、次回はインテリジェントブロックの機能について紹介します。
具体的には「バッテリ」「ボタン」「LCD」「LEDライト」「スピーカ」の使い方です。
これらも使えるようになると、ロボットの状態を取得したり、あるいは条件判定の結果を表示したりと、よりプログラムの開発が進むかと思いますので、マスターしていきましょう。

尚、本記事では「IRセンサ(赤外線センサ)」「HiTechninc加速度センサ」「NXT温度センサ」については取り上げません。
理由としては、これらのセンサは基本・拡張キットに含まれておらず、WROをはじめとしたロボコンにおいて需要がほぼ無いこと、もう少し言えば私の手元にもありませんので動作確認が出来ない、などがあります。
悪しからずご了承ください。

前回: [#7 カラーセンサを使おう](https://qiita.com/koushiro/items/86c8855c85c5a6658030)
次回: [#9 インテリジェントブロックの機能を使おう](https://qiita.com/koushiro/items/2e10eeee3fdf39866a00)