4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ネットで見つけた激安ロボットが実際動くまでのハナシ

Last updated at Posted at 2019-12-10

はじめ

ヘキサポッドとか言うカッコよくて安い1の発見したのでポチった!

届いた

これ、骨組みしかなくね?2

結局使った部品

名称 個数 購入サイト 補足
ヘキサポッド/ 6本足ロボット スパイダー ロボットフルセット ブラケット アクセサリー ブラック 1 https://www.amazon.co.jp/dp/B07QFDPJM5 骨組み、ネジ+ナット、ケーブルスパイラルチューブのセット
ネジ+ナット+平ワッシャー+スプリングワッシャー(M2) * https://www.amazon.co.jp/dp/B01NCRQHRK 上記セットにM2ネジ+ナットが含まれていなかったため
obniz 1 https://www.amazon.co.jp/dp/B07DD6FK8G 心臓部
サーボモータードライバー PCA9685 1 https://www.amazon.co.jp/dp/B07SLRG5J1 1つで16個のサーボモーターを制御可能
サーボモーター SG90 12 https://www.amazon.co.jp/dp/B00VUJYNWG 本来6本脚のロボットだが、一身上の都合3により4本脚に
カメラ Arducam Mini 2MP 1 https://www.switch-science.com/catalog/3780/ カメラが付いてた方がカッコいいと思った
モバイルバッテリー+USBケーブル(A-MicroB) 1 https://store.shopping.yahoo.co.jp/meiseishop/539a1.html 出力2.1Aの割りに軽かったので採用、電流低いとサーボが上手く動かない…
ブレッドボード 1 http://akizukidenshi.com/catalog/g/gP-05155/ 小さめのものを採用
L型ヘッダーピン 1 http://akizukidenshi.com/catalog/g/gC-05336/ obnizのJ1ピンに半田付けしておくと電源の取り回しが楽になる。新型では不要
両側ロングヘッダーピン 1 http://akizukidenshi.com/catalog/g/gC-09056/ obnizをブレッドボードに接続する時に使用
ジャンパーワイヤー * 短い物もあると良い
スポンジ両面テープ * 百均ので十分
ケーブルタイ * 百均ので十分

部品の山を見ると、完成するか不安になる…
部品

obniz とは

公式サイト
端的に言うとJavascriptで電子工作できヤツ
今回使う部品のライブラリもあるので、驚くほど簡単に作れるハズ

サーボモータードライバー とは

obniz単体でもサーボモーターの制御は可能だが、流石に12個も制御できない4ので採用

大体の手順

部品の動作確認

パーツライブラリのサーボドライバーサーボを参考に動作確認を行う
この時、サーボを90°に設定してサーボホーンを付けておくと組み立て時に二度手間にならない…
サーボの動作確認

カメラも動作確認しておく
カメラの動作確認

組み立て

購入サイトの完成図を見ながら骨組みにサーボを取り付けていく
骨組みにサーボを取り付け
ネジ+ナット、スポンジ両面テープ、ケーブルタイ等を駆使し、他の部品を盛っていく
配線

プログラム

動作確認時に書いたコードを元にロボット制御用のプログラムを作成する
歩行に関してはこのページを参考にしたけど、難しいことはわからなかったので、角度は現物合わせした

他に気を付ける点は、obnizはI2C5が1つしか使えないため、サーボモータードライバーとカメラで同じI2Cを使うようコードを書く必要がある

コード(ぼちぼち長いので折り畳み)
コード
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
    <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
    <script src="https://unpkg.com/obniz@3.1.0/obniz.js" crossorigin="anonymous"></script>
    <style>
      @media screen and (orientation: landscape) {
        .camera-image {
          width: auto;
          height: 100%;
          right: 0px;
        }
      }

      @media screen and (orientation: portrait) {
        .camera-image {
          width: 100%;
          height: auto;
        }
      }

      body {
        user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        -khtml-user-select: none;
        -webkit-user-select: none;
        -webkit-touch-callout: none;
      }

      .camera-image {
        position: absolute;
        z-index: 0;
        border: 2px;
        border-style: solid;
        border-color: dimgray;
      }

      .parent {
        height: 100%;
        width: 100%;
        background-color: darkgray;
        position: relative;
      }

      .moveGroup {
        position: absolute;
        bottom: 210px;
        left: 5px;
        z-index: 1;
      }

      .button {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 100px;
        height: 100px;
        border: 1px solid #000000;
        border-radius: 10% 10% 10% 10%;
        font-size: xx-large;
        opacity: 0.50;
        background-color: white;
      }

      .button-pressed {
        background: #EFEFEF;
      }

      .forward {
        position: absolute;
        top: 0px;
        left: 105px;
      }

      .turn-left {
        position: absolute;
        top: 0px;
        left: 0px;
      }

      .turn-right {
        position: absolute;
        top: 0px;
        left: 210px;
      }

      .left {
        position: absolute;
        top: 105px;
        left: 0px;
      }

      .right {
        position: absolute;
        top: 105px;
        left: 210px;
      }

      .backward {
        position: absolute;
        top: 105px;
        left: 105px;
      }
    </style>
  </head>

  <body>
    <div class="parent">
      <div class="moveGroup">
        <div class="button forward" id="Forward"></div>
        <div class="button turn-left" id="TurnLeft"></div>
        <div class="button turn-right" id="TurnRight"></div>
        <div class="button left" id="Left"></div>
        <div class="button backward" id="Backward"></div>
        <div class="button right" id="Right"></div>
      </div>
      <img class="camera-image" id="CameraImage">
    </div>
    <script>
      /**
       * スリープ
       * param {number} msec ミリ秒
       */
      const sleep = msec => new Promise(resolve => setTimeout(resolve, msec));
      
      var obniz = new Obniz();

      obniz.onconnect = async function () {
        var i2c = obniz.getFreeI2C();
        i2c.start({ mode: "master", sda: 8, scl: 9, clock: 400000, pull: "5v" });

        var camera = obniz.wired("ArduCAMMini", { cs: 2, mosi: 3, miso: 4, sclk: 5, gnd: 6, i2c: i2c, module_version: 1 });
        await camera.startupWait();
           camera.setSize("176x144");
           await obniz.wait(1000);
        
        await setInterval(async function () {
          let data = await camera.takeWait();
          let base64 = camera.arrayToBase64(data);
          $("#CameraImage").attr("src", "data:image/jpeg;base64, " + base64);
        }, 1000);

        var servoDriver = obniz.wired("PCA9685", { i2c: i2c });
        var rightFront1Servo = obniz.wired("ServoMotor", { pwm: servoDriver.getPWM(0) });
        var rightFront2Servo = obniz.wired("ServoMotor", { pwm: servoDriver.getPWM(1) });
        var rightFront3Servo = obniz.wired("ServoMotor", { pwm: servoDriver.getPWM(2) });
        var rightRear1Servo = obniz.wired("ServoMotor", { pwm: servoDriver.getPWM(4) });
        var rightRear2Servo = obniz.wired("ServoMotor", { pwm: servoDriver.getPWM(5) });
        var rightRear3Servo = obniz.wired("ServoMotor", { pwm: servoDriver.getPWM(6) });
        var leftRear1Servo = obniz.wired("ServoMotor", { pwm: servoDriver.getPWM(8) });
        var leftRear2Servo = obniz.wired("ServoMotor", { pwm: servoDriver.getPWM(9) });
        var leftRear3Servo = obniz.wired("ServoMotor", { pwm: servoDriver.getPWM(10) });
        var leftFront1Servo = obniz.wired("ServoMotor", { pwm: servoDriver.getPWM(12) });
        var leftFront2Servo = obniz.wired("ServoMotor", { pwm: servoDriver.getPWM(13) });
        var leftFront3Servo = obniz.wired("ServoMotor", { pwm: servoDriver.getPWM(14) });

        // 各サーボのパルス幅
        rightFront1Servo.range = { min: 0.5, max: 2.4 }
        rightFront2Servo.range = { min: 0.5, max: 2.4 }
        rightFront3Servo.range = { min: 0.5, max: 2.4 }
        rightRear1Servo.range = { min: 0.5, max: 2.4 }
        rightRear2Servo.range = { min: 0.5, max: 2.4 }
        rightRear3Servo.range = { min: 0.5, max: 2.4 }
        leftRear1Servo.range = { min: 0.5, max: 2.4 }
        leftRear2Servo.range = { min: 0.5, max: 2.4 }
        leftRear3Servo.range = { min: 0.5, max: 2.4 }
        leftFront1Servo.range = { min: 0.5, max: 2.4 }
        leftFront2Servo.range = { min: 0.5, max: 2.4 }
        leftFront3Servo.range = { min: 0.5, max: 2.4 }

        // 各サーボのトリム値
        const rightFront1Trim = -6;
        const rightFront2Trim = 0;
        const rightFront3Trim = 0;
        const rightRear1Trim = 0;
        const rightRear2Trim = 0;
        const rightRear3Trim = 0;
        const leftRear1Trim = -5;
        const leftRear2Trim = -2;
        const leftRear3Trim = -2;
        const leftFront1Trim = 12;
        const leftFront2Trim = 7;
        const leftFront3Trim = 3;

        var isBasePosition = false;
        var isForward = false;
        var isTurnRight = false;
        var isTurnLeft = false;
        var isRight = false;
        var isLeft = false;
        var isBackward = false;
        var isStoped = true;

        /**
         * 右前1角度設定
         * param {number} val 角度
         */
        const rightFront1Angle = function (val) {
            rightFront1Servo.angle(rightFront1Trim + val);
        }

        /**
         * 右前2角度設定
         * param {number} val 角度
         */
        const rightFront2Angle = function (val) {
          rightFront2Servo.angle(180 - (rightFront2Trim + val));
        }

        /**
         * 右前3角度設定
         * param {number} val 角度
         */
        const rightFront3Angle = function (val) {
          rightFront3Servo.angle(180 - (rightFront3Trim + val));
        }

        /**
         * 右後1角度設定
         * param {number} val 角度
         */
        const rightRear1Angle = function (val) {
          rightRear1Servo.angle(180 - (rightRear1Trim + val));
        }

        /**
         * 右後2角度設定
         * param {number} val 角度
         */
        const rightRear2Angle = function (val) {
          rightRear2Servo.angle(rightRear2Trim + val);
        }

        /**
         * 右後3角度設定
         * param {number} val 角度
         */
        const rightRear3Angle = function (val) {
          rightRear3Servo.angle(rightRear3Trim + val);
        }

        /**
         * 左後1角度設定
         * param {number} val 角度
         */
        const leftRear1Angle = function (val) {
          leftRear1Servo.angle(leftRear1Trim + val);
        }

        /**
         * 左後2角度設定
         * param {number} val 角度
         */
        const leftRear2Angle = function (val) {
          leftRear2Servo.angle(180 - (leftRear2Trim + val));
        }

        /**
         * 左後3角度設定
         * param {number} val 角度
         */
        const leftRear3Angle = function (val) {
          leftRear3Servo.angle(180 - (leftRear3Trim + val));
        }

        /**
         * 左前1角度設定
         * param {number} val 角度
         */
        const leftFront1Angle = function (val) {
          leftFront1Servo.angle(180 - (leftFront1Trim + val));
        }

        /**
         * 左前2角度設定
         * param {number} val 角度
         */
        const leftFront2Angle = function (val) {
          leftFront2Servo.angle(leftFront2Trim + val);
        }

        /**
         * 左前3角度設定
         * param {number} val 角度
         */
        const leftFront3Angle = function (val) {
          leftFront3Servo.angle(leftFront3Trim + val);
        }

        /**
         * 右前角度設定
         * param {number} val1 角度1
         * param {number} val2 角度2
         * param {number} val3 角度3
         */
        const rightFrontAngle = function (val1, val2, val3) {
          rightFront1Angle(val1);
          rightFront2Angle(val2);
          rightFront3Angle(val3);
        }

        /**
         * 右後角度設定
         * param {number} val1 角度1
         * param {number} val2 角度2
         * param {number} val3 角度3
         */
        const rightRearAngle = function (val1, val2, val3) {
          rightRear1Angle(val1);
          rightRear2Angle(val2);
          rightRear3Angle(val3);
        }

        /**
         * 左前角度設定
         * param {number} val1 角度1
         * param {number} val2 角度2
         * param {number} val3 角度3
         */
        const leftRearAngle = function (val1, val2, val3) {
          leftRear1Angle(val1);
          leftRear2Angle(val2);
          leftRear3Angle(val3);
        }

        /**
         * 左後角度設定
         * param {number} val1 角度1
         * param {number} val2 角度2
         * param {number} val3 角度3
         */
        const leftFrontAngle = function (val1, val2, val3) {
          leftFront1Angle(val1);
          leftFront2Angle(val2);
          leftFront3Angle(val3);
        }

        /**
         * ベースポジション
         */
        const basePosition = async function () {
          rightFrontAngle(40, 60, 80);
          leftFrontAngle(40, 60, 80);
          rightRearAngle(40, 60, 80);
          leftRearAngle(40, 60, 80);
          await sleep(100);

          isBasePosition = true;
        }

        /**
         * 前進
         */
        const forward = async function () {
          while (isForward) {
            isStoped = false;

            leftRearAngle(40, 90, 100);
            await sleep(100);
            leftRearAngle(40, 60, 100);
            await sleep(100);

            leftFrontAngle(50, 90, 65);
            await sleep(100);
            leftFrontAngle(50, 75, 65);
            await sleep(100);

            rightFrontAngle(40, 60, 100);
            leftFrontAngle(40, 60, 80);
            rightRearAngle(50, 75, 65);
            leftRearAngle(40, 60, 80);
            await sleep(100);

            rightRearAngle(40, 90, 100);
            await sleep(100);
            rightRearAngle(40, 60, 100);
            await sleep(100);

            rightFrontAngle(50, 90, 65);
            await sleep(100);
            rightFrontAngle(50, 75, 65);
            await sleep(100);

            rightFrontAngle(40, 60, 80);
            leftFrontAngle(40, 60, 100);
            rightRearAngle(40, 60, 80);
            leftRearAngle(50, 75, 65);
            await sleep(100);

            isBasePosition = false;
          }

          isStoped = true;
        }

        /**
         * 右旋回
         */
        const turnRight = async function () {
          while (isTurnRight) {
            isStoped = false;

            rightFrontAngle(40, 90, 110);
            await sleep(100);
            rightFrontAngle(40, 60, 110);
            await sleep(100);

            leftRearAngle(40, 90, 110);
            await sleep(100);
            leftRearAngle(40, 60, 110);
            await sleep(100);

            rightRearAngle(40, 90, 50);
            await sleep(100);
            rightRearAngle(40, 60, 50);
            await sleep(100);

            leftFrontAngle(40, 90, 50);
            await sleep(100);
            leftFrontAngle(40, 60, 50);
            await sleep(100);

            basePosition();
          }

          isStoped = true;
        }

        /**
         * 左旋回
         */
        const turnLeft = async function () {
          while (isTurnLeft) {
            isStoped = false;

            leftFrontAngle(40, 90, 110);
            await sleep(100);
            leftFrontAngle(40, 60, 110);
            await sleep(100);

            rightRearAngle(40, 90, 110);
            await sleep(100);
            rightRearAngle(40, 60, 110);
            await sleep(100);

            leftRearAngle(40, 90, 50);
            await sleep(100);
            leftRearAngle(40, 60, 50);
            await sleep(100);

            rightFrontAngle(40, 90, 50);
            await sleep(100);
            rightFrontAngle(40, 60, 50);
            await sleep(100);

            basePosition();
          }

          isStoped = true;
        }

        /**
         * 右移動
         */
        const right = async function () {
          while (isRight) {
            isStoped = false;

            rightFrontAngle(60, 90, 80);
            await sleep(100);
            rightFrontAngle(60, 80, 80);
            await sleep(100);

            rightRearAngle(40, 90, 80);
            await sleep(100);
            rightRearAngle(60, 80, 80);
            await sleep(100);

            leftFrontAngle(20, 90, 80);
            await sleep(100);
            leftFrontAngle(20, 40, 80);
            await sleep(100);

            leftRearAngle(20, 90, 80);
            await sleep(100);
            leftRearAngle(20, 40, 80);
            await sleep(100);

            basePosition();
          }

          isStoped = true;
        }

        /**
         * 左移動
         */
        const left = async function () {
          while (isLeft) {
            isStoped = false;

            leftFrontAngle(60, 90, 80);
            await sleep(100);
            leftFrontAngle(60, 80, 80);
            await sleep(100);

            leftRearAngle(40, 90, 80);
            await sleep(100);
            leftRearAngle(60, 80, 80);
            await sleep(100);

            rightFrontAngle(20, 90, 80);
            await sleep(100);
            rightFrontAngle(20, 40, 80);
            await sleep(100);

            rightRearAngle(20, 90, 80);
            await sleep(100);
            rightRearAngle(20, 40, 80);
            await sleep(100);

            basePosition();
          }

          isStoped = true;
        }

        /**
         * 後退
         */
        const backward = async function () {
          while (isBackward) {
            isStoped = false;

            if (isBasePosition) {
              leftFrontAngle(40, 90, 100);
              await sleep(100);
              leftFrontAngle(40, 60, 100);
              await sleep(100);

              leftRearAngle(50, 90, 65);
              await sleep(100);
              leftRearAngle(50, 75, 65);
              await sleep(100);
            }

            rightFrontAngle(50, 75, 65);
            leftFrontAngle(40, 60, 80);
            rightRearAngle(40, 60, 100);
            leftRearAngle(40, 60, 80);
            await sleep(100);

            rightFrontAngle(40, 90, 100);
            await sleep(100);
            rightFrontAngle(40, 60, 100);
            await sleep(100);

            rightRearAngle(50, 90, 65);
            await sleep(100);
            rightRearAngle(50, 75, 65);
            await sleep(100);

            rightFrontAngle(40, 60, 80);
            leftFrontAngle(50, 75, 65);
            rightRearAngle(40, 60, 80);
            leftRearAngle(40, 60, 100);
            await sleep(100);

            leftFrontAngle(40, 90, 100);
            await sleep(100);
            leftFrontAngle(40, 60, 100);
            await sleep(100);

            leftRearAngle(50, 90, 65);
            await sleep(100);
            leftRearAngle(50, 75, 65);
            await sleep(100);

            isBasePosition = false;
          }

          isStoped = true;
        }

        basePosition();

        /**
         * 前進ボタン
         */
        $("#Forward").on("mousedown touchstart", async function () {
          $(this).addClass("button-pressed");
          if (isStoped) {
            isForward = true;
            await forward();
          }
        }).on("mouseup mouseleave touchend", async function () {
          $(this).removeClass("button-pressed");
          isForward = false;
        });

        /**
         * 右旋回ボタン
         */
        $("#TurnRight").on("mousedown touchstart", async function () {
          $(this).addClass("button-pressed");
          if (isStoped) {
            isTurnRight = true;
            turnRight();
          }
        }).on("mouseup mouseleave touchend", async function () {
          $(this).removeClass("button-pressed");
          isTurnRight = false;
        });

        /**
         * 左旋回ボタン
         */
        $("#TurnLeft").on("mousedown touchstart", async function () {
          $(this).addClass("button-pressed");
          if (isStoped) {
            isTurnLeft = true;
            turnLeft();
          }
        }).on("mouseup mouseleave touchend", async function () {
          $(this).removeClass("button-pressed");
          isTurnLeft = false;
        });

        /**
         * 右移動ボタン
         */
        $("#Right").on("mousedown touchstart", async function () {
          $(this).addClass("button-pressed");
          if (isStoped) {
            isRight = true;
            right();
          }
        }).on("mouseup mouseleave touchend", async function () {
          $(this).removeClass("button-pressed");
          isRight = false;
        });

        /**
         * 左移動ボタン
         */
        $("#Left").on("mousedown touchstart", async function () {
          $(this).addClass("button-pressed");
          if (isStoped) {
            isLeft = true;
            left();
          }
        }).on("mouseup mouseleave touchend", async function () {
          $(this).removeClass("button-pressed");
          isLeft = false;
        });

        /**
         * 後退ボタン
         */
        $("#Backward").on("mousedown touchstart", async function () {
          $(this).addClass("button-pressed");
          if (isStoped) {
            isBackward = true;
            backward();
          }
        }).on("mouseup mouseleave touchend", async function () {
          $(this).removeClass("button-pressed");
          isBackward = false;
        });
      }
    </script>
  </body>
</html>

完成

結構カッコよくね?
完成品

スマホでの操作画面、コントローラーとカメラ映像ももちろん表示!
スマホでの操作画面

動画
https://twitter.com/yamazin/status/1202192900198518786

感想

obnizは簡単に電子工作できるのでオススメ!
サーボモーター1つの価格は大したことないけど12個も使うと結構高い…

  1. 当時1,923円

  2. 説明の紙すら入ってない

  3. サーボモーターは思ったより高い!

  4. 6個まで行けるらしい

  5. シリアル通信の一種、2本の信号線で1つのマスタから複数のスレーブと通信できる

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?