obnizで作る歯ブラシロボット

このエントリーをはてなブックマークに追加

あの歯ブラシロボットが,世界中どこからでも操作できるようになりました.

歯ブラシロボットはタイヤの代わりにブラシがついていて,ブラシに振動を与えると,毛並みの非対称性から特定の方向に進むという性質を利用したロボットです.
このロボットでは2個の振動モーターをPWM制御し,左右のブラシをそれぞれ操作することで,直進,右折,左折ができます.
さらに前面には距離センサーを搭載し,前方に障害物がないか,スマートフォンの画面でリアルタイムに確認することができます.

操作画面

操作方法

画面のInfoのエリアの色は,前方の障害物までの距離に応じて,遠いほど緑,近いほど赤に変わります.
Manualモードでは,画面のControlエリアのスライダーで左右の振動の強さを調整することで,直進,右左折を自由にコントロールすることができます.ロボットが見えない場所から,画面の色だけを見て操縦してみるのも面白いかもしれません.
PowerをLow, Middle, またはHighにするとAutoモードとなり,普段は直進し続け,障害物に近づくと自動的に右折することで,半永久的に動き続けることもできます.

なお,ブラシは毛先が斜め後ろを向くように癖をつけておく必要があります.
また1台のスマートフォンからたくさんのobnizを同時に操作することもできるので,このようなロボットをたくさん作り,同時に動かすとお互いに作用して面白いかもしれません.

Program

<!-- HTML Example -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script src="https://unpkg.com/obniz@1.5.0/obniz.js"></script>
    <style type="text/css">
        html,
        css {
            touch-action: none;
        }

        input.controller {
            margin-top: 25%;
            -webkit-transform: rotate(270deg);
            -moz-transform: rotate(270deg);
            -o-transform: rotate(270deg);
            transform: rotate(270deg);
            -webkit-border-radius: 24px;
            -moz-border-radius: 24px;
            border-radius: 24px;
        }

        input.controller {
            -webkit-appearance: none;
            width: 90%;
            background: #eee;
            box-shadow: 0 0 10px #666;
            border-radius: 10px;
        }

        input.controller::-webkit-slider-thumb {
            -webkit-appearance: none;
            background: #333;
            width: 50px;
            height: 50px;
            border-radius: 50%;
        }

        input.controller::-webkit-slider-thumb:hover {
            background: #999;
        }

        input.controller::-webkit-slider-thumb:active {
            background: #fefefe;
        }

        .box {
            display: table-cell;
        }
    </style>
</head>

<body>
    <div id="obniz-debug"></div>
    <h3>Info</h3>
    <div id="mode-print"></div>
    <div id="dist-print"></div>
    <div id="print" style="height:100px;"></div>

    <h3>Control</h3>
    <p class="switch">
        Power:
        <br />
        <label>
            <input type="radio" name="power" id="" value=0 onchange="changemode(this.value)">Manual</label>
        <label>
            <input type="radio" name="power" value="60" onchange="changemode(this.value)">Low</label>
        <label>
            <input type="radio" name="power" value="80" onchange="changemode(this.value)">Middle</label>
        <label>
            <input type="radio" name="power" value="100" onchange="changemode(this.value)">High</label>
    </p>

    <div class="sliders">
        <div class="box">
            <label for="slider_pwm1">Left</label>
            <input id="slider_pwm1" type="range" class="controller" min="0" max="100" value="0" />
        </div>
        <div class="box">
            <label for="slider_pwm2">Right</label>
            <input id="slider_pwm2" type="range" class="controller" min="0" max="100" value="0" />
        </div>
    </div>

    <script>
        var obniz = new Obniz("OBNIZ_ID_HERE");
        var mode = 0, pwm, pwm2;
        var power = 0;

        obniz.onconnect = async function () {
            var sensor = obniz.wired("GP2Y0A21YK0F", { vcc: 0, gnd: 1, signal: 2 })
            sensor.start(function (distance) {
                $("#dist-print").text("distance: " + distance + " mm");

                var color_scale = d3.scaleLinear()
                    .domain([0, 200, 400])
                    .range(['red', 'yellow', 'lime'])
                    ;
                changeBoxColor(color_scale(distance));

                if (mode == 1) {
                    if (distance < 150) {
                        $("#mode-print").text("RIGHT");
                        console.log("near");
                        pwm.duty(100);
                        pwm2.duty(0);
                    } else {
                        $("#mode-print").text("GO");
                        console.log("far");
                        pwm.duty(power);
                        pwm2.duty(power);
                    }
                } else {
                    $("#mode-print").text("");
                }
            })

            pwm = obniz.getFreePwm();
            pwm.start({ io: 7 });
            pwm.freq(20);
            obniz.io6.output(false);

            $("#slider_pwm1").on('input', function () {
                pwm.duty($("#slider_pwm1").val())
            });

            pwm2 = obniz.getFreePwm();
            pwm2.start({ io: 5 });
            pwm2.freq(20);
            obniz.io4.output(false);

            $("#slider_pwm2").on('input', function () {
                pwm2.duty($("#slider_pwm2").val())
            });
        };

        obniz.onclose = async function () {
            $("#slider").off('input');
            $("#slider_pwm").off('input');
            $("#slider_pwm2").off('input');
        };

        function changemode(_power) {
            power = _power;
            if (_power != 0) {
                mode = 1;
                $("#slider_pwm1").prop('disabled', true);;
                $("#slider_pwm2").prop('disabled', true);;
            } else {
                mode = 0;
                $("#slider_pwm1").prop('disabled', false);
                $("#slider_pwm2").prop('disabled', false);
                pwm.duty(0);
                pwm2.duty(0);
            }
        }

        function changeBoxColor(newColor) {
            document.getElementById('print').style.backgroundColor = newColor;
        }
    </script>
</body>

</html>

今すぐ実行

HTMLがブラウザで開かれて実行されます。