[ev3×Java]   複数モーターの制御(メソッド、ランダム、確率、制限時間、配列)

この記事はJavaでev3を操作してみたい人のための記事です。
今回はタンクをいろいろな方法で制御していきたいと思います。

#目次
0 . 用意するもの
1 . 複数モーターの制御
　1-0 . タンクを前進させるプログラム
　1-1 . メソッドをつくって制御する
　1-2 . 引数を指定できるメソッドをつくる
　1-3 . StopWatchクラスを使うプログラム
　1-4 . メソッドを指定した確率で呼び出すプログラム
　1-5 . 配列を使うプログラム
最後に








#0.用意するもの
◯ ev3(タンク)
◯ Lモーター/Mモーター
◯ パソコン(eclipse)
◯ bluetooth
◯ microSD
◯ [API Documentation](http://www.lejos.org/ev3/docs/)(これをみながら進めていくのがオススメです。)



---












　

#1.複数モーターの制御

##1-0 . タンクを前進させるプログラム

◯タンクが5秒間前進して方向転換するプログラムです。
必要なメソッドを並べるだけのシンプルなものですが、順次処理だとうまくいかない部分があるので、工夫が必要です。

```tank00.java
//必要なもの(材料)をimportする
import lejos.hardware.motor.EV3LargeRegulatedMotor;
import lejos.hardware.port.MotorPort;
import lejos.robotics.RegulatedMotor;
import lejos.utility.Delay;

//クラスをつくる
public class tank00 {

	//staticメソッドをつくる
    public static void main(String[] args) {

        //インスタンスの生成
        RegulatedMotor l_a = new EV3LargeRegulatedMotor(MotorPort.A);
        RegulatedMotor l_b = new EV3LargeRegulatedMotor(MotorPort.B);
        //スピードを設定する(単位は[degrees/s])
        l_a.setSpeed(720);
        l_b.setSpeed(720);
        //5秒間前進する
        l_a.forward();
        l_b.forward();
        Delay.msDelay(5000);
        //その場で方向転換する(同時に回転を開始する)
        l_a.rotate(-2160,true);
        l_b.rotate(2160);
        //モータ2つを同時に停止する
        l_a.stop(true);
        l_b.stop();
    }
}
```

---
Point : **public void rotate(int angle,boolean immediateReturn)**
**immediateReturn** - if true **do not wait for the move to complete**

rotate()メソッドは第2引数で、**その処理の完了を待ってから次の処理を実行するか否か**を決定できます。trueにすると、処理の完了を待たずに次の処理を開始します。
これによって、上のプログラムではモーターAとBを両方同時に回転させることができています。

---

Point : **public void stop(boolean immediateReturn)**
引数にtrueを指定することによって、**モーターを停止させる処理の完了を待たず**に**即座に次の処理を実行**することができます。
[参考資料]　:　[Class BaseRegulatedMotor](http://www.lejos.org/ev3/docs/)

---

























##1-1 . メソッドをつくって制御するプログラム

◯使い回しがしやすいように、メソッドを自分でつくります。

```tank01.java
//必要なもの(材料)をimportする
import lejos.hardware.motor.EV3LargeRegulatedMotor;
import lejos.hardware.port.MotorPort;
import lejos.robotics.RegulatedMotor;
import lejos.utility.Delay;

//クラスをつくる
public class tank01 {
	
	//インスタンスの生成
	private final static RegulatedMotor l_a = new EV3LargeRegulatedMotor(MotorPort.A);
	private final static RegulatedMotor l_b = new EV3LargeRegulatedMotor(MotorPort.B);
	//整数型の変数motorSpeedの定義
	private final static int motorSpeed = 200;
	
	//staticメソッドをつくる
    public static void main(String[] args) {
        //実行するメソッド
        forward();
        turnRight();
        forward();
        stop();
    }

    //メソッドの定義
    public static void forward(){
    	l_a.setSpeed(motorSpeed);
    	l_b.setSpeed(motorSpeed);
    	l_a.forward();
    	l_b.forward();
    	Delay.msDelay(2000);
    }
    //メソッドの定義    
    public static void turnRight(){
    	l_a.setSpeed(motorSpeed);
    	l_b.setSpeed(motorSpeed-100);
    	l_a.forward();
    	l_b.forward();
    	Delay.msDelay(1000);
    }
    //メソッドの定義    
    public static void stop(){
    	l_a.stop(true);
    	l_b.stop();
    }
}
```
---

Point : **メソッドをつくる**
主に"**コードをみやすくする**"、"**再利用する**"ためにメソッドをつくります。
関連のあるコードをまとめて1つにすることを**モジュール化**といいます。
関数をつくることは、モジュール化であると言えます。

---

























##1-2 . 引数を指定できるメソッドをつくる

◯引数を指定して、より自由度の高いメソッドをつくっていくプログラムです。

```tank02.java
//必要なもの(材料)をimportする
import lejos.hardware.motor.EV3LargeRegulatedMotor;
import lejos.hardware.port.MotorPort;
import lejos.robotics.RegulatedMotor;

//クラスをつくる
public class tank02 {
	
	//インスタンスの生成
	private final static RegulatedMotor l_a = new EV3LargeRegulatedMotor(MotorPort.A);
	private final static RegulatedMotor l_b = new EV3LargeRegulatedMotor(MotorPort.B);
	
	//staticメソッドをつくる
    public static void main(String[] args) {
        //実行するメソッド
        tank_drive(80,80,4);
        tank_drive(-100,-100,2);
        tank_drive(-30,30,3);
        stop();
    }

    //メソッドの定義
    public static void tank_drive(int a_speed,int b_speed,int rotations){
    	//マックスの(a_speed)% or (b_speed)% の(絶対値の)スピードが出るようにする
    	l_a.setSpeed(Math.abs((int) l_a.getMaxSpeed()*a_speed/100));
    	l_b.setSpeed(Math.abs((int) l_b.getMaxSpeed()*b_speed/100));
    	
    	//a_speedとb_speedの正負の組み合わせで場合分けする
    	if(a_speed > 0) {
                //a_speed、b_speedともに正
    		if(b_speed > 0) {
    	    	l_a.rotate(rotations*360,true);
    	        l_b.rotate(rotations*360);
                //a_speedは正、b_speedは負
    		}else {
    	    	l_a.rotate(rotations*360,true);
    	        l_b.rotate(rotations*360*-1);
    		}
    	}else {
                //a_speedは負b_speedは正
    		if(b_speed > 0) {
    	    	l_a.rotate(rotations*360*-1,true);
    	        l_b.rotate(rotations*360);
                //a_speed、b_speedともに負
    		}else {
    	    	l_a.rotate(rotations*360*-1,true);
    	        l_b.rotate(rotations*360*-1);
    		}
    	}
    }

    //メソッドの定義    
    public static void stop(){
    	l_a.stop(true);
    	l_b.stop();
    }
}

```

---

Point : **絶対値**
Math.abs()で求めることができる。
[参考記事]　:　[Java 絶対値を取得するサンプル(abs)](https://itsakura.com/java-abs)

---









##1-3.StopWatchクラスを使うプログラム

◯制限時間を設けるプログラムです。


```tank03.java
import lejos.hardware.motor.EV3LargeRegulatedMotor;
import lejos.hardware.port.MotorPort;
import lejos.robotics.RegulatedMotor;
import lejos.utility.Stopwatch;
import java.util.Random;
import lejos.utility.Delay;


public class tank03 {
	//インスタンスの生成
    public static Stopwatch stopwatch = new Stopwatch();
    private final static RegulatedMotor l_a = new EV3LargeRegulatedMotor(MotorPort.A);
	private final static RegulatedMotor l_b = new EV3LargeRegulatedMotor(MotorPort.B);
	
    //staticメソッドをつくる
    public static void main(String[] args) {     

    	//タイマーをリセット
        stopwatch.reset(); 
        //無限ループ
        while (true) {
        	forward();
            //経過時間を表示する
        	System.out.println(Math.round(stopwatch.elapsed() / 1000));
        	//20秒を過ぎたらモータを停止して、ループを抜ける
        	if(stopwatch.elapsed() > 20 * 1000) {
        		stop();
        		break;
        	}
        }
    }
    //メソッドの定義
    public static void forward(){
    	//インスタンスの生成
    	Random r = new Random();
    	//0-マックススピードの範囲から値をランダムに選択してスピードに設定する
    	l_a.setSpeed(r.nextInt((int) l_a.getMaxSpeed()));
    	l_b.setSpeed(r.nextInt((int) l_a.getMaxSpeed()));
    	l_a.forward();
    	l_b.forward();
    	Delay.msDelay(1000);
    }
    //メソッドの定義   	
    public static void stop(){
    	l_a.stop(true);
    	l_b.stop();
    }
}
```
---
Point : **Math.round**
四捨五入するためのメソッドです。
[参考記事]　:　[Java 四捨五入するサンプル(round)](https://itsakura.com/java-round)

---

Point : **Randomクラス**
ランダムな値を出力します。
[参考記事]　:　[Javaで指定範囲の乱数を生成する：Random.nextInt()](https://uxmilk.jp/47989)

---
Point : **stopwatch.elapsed()**
Stopwatchクラスのメソッド。
経過時間を返します。

---

##1-4.メソッドを指定した確率で呼び出すプログラム

◯前進する確率が高くなるように意図的に設定したプログラムです。

```.java
import java.util.Random;
import lejos.hardware.motor.EV3LargeRegulatedMotor;
import lejos.hardware.port.MotorPort;
import lejos.robotics.RegulatedMotor;
import lejos.utility.Delay;

public class tank04 {
    private final static RegulatedMotor l_a = new EV3LargeRegulatedMotor(MotorPort.A);
	private final static RegulatedMotor l_b = new EV3LargeRegulatedMotor(MotorPort.B);
	private final static int motorSpeed = 200;
	
    //staticメソッドをつくる
    public static void main(String[] args) {     
    	l_a.setSpeed(motorSpeed);
    	l_b.setSpeed(motorSpeed);
    	
        //10回処理を繰り返す
    	for(int i = 0;i < 10;i++) {
            //インスタンスの生成
    		Random r = new Random();
            //0から100までの範囲の中から、ランダムに数値を選ぶ
    		int random_value = r.nextInt(100);
    		System.out.println("random_value = " + random_value);
    		
    		//10%の確率で停止
    		if(random_value < 10) {
        		stop();
        	//10%の確率で右に方向転換
        	}else if(random_value < 20) {
        		turnRight();
        	//30%の確率で左に方向転換
        	}else if(random_value < 50) {
        		turnLeft();
        	//50%の確率で前進
        	}else {
        		forward();
        	}
    	}
    }
    //メソッドの定義
    public static void forward(){
    	l_a.forward();
    	l_b.forward();
    	Delay.msDelay(2000);
    }
    //メソッドの定義    
    public static void turnRight(){
    	l_a.forward();
    	l_b.backward();
    	Delay.msDelay(1000);
    }
    //メソッドの定義    
    public static void turnLeft(){
    	l_a.backward();
    	l_b.forward();
    	Delay.msDelay(1000);
    }
    //メソッドの定義    
    public static void stop(){
    	l_a.stop(true);
    	l_b.stop();
    	Delay.msDelay(1000);
    }
}
```

---

Point : **確率のコントロール**
前進する確率だけが高くなるようにする方法を取り上げました。
しかし、下のように書いてしまうと、意図するものにはなりません。

```.java
if(random_value < 100){
    forward();
}else if(random_value < 50){
    turnRight();  
}else if(random_value < 20){
    turnLeft();
}else{
    stop();
}
```

なぜなら、プログラムは上から順番に処理を実行していくからです。
random_numberが決まったとき、最初にその数値が条件と合致するかをチェックするのは、1番上の``if(random_value < 100)``です。この場合全ての数値がこの条件を満たすので、forward()メソッドしか実行することができません。

---

##1-5.配列を使うプログラム

◯配列から要素を取り出してスピードに設定するプログラムです。

```.java
import lejos.hardware.motor.EV3LargeRegulatedMotor;
import lejos.hardware.port.MotorPort;
import lejos.robotics.RegulatedMotor;
import lejos.utility.Delay;


public class tank05 {
    private final static RegulatedMotor l_a = new EV3LargeRegulatedMotor(MotorPort.A);
	private final static RegulatedMotor l_b = new EV3LargeRegulatedMotor(MotorPort.B);
	
    //staticメソッドをつくる
    public static void main(String[] args) {     
    	//インスタンスの生成
    	//配列の宣言と要素数の決定
        int[] speed_list = new int[5];
        //要素の初期化
        speed_list[0] = 500;
        speed_list[1] = 1000;
        speed_list[2] = 1500;
        speed_list[3] = 2000;
        speed_list[4] = 2500;

        //5回処理を繰り返し、その度にiが1ずつ加算されていく
        for (int i = 0;i < 5;i++) {
        	//配列の要素を使う
        	//count = 2 : speed = 1500 のように表示される
        	System.out.println("count = " + i + ":" + "speed = " + speed_list[i]);
        	l_a.setSpeed(speed_list[i]);
        	l_b.setSpeed(speed_list[i]);
        	l_a.forward();
        	l_b.forward();
        	Delay.msDelay(3000);
        }
    	l_a.stop(true);
    	l_b.stop();
    }
}
```
---


Point : **配列の使いかた**
配列は、以下の手順で使います。

1.配列の**宣言**(と要素数の決定)
2.配列の要素の**初期化**
3.配列の**使用**

---

Point : **配列の要素の初期化**

配列の要素の初期化にはいくつか方法があります。
規則的な要素を配列に格納していく場合などは、ループと組み合わせて以下のようにできます。

```.java
for(int i = 0;i < 5;i++){
    //speed_list = [500,1000,1500,2000,2500]
    speed_list[i] = 500*(i+1);
}
```

---









#最後に

読んで頂きありがとうございました！！
次回は複数のモーターの制御について書いていきたいと思います！

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