5000円で四足歩行ロボットしてみた。

## はじめに
この記事は素人が急に思い立って四足歩行ロボットを作った話です。
突っ込みどころ満載かと思いますが、ムムと思うところがあればご指導ご鞭撻のほどよろしくお願いします。
参考にできる点があるかはわかりませんが、タチコマはみんな好きだと思います。
今回は予算の都合で関節（サーボモーター）は8個でぎりぎり歩けるものを作ります。

## 用意するもの
とりあえず流行りのArduino使ってみたいじゃん。ってことで、必要な部品です。
6か月前に買ったので、ひょっとしたら現在は正確ではないかもしれません。
- Arduino UNO(の互換製品でサインスマートのやつ) 　1360円
- OSOYOO SG90 9g マイクロサーボ （10個セット） 2380円
- 低発砲塩ビ板　ホームセンターで500円くらい
- ユニバーサル基板、ジャンパピン、ピンソケット、単3*4本の電池ケース　秋月電子で合わせて500円くらい
 
しめて4700円位でした。
工具類やネジ類は家にあったものを使用したので、この辺を含めるともう少しいくかもしれません。

## 塩ビ板を加工してフレームを作る
まずは低発砲塩ビ板を加工してロボットのフレームを作ります。
ちなみに加工のしやすさと安価さから今回低発砲塩ビ板を選びましたが、単3電池4本を乗せるとたわんでしまうので、
加工できる方や予算に糸目はつけねーぜ！ってブルジョワな方はアルミとかで作るとよいかと思います。

直線に切り出す際は曲尺を当て、アクリルカッターでこれでもかってくらい同じところをなぞった後、
念のため目を閉じてからエイヤと折ります。
エイヤと折れないくらい細かい部分の切り出しは、ドリルで穴をあけた後にニッパーで穴と穴の間をパチパチする感じで切りました。

塩ビ板の曲げ加工ですが、それ用の工具がなかったので、ライターであぶって無理やり90度（大体）に曲げました。
塩ビ板が火に直接あたるとチョコレートのように溶けてしまうので、ライターの火があたらないくらいのところで根気よく温めたほうが良いかと思います。
結構熱かった上に純白の塩ビ板が焦げてしまったので、あまりお勧めしません。いい方法があったら教えてください。

![P_20180127_170907_vHDR_On.jpg](https://qiita-image-store.s3.amazonaws.com/0/279581/779e0eec-4347-5fb0-be63-704a4bc9ee04.jpeg)

こんな感じになりました。図面を描く才能に恵まれなかったので、直接部品をあてがいながら鉛筆で目安線を描きました。
スマートじゃないですね。

## 組み立て
組み立てます。ここが一番楽しい。
サーボモータをフレームにねじ止めしていきますが、その際サーボモータの可動域に注意してください。
可能であれば先に通電しておき、可動域の中心点を出しておいたほうがいいでしょう。
あとはロボットの姿を想像しながら可動域を調整・ねじ止めしていきます。
![kumitate.jpg](https://qiita-image-store.s3.amazonaws.com/0/279581/61d6bada-5bfd-3350-2e99-f43e9eba2e75.jpeg)
大体こんな感じになりました。
電源は単3電池4本を直列につないで5V確保しています。
配線ですが、サーボモータの信号線をArduino(もどき)のDigital Pinの1~9に、電源線は5VピンとGNDにつなげたかったので、
ユニバーサルボードにソケットピンを半田付けッティングし、いい具合に押し込みました。

![handa.jpg](https://qiita-image-store.s3.amazonaws.com/0/279581/f1985c8d-ce41-aeb1-06a4-cd0583e3592d.jpeg)


## いのちをふきこむ
あとは、4本の足を使って歩けるようにプログラミングをするだけです。

![creep.jpg](https://qiita-image-store.s3.amazonaws.com/0/279581/d66270a3-4f94-fdd2-7ff9-8c6c289451de.jpeg)
http://makezine.jp/blog/2016/12/robot-quadruped-arduino-program.html より

こういう動きで前に進むようにしたいと思います。
PWMとかで制御するのはかなり根気を要するので、Arduinoのservoライブラリを利用します。
attach()関数でpinを指定し、write()関数で角度を指定して動かせるのでラクチンです。

まずservoオブジェクトを関節の数だけ定義し、ボート上で接続したピン番号をattachしていきます。

``` c:sketch.ino

#include <Servo.h>

Servo frontRight1;
Servo frontRight2;
Servo backRight1;
Servo backRight2;
Servo frontLeft1;
Servo frontLeft2;
Servo backLeft1;
Servo backLeft2;

void setup() {
  frontRight1.attach(2);
  frontRight2.attach(3);
  backRight1.attach(4);
  backRight2.attach(5);
  frontLeft1.attach(6);
  frontLeft2.attach(7);
  backLeft1.attach(8);
  backLeft2.attach(9);
}
```

次に、servoオブジェクトを引数に取り、指定された角度動かす関数を作ります。
ロボットの右側につけたサーボモータが指定された場合は、角度に-1をかけて反転させています。

``` c:sketch.ino
void writeServo(String servo, int angle){
  if (servo == "frontRight1"){
      angle = angle * (-1);
      int angleNow = frontRight1.read();
      frontRight1.write(angleNow + angle);
  } else if (servo == "frontRight2"){
      angle = angle * (-1);
      int angleNow = frontRight2.read();
      frontRight2.write(angleNow + angle);
  } else if (servo == "backRight1"){
      angle = angle * (-1);
      int angleNow = backRight1.read();
      backRight1.write(angleNow + angle);
  } else if (servo == "backRight2"){
      int angleNow = backRight2.read();
      backRight2.write(angleNow + angle);
  } else if (servo == "frontLeft1"){
      int angleNow = frontLeft1.read();
      frontLeft1.write(angleNow + angle);
  } else if (servo == "frontLeft2"){
      int angleNow = frontLeft2.read();
      frontLeft2.write(angleNow + angle);
  } else if (servo == "backLeft1"){
      int angleNow = backLeft1.read();
      backLeft1.write(angleNow + angle);
  } else if (servo == "backLeft2"){
      angle = angle * (-1);
      int angleNow = backLeft2.read();
      backLeft2.write(angleNow + angle);
  }
}
```

次に、ロボットを一度立たせたいのですが、サーボモータの個体差や組み立てのずれで微妙に調節が必要になってくるので、
サーボモータのグラウンドポジションを定数として定義しています。
standUp()関数でまっすぐ立つようにします。

``` c:sketch.ino
int GROUNDFRONTRIGHT1 = 120;
int GROUNDBACKRIGHT1 = 30;
int GROUNDFRONTLEFT1 = 60;
int GROUNDBACKLEFT1 = 150;

int GROUNDFRONTRIGHT2 = 30;
int GROUNDBACKRIGHT2 = 150;
int GROUNDFRONTLEFT2 = 150;
int GROUNDBACKLEFT2 = 30;

void standUp(){
  frontRight1.write(GROUNDFRONTRIGHT1);
  frontRight2.write(GROUNDFRONTRIGHT2);
  backRight1.write(GROUNDBACKRIGHT1);
  backRight2.write(GROUNDBACKRIGHT2);
  frontLeft1.write(GROUNDFRONTLEFT1);
  frontLeft2.write(GROUNDFRONTLEFT2);
  backLeft1.write(GROUNDBACKLEFT1);
  backLeft2.write(GROUNDBACKLEFT2);
}
```

ではついに前に歩く関数を作ります。
今回は歩かせることができればいったんゴールということにします。

``` c:sketch.ino
void moveForward(){
    writeServo("frontRight1", 30);
    writeServo("backRight1", -30);
    delay(100);
    while(true){
      writeServo("frontRight2", -50);
      delay(50);
      writeServo("frontRight1", -90);
      delay(50);
      writeServo("frontRight2", 50);
      delay(100);

      writeServo("frontRight1", 60);
      writeServo("backRight1", 30);
      writeServo("frontLeft1", 30);
      writeServo("backLeft1", 30);
      delay(100);

      writeServo("backLeft2", -50);
      delay(50);
      writeServo("backLeft1", -60);
      delay(50);
      writeServo("backLeft2", 50);
      delay(100);

      writeServo("frontLeft2", -50);
      delay(50);
      writeServo("frontLeft1", -90);
      delay(50);
      writeServo("frontLeft2", 50);
      delay(100);

      writeServo("frontLeft1", 60);
      writeServo("backLeft1", 30);
      writeServo("frontRight1", 30);
      writeServo("backRight1", 30);
      delay(100);

      writeServo("backRight2", -50);
      delay(50);
      writeServo("backRight1", -60);
      delay(50);
      writeServo("backRight2", 50);
      delay(100);
    }
}
```
delayをかけないと文字通り足が絡まってｼﾞｼﾞｼﾞ･･･となってしまうので、delayしまくってます。
まとめるとこんな感じです。

```c:sketch.ino
#include <Servo.h>
Servo frontRight1;
Servo frontRight2;
Servo backRight1;
Servo backRight2;
Servo frontLeft1;
Servo frontLeft2;
Servo backLeft1;
Servo backLeft2;

int GROUNDFRONTRIGHT1 = 120;
int GROUNDBACKRIGHT1 = 30;
int GROUNDFRONTLEFT1 = 60;
int GROUNDBACKLEFT1 = 150;

int GROUNDFRONTRIGHT2 = 30;
int GROUNDBACKRIGHT2 = 150;
int GROUNDFRONTLEFT2 = 150;
int GROUNDBACKLEFT2 = 30;

void writeServo(String servo, int angle){
  if (servo == "frontRight1"){
      angle = angle * (-1);
      int angleNow = frontRight1.read();
      frontRight1.write(angleNow + angle);
  } else if (servo == "frontRight2"){
      angle = angle * (-1);
      int angleNow = frontRight2.read();
      frontRight2.write(angleNow + angle);
  } else if (servo == "backRight1"){
      angle = angle * (-1);
      int angleNow = backRight1.read();
      backRight1.write(angleNow + angle);
  } else if (servo == "backRight2"){
      int angleNow = backRight2.read();
      backRight2.write(angleNow + angle);
  } else if (servo == "frontLeft1"){
      int angleNow = frontLeft1.read();
      frontLeft1.write(angleNow + angle);
  } else if (servo == "frontLeft2"){
      int angleNow = frontLeft2.read();
      frontLeft2.write(angleNow + angle);
  } else if (servo == "backLeft1"){
      int angleNow = backLeft1.read();
      backLeft1.write(angleNow + angle);
  } else if (servo == "backLeft2"){
      angle = angle * (-1);
      int angleNow = backLeft2.read();
      backLeft2.write(angleNow + angle);
  }
}

void standUp(){
  frontRight1.write(GROUNDFRONTRIGHT1);
  frontRight2.write(GROUNDFRONTRIGHT2);
  backRight1.write(GROUNDBACKRIGHT1);
  backRight2.write(GROUNDBACKRIGHT2);
  frontLeft1.write(GROUNDFRONTLEFT1);
  frontLeft2.write(GROUNDFRONTLEFT2);
  backLeft1.write(GROUNDBACKLEFT1);
  backLeft2.write(GROUNDBACKLEFT2);
}

void moveForward(){
    writeServo("frontRight1", 30);
    writeServo("backRight1", -30);
    delay(100);
    while(true){
      writeServo("frontRight2", -50);
      delay(50);
      writeServo("frontRight1", -90);
      delay(50);
      writeServo("frontRight2", 50);
      delay(100);

      writeServo("frontRight1", 60);
      writeServo("backRight1", 30);
      writeServo("frontLeft1", 30);
      writeServo("backLeft1", 30);
      delay(100);

      writeServo("backLeft2", -50);
      delay(50);
      writeServo("backLeft1", -60);
      delay(50);
      writeServo("backLeft2", 50);
      delay(100);

      writeServo("frontLeft2", -50);
      delay(50);
      writeServo("frontLeft1", -90);
      delay(50);
      writeServo("frontLeft2", 50);
      delay(100);

      writeServo("frontLeft1", 60);
      writeServo("backLeft1", 30);
      writeServo("frontRight1", 30);
      writeServo("backRight1", 30);
      delay(100);

      writeServo("backRight2", -50);
      delay(50);
      writeServo("backRight1", -60);
      delay(50);
      writeServo("backRight2", 50);
      delay(100);
    }
}

void setup() {
  frontRight1.attach(2);
  frontRight2.attach(3);
  backRight1.attach(4);
  backRight2.attach(5);
  frontLeft1.attach(6);
  frontLeft2.attach(7);
  backLeft1.attach(8);
  backLeft2.attach(9);

  standUp();
  delay(100);
  moveForward();

}

void loop() {

}
```

## 蛇足

![vlcsnap-2018-08-12-12h50m33s963.png](https://qiita-image-store.s3.amazonaws.com/0/279581/34dfc9ff-5303-d8c9-2785-b051d1365992.png)


カオナシ状態でかわいそうだったので、手近な素材で顔を作ってみました。
愛着わきまくりです。
部品を切り出す時点で電池ケースのサイズ測定をミスって綺麗にはまらなかったので、紙テープを巻きつけてハンドメイド感を演出しています。

最後までお付き合いいただき、ありがとうございました！


## 参考
http://makezine.jp/blog/2016/12/robot-quadruped-arduino-program.html
http://www.musashinodenpa.com/arduino/ref/