#6 モータを動かそう【もっと!後輩たちのためのEV3rt講座】

# 目次

| 章 | タイトル | 内容 |
| :-: | :-: | :-: |
| 1 | [はじめに](#1-はじめに) | 今回やることの説明 |
| 2 | [モータの設定](#2-モータの設定) | モータの設定方法について説明 |
| 3 | [モータを動かす系の関数](#3-モータを動かす系の関数) | モータを動かす系の関数を紹介 |
| 4 | [回転角度に関わる関数](#4-回転角度に関わる関数) | 回転角度に関わる関数を紹介 |
| 5 | [モータ情報取得系関数](#5-モータ情報取得系関数) | モータ情報取得系関数を紹介 |
| 6 | [まとめ](#6-まとめ) | 今回のまとめ |

# 1. はじめに

前回まで2回に渡り、EV3rtでのプログラム開発に必要なC言語の文法を一通りおさえていきました。
今回からはいよいよ、ロボットを動かす関数(API)等を紹介していきます。

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

[EV3RT C API Reference / サーボモータ](https://www.toppers.jp/ev3pf/EV3RT_C_API_Reference/group__ev3motor.html#ga25f485fbe38e4234b29e4f1e6a87bd3e)

# 2. モータの設定

モータを動かす前に、「どのポート」に「どのモータ」が接続されているかを設定する必要があります。
まずはこの方法から説明していきます。

## EV3のモータポートについて

EV3インテリジェントブロックのモータ用のポートは、`A`~`D`までの4つがあります。

![IMG_7146.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/0134a991-3241-0976-ba23-ea00304f6375.jpeg)

EV3rtには、これら4つのポートを指定するための定数が定義されています。

```c
typedef enum {
    EV3_PORT_A = 0,
    EV3_PORT_B = 1,
    EV3_PORT_C = 2,
    EV3_PORT_D = 3,
    TNUM_MOTOR_PORT = 4
} motor_port_t;
```

| ポート | 定数 | 値 |
| :-: | :-: | :-: |
| A | EV3_PORT_A | 0 |
| B | EV3_PORT_B | 1 |
| C | EV3_PORT_C | 2 |
| D | EV3_PORT_D | 3 |

上記の定数は「列挙型」というデータ型で定義されています。
が、細かいことは気にせず、ポートAを使用したければ`EV3_PORT_A`と書けばOKです。

## モータの種類について

EV3のモータは「Lモータ」と「Mモータ」の2種類があります。

![motor_type.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/b88d15b5-69c2-ab9b-d59f-9787fbd8a286.png)

こちらも、モータの種類を指定する定数が定義されています。

```c
typedef enum {
    NONE_MOTOR = 0,
    MEDIUM_MOTOR,
    LARGE_MOTOR,
    UNREGULATED_MOTOR,
    TNUM_MOTOR_TYPE
} motor_type_t;
```

| モータタイプ | 定数 | 値 |
| :-: | :-: | :-: |
| 未接続 | NONE_MOTOR | 0 |
| Mモータ | MEDIUM_MOTOR | 1 |
| Lモータ | LARGE_MOTOR | 2 |
| 未調整のモータ | UNREGULATED_MOTOR | 3 |

こちらも「列挙型」により定義されていますが、Mモータを使用するときは`MEDIUM_MOTOR`と、Lモータを使用するときは`LARGE_MOTOR`と書けばOKです。

## モータの接続設定関数

では本題のモータを設定する関数についてです。
モータ設定関数は以下のように定義されています。

```c
ER ev3_motor_config(motor_port_t port, motor_type_t type);
```

まず、関数の名前は`ev3_motor_config`です。
引数として、 **「ポート」** と **「モータタイプ」** の2つを指定します。

戻り値のデータ型は「ER」と書かれていますが、これはエラーコードを返すものです。
(実際のロボットプログラムではほぼ使用しないかと思います。以降、ER型の戻り値の説明は省略させて頂きます。必要であればご自身でAPIを確認してください。)

実際に使用するときは、以下のように記述します。

*Ex 1. Lモータ2つをポートAとDに接続するとき*

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

*Ex 2. Mモータ1つをポートBに接続するとき*

```c
void main_task(intptr_t unused)
{
  ev3_motor_config(EV3_PORT_B, MEDIUM_MOTOR);
}
```

この設定を行っておかないとモータが動きませんので、`main_task`が始まったらすぐに実行するよう心がけましょう。

# 3. モータを動かす系の関数

では、実際にモータの動作に関わる関数を紹介していきます。

## モータON 関数

まずは、モータをONする関数を紹介します。
EV3ソフトではこの👇ブロックにあたります。

![on.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/64958912-e352-35f5-f481-7c497312e558.png)

```c
ER ev3_motor_set_power(motor_port_t port, int power);
```

関数名は`ev3_motor_set_power`で、その名の通りモータパワーを設定する関数です。
引数として **「ポート」** と **「パワー」(整数値)** を指定します。

使い方の例は次のOFF関数を紹介してから示します。

## モータOFF 関数

モータをONする関数と対を成す存在として、モータをOFFする関数があります。
EV3ソフトではこの👇ブロックにあたります。

![off.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/e75491cc-cb14-9527-79cf-508023b18f6c.png)

```c
ER ev3_motor_stop(motor_port_t port, bool_t brake);
```

関数名は`ev3_motor_stop`で、モータの回転を止める関数です。
引数として **「ポート」** と **「ブレーキの有無」** を指定します。

EV3ソフトで表すところの`☑`「ブレーキ」を使いたい場合は`true`を、`X`「惰性運転」を使いたい場合は`false`を指定します。

では、以下のEV3ソフトでのプログラムブロックを、EV3rtにて表現してみます。

![ex_on_off.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/8102f0be-b854-d31d-2e70-d9d700c21f48.png)

*Ex 3. モータをパワー50%でONし、1秒待機して、モータをOFFする*

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

  ev3_motor_set_power(EV3_PORT_A, 50);
  tslp_tsk(1*1000*1000);
  ev3_motor_stop(EV3_PORT_A, true);
}
```

どちらの関数もモータを動かすときの基本となるので、マスターしておきましょう。

## 指定した角度だけ回転する関数

次に、指定した角度だけモータが回転する関数を紹介します。
EV3ソフトではこの👇ブロックにあたります。

![degree.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/370bb4ee-a1ae-6048-69a5-b3b8a4906b6a.png)

```c
ER ev3_motor_rotate(motor_port_t port, int degrees, uint32_t speed_abs, bool_t blocking);
```

関数名は`ev3_motor_rotate`で、その名の通りモータパワーを設定する関数です。
引数として **「ポート」** 、 **「回転角度」(整数値)** 、**「スピード」(正の整数値)** 、 **「回転完了まで待つか」** を指定します。

「スピード」は `0~100` の自然数で指定する必要があります。
回転方向については「回転角度」で指定することになります。

「回転完了まで待つか」という項目ですが、指定した角度の回転が完全に終わるまで、次の関数を実行するのを待つかどうかということです。
待つ場合は`true`、待たない場合は`false`を指定します。

例を交えながら説明してみましょう。

*Ex 4. モータAをパワー50%で360度回し、その後にモータDをパワー50%で360度回す*

```c
void main_task(intptr_t unused)
{
    //プログラムをここから書く
    ev3_motor_config(EV3_PORT_A, LARGE_MOTOR);
    ev3_motor_config(EV3_PORT_D, LARGE_MOTOR);

    ev3_motor_rotate(EV3_PORT_A, 360, 50, true);
    ev3_motor_rotate(EV3_PORT_D, 360, 50, true);
}
```

この例であれば、モータAが360度(つまり一周)回り切って止まった後に、モータDが回り始めます。

では、次の例はどうでしょうか。

*Ex 5. モータAとDをパワー50%で360度回す*

```c
void main_task(intptr_t unused)
{
    //プログラムをここから書く
    ev3_motor_config(EV3_PORT_A, LARGE_MOTOR);
    ev3_motor_config(EV3_PORT_D, LARGE_MOTOR);

    ev3_motor_rotate(EV3_PORT_A, 360, 50, false);
    ev3_motor_rotate(EV3_PORT_D, 360, 50, true);
}
```

*Ex 4*と異なるのは、ポートAの`ev3_motor_rotate`の一番最後の引数を`true`から`false`に変えたところです。
この変更により、ポートAの`ev3_motor_rotate`関数は、モータの回転完了を待たず、次の関数の実行に移ります。
よって、ポートAとポートDのモータが**同時に回り**、それぞれ360度回ったところで停止します。

上手く活用すれば、ロボットの直進動作に使えそうですね。

## 2つのモータでステアリング制御する関数

最後に、2つのモータをステアリングで制御する関数を紹介します。
EV3ソフトではこの👇ブロックにあたります。

![steer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/b74ad72c-13da-d7e0-5472-31e96ea2d7b2.png)

```c
ER 	ev3_motor_steer(motor_port_t left_motor, motor_port_t right_motor, int power, int turn_ratio);
```

関数名は`ev3_motor_steer`で、上述のとおり2つのモータをステアリングで制御する関数です。
引数として **「左モータのポート」** 、 **「右モータのポート」** 、 **「パワー」(整数値)** 、 **「ステアリング度合」(整数値)** を指定します。

こちらに関しては「パワー」も「ステアリング度合」も`-100~100`の間の整数で指定します。

*Ex 6. モータAとDをパワー50%、ステアリング度合10で3秒回す*

```c
void main_task(intptr_t unused)
{
    //プログラムをここから書く
    ev3_motor_config(EV3_PORT_A, LARGE_MOTOR);
    ev3_motor_config(EV3_PORT_D, LARGE_MOTOR);

    ev3_motor_steer(EV3_PORT_A, EV3_PORT_D, 50, 10);
    tslp_tsk(3*1000*1000);
    ev3_motor_stop(EV3_PORT_A, true);
    ev3_motor_stop(EV3_PORT_D, true);
}
```

この`ev3_motor_steer`関数を使うときの注意点として、関数自体に回転量を指定する機能がありません。
従って、上記の例のように、秒数待機をした後に、`ev3_motor_stop`関数で回転を止める必要があります。
感覚的には、「`ev3_motor_set_power`関数をポート2つ分一気に行えるようにしたもの」といった感じですね。

以上が、モータを動かす系の関数です。

# 4. 回転角度に関わる関数

EV3モータには「ロータリーエンコーダ」という回転角度を測るセンサが内蔵されています。
これにより回転角度を指定した回転命令などが出来るわけですが、一つのセンサとして値を取得することも出来ます。

## 回転角度を取得する

まずは、回転角度を取得してみましょう。
EV3ソフトではこの👇ブロックにあたります。

![degree_get.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/ced435b9-24b0-8315-8db8-c08b5d36f739.png)

```c
int32_t ev3_motor_get_counts(motor_port_t port);
```

関数名は`ev3_motor_get_counts`で、回転角度を取得する関数です。
引数として **「ポート」** を指定し、戻り値として **「回転角度」** が整数値で返って来ます。

使い方についてですが、例えば先ほど紹介した`ev3_motor_steer`関数を角度で制御しようとすると、以下のように実現できます。

*Ex 7. モータAとDをパワー50%、ステアリング度合10で360度回す*

```c
void main_task(intptr_t unused)
{
    //プログラムをここから書く
    ev3_motor_config(EV3_PORT_A, LARGE_MOTOR);
    ev3_motor_config(EV3_PORT_D, LARGE_MOTOR);

    while(ev3_motor_get_counts(EV3_PORT_A) < 360){
        ev3_motor_steer(EV3_PORT_A, EV3_PORT_D, 50, 10);
    }
    ev3_motor_stop(EV3_PORT_A, true);
    ev3_motor_stop(EV3_PORT_D, true);
}
```

こうすると、「モータAが360度回転するまでループ」になるので、角度制御することができます。

## 回転角度をリセットする

先ほどから頻りに申している「回転角度」ですが、当然「基準となるポイント」があります。
通常、プログラム実行時に回転角度のリセットがかかるため、そこからの角度ということになりますが、任意のタイミングでリセットをしたい時もあるでしょう。

その時は次のコマンドでリセットすることが出来ます。
EV3ソフトではこの👇ブロックにあたります。

![degree_reset.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/0ae35fe2-bc51-5f68-27c9-47dd975c7894.png)

```c
ER ev3_motor_reset_counts(motor_port_t port);
```

関数名は`ev3_motor_reset_counts`で、回転角度をリセットする関数です。
引数として **「ポート」** を指定します。

使い方の例として、先ほどの*Ex 7*にリセット関数を書き足してみたいと思います。

*Ex 7-1. モータAとDをパワー50%、ステアリング度合10で360度回す*

```c
void main_task(intptr_t unused)
{
    //プログラムをここから書く
    ev3_motor_config(EV3_PORT_A, LARGE_MOTOR);
    ev3_motor_config(EV3_PORT_D, LARGE_MOTOR);

    ev3_motor_reset_counts(EV3_PORT_A);

    while(ev3_motor_get_counts(EV3_PORT_A) < 360){
        ev3_motor_steer(EV3_PORT_A, EV3_PORT_D, 50, 10);
    }
    ev3_motor_stop(EV3_PORT_A, true);
    ev3_motor_stop(EV3_PORT_D, true);
}
```

角度制御をする場合は、回転角度をリセットして基準点を設定する必要があるため、この方法を使えるようにしておきましょう。

# 5. モータ情報取得系関数

最後に、モータの情報を取得する関数を2つ紹介します。

## モータパワーを取得する関数

現在出力されているモータパワーを取得する関数です。
EV3ソフトではこの👇ブロックにあたります。

![power_get.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/a4de2059-b191-60fe-2991-8f7deb062e31.png)
 
```c
int ev3_motor_get_power(motor_port_t port);
```

関数名は`ev3_motor_get_power`で、出力パワー値を返す関数です。
引数として **「ポート」** を指定すると、 **「出力パワー値」** が戻り値として返ってきます。

ポイントとしては、「設定したパワー値」ではなく、「実際に出力されているパワー値」であるところです。

サンプルプログラムを作成してみましたので、是非実際に動かして確認してみてください。
(尚、まだ紹介していない関数等が含まれていますが、今は気にせず実行してみましょう。いずれも今後紹介予定です。)

*Ex 8. モータAの実際のパワー値を確認する*

```c
void main_task(intptr_t unused)
{
    //プログラムをここから書く
    ev3_motor_config(EV3_PORT_A, LARGE_MOTOR);
    ev3_motor_config(EV3_PORT_D, LARGE_MOTOR);

    ev3_lcd_set_font(EV3_FONT_MEDIUM);
    char str[8];

    ev3_motor_set_power(EV3_PORT_A, 50);
    while(1){
        sprintf(str, "%d", ev3_motor_get_power(EV3_PORT_A));
        ev3_lcd_draw_string(str, 0, 0);
    }
}
```

## モータタイプを取得する関数

以下の関数は、`ev3_motor_config`関数により設定したポートに対するモータタイプを取得します。

```c
ER_UINT ev3_motor_get_type(motor_port_t port);
```

使い方としては、以下のように引数に「ポート」を指定すると、戻り値としてモータタイプが返ってきます。

```c
ev3_motor_get_type(EV3_PORT_A);
```

しかし、`ev3_motor_config`関数により設定した情報、つまり既に自分が知っている情報なので、あまり使いどころは無いかと感じます。
(内部処理で使用されているようです。)

# 6. まとめ

今回はEV3のモータに関わる関数を一通り紹介しました。
ロボット開発には必要不可欠なモータを扱う関数ですので、是非マスターしましょう。

次回は、カラーセンサの使い方を紹介したいと思います。

前回: [#5 C言語のキホン➁](https://qiita.com/koushiro/items/9ee84c3b5bdefc0475c8)
次回: [#7 カラーセンサを使おう](https://qiita.com/koushiro/items/86c8855c85c5a6658030)