ロボット 記事一覧

ダンボーハック~よつばと!リボルテックダンボーのロボット化計画―第4章― 回路とプログラム

公開日時:2016/10/27 21:07

よつばと!リボルテックダンボーを改造してみました

ダンボーを Raspberry Pi に接続

さて、これでダンボーのパーツが完成したので、Raspberry Pi に接続します。

まず、ダンボーの組み立て。

ダンボー組み立て図

これで、各電子部品から伸びているジャンパワイヤ―をブレッドボードに接続し、その先を Raspberry Pi に接続していきます。ブレッドボードは余裕を持って2つ使いました。

配線図は以下のとおりです。基本は当サイトの「HAL」で利用している当サイト推奨回路です。

ダンボー回路図

推奨回路との相違点は、赤外線受光モジュールが変わったのでコンデンサの容量を4.7μFに、その前の抵抗値130Ωに替えたこと、ステータス用の LED が単色 LED から RGB LED に変わったので抵抗を470Ω 3本に替えたことです。

注意点として、サーボモーターにつながる電池ボックスのマイナスにつながる線は、必ずGNDにも繋いで下さい。これを忘れると、サーボモーターが発熱して故障し、火事の原因にもなりかねません。

サーボモーターを使う上での注意点

Raspberry Pi では、B+ 以降のモデル(B+、2、3)では、PWM 信号という、デジタルで擬似的にアナログを表現する信号が 2 本まで使えます。

これは GPIO 18番と 19番なのですが、GPIO 制御ライブラリである WiringPi では、GPIO の制御に BCM2835 用のライブラリを利用しているそうで(Wiring Pi参照)、Raspberry Pi の標準音声出力に利用している BCM2835 と共通であるため、サーボモーターの制御用に PWM 信号を利用すると Pi のアナログ端子や HDMI 端子から音声が出力できなくなります。また逆に、サーボモーターを利用できるように GPIO を設定しておくと、音楽を再生すると同時にサーボモーターが不規則に回転してしまいます。

このため、Raspberry Pi にサーボモーターを接続して制御させる場合は、音声出力を USB スピーカーにするか、USB 変換アダプターを利用する、または Bluetooth スピーカーを利用するなどする必要があります。

USB 変換アダプターとしては、次の製品がノイズがほとんど無くおすすめです。

半固定抵抗を使って顔の回転の制御

さて、では顔の制御を行っていきます。

前述のダンボー用回路で、右側に半固定抵抗が2つ設置してあります。これが、ダンボーの顔の水平方向、及び垂直方向の回転をテスト制御するためのマニピュレーターになります。

半固定抵抗はこんなものです。

半固定抵抗|秋月電子通商

抵抗値は10KΩ~50KΩくらいのもので、どれでも構いません。

半固定抵抗は、つまみを回転させることで分圧を行い、出力の電圧を変化させる事ができます。

参考:分圧とは

この半固定抵抗で分圧した電圧値を アナログ/デジタル・コンバーターの MCP3208 で読み取って、その値をサーボモーターの Duty サイクルに変換することで、サーボモーターの回転を制御させます。

注意:サーボモーターを電池ボックスに接続した時に、サーボモーターが常に「ジッジッ…」と音を立てていたり、あるいは変な臭いがするなど、異常がある場合にはすぐに電池ボックスのスイッチを切るか、コードを抜いて下さい。回路が間違っている場合に、このような現象が起き、放置するとサーボモーターが発熱して故障してしまいます。

テストプログラムは以下のとおりです。(ソケットサーバー『HAL』のライブラリを利用しています。このテストプログラムは HAL 内の /apps ディレクトリ内に置くことを想定しています)

サーボのDuty サイクル算出プログラム(HALの組み込みライブラリ)
Servo.php
<?php
/* *
 * 
 * socket server HAL v1.5
 * 
 * (c) 2016 Katsuhiko Miki
 * 
 * */
namespace Feijoa\HAL;

class Servo
{

    public static function getDuty($ratio, $min_msec = 0.5, $max_msec = 2.4, $unit_msec = 20)
    {
        return (int)(1024 * (($min_msec + (($max_msec - $min_msec) * ($ratio / 100))) / $unit_msec));
    }

}
サーボモーターリセットプログラム

サーボモーターをデフォルトの状態にするプログラムです。

※HAL::SPICLK, HAL::OUTPUT…といった定数は、HAL の /HAL/setting/HALSetting.php で定義されています。ReadADCクラスも、HAL 組み込みライブラリです。

servo_rest.php
<?php
namespace Feijoa\HAL;

// 必要なライブラリの読み込み
define("__HALROOT", "/var/www/HAL/");           // HAL までのパスを __HALROOT として定義 
define("__PROG", "SERVO");               // データベース記録用のメタ情報
require(__HALROOT . "setting/requires.php");    // 必要なライブラリの読み込み

$PWM_VERTICAL = 18;
$PWM_HORIZONTAL = 19;

// GPIO セットアップ
wiringPiSetupGpio();

pinMode($PWM_VERTICAL, HAL::PWM);
pinMode($PWM_HORIZONTAL, HAL::PWM);

pwmSetMode(HAL::PWM_MODE_MS);

pwmSetClock(375);  // 50 Hz。ここには 18750/(周波数) の計算値に近い整数を入れる

$duty_horizontal = Servo::getDuty(50, 0.5, 2.4, 20);
pwmWrite($PWM_HORIZONTAL, $duty_horizontal);

$duty_vertical = Servo::getDuty(50, 0.5, 2.4, 20);
pwmWrite($PWM_VERTICAL, $duty_vertical);

usleep(10000);

exit;

上記の Servo クラスは、第1引数で与えた割合を、サーボモーターを制御するための値に変換するためのクラスです。

第1引数にサーボを回転させたい比率、第2引数に比率 0 の時のそのサーボモーターでの最低 HIGH 時間、第3引数に比率 100 の時のそのサーボモーターでの最高 HIGH 時間、第4引数にユニット長を指定します。

上記サンプルでは 0.5ms、2.4ms、20ms を指定していますが、これはSG90の仕様書を参考にしています。

今回のダンボーハックで使っているサーボモータ―は別のものですが、仕様書が見当たらなかったため、試しに SG90 の使用で動かしてみた所、特に問題が見受けられませんでしたのでそのまま利用しました。

このリセットプログラムを下記のように実行し、

sudo php -q /var/www/HAL/apps/servo_reset.php

サーボモータの初期値の時に顔が正面を向くよう、サーボモーターの軸とサーボホーンの位置を合わせてください。

サーボモーターの回転テスト

サーボモーターの初期化の方法が分かったら、次はサーボモーターの回転可能値の取得です。

テストプログラムは以下です。

servo.php
<?php
namespace Feijoa\HAL;

// 必要なライブラリの読み込み
define("__HALROOT", "/var/www/HAL/");           // HAL までのパスを __HALROOT として定義
define("__PROG", "SERVO");               // データベース記録用のメタ情報
require(__HALROOT . "setting/requires.php");    // 必要なライブラリの読み込み

$PWM_H = 18;
$PWM_V = 19;

// GPIO セットアップ
wiringPiSetupGpio();

// SPI通信用の入出力を定義
pinMode(HAL::SPICLK, HAL::OUTPUT);
pinMode(HAL::SPIMOSI, HAL::OUTPUT);
pinMode(HAL::SPIMISO, HAL::INPUT);
pinMode(HAL::SPICS, HAL::OUTPUT);

pinMode($PWM_H, HAL::PWM);
pinMode($PWM_V, HAL::PWM);

pwmSetMode(HAL::PWM_MODE_MS);

pwmSetClock(375);  // 50 Hz。ここには 18750/(周波数) の計算値に近い整数を入れる

$dutyH = Servo::getDuty(50, 0.5, 2.4, 20);
pwmWrite($PWM_H, $dutyH);

$duty_V = Servo::getDuty(50, 0.5, 2.4, 20);
pwmWrite($PWM_V, $duty_V);

while(true)
{
    $val0 = ReadADC::exec(4);
    $dutyH = Servo::getDuty($val0, 0.5, 2.4, 20);
    pwmWrite($PWM_H, $dutyH);

    $val1 = ReadADC::exec(5);
    $duty_V = Servo::getDuty($val1, 0.5, 2.4, 20);
    pwmWrite($PWM_V, $duty_V);

    usleep(200000);

    echo "{$dutyH} : {$val0} % / {$duty_V} : {$val1} %\n";
}

これは、半固定抵抗の値を MCP3208 で読み取って、その値をそれぞれ水平、垂直方向の回転用のサーボモーターの回転量に変換するプログラムです。

実際のテスト状況はこちらです。

このようにして、サーボモーターに無理のない可動範囲(%)を調べて下さい。「無理な状態」とは、ボディーやケーブルと干渉して回転できず、サーボモーターが「ジッジッ…」と音を立てたままの状態になることです。この場合、サーボモーターに負荷がかかり続けているので、音がしなくなるまでを可動範囲としてください。

顔認識

サーボモータの可動範囲が分かったら、次は顔認識です。せっかくダンボーの右目にカメラを埋め込んでいるので、人間の顔を認識したらそちらを向く処理を追加しましょう。

人間の顔認識には、OpenCV というオープンソースライブラリを使うのがお手軽で便利です。

まず、OpenCV をインストールできるように、Raspbian の パッケージリストを update します。

sudo apt-get update

update は、OS のパッケージリストの更新です。これを行うことで、最新で最適なパッケージの参照先が更新されます。

この状態で

sudo apt-get upgrade

を行うと、お使いの OS に既にインストールされているプログラムについても全て最新で最適な物に更新されます。

update を行ってから

sudo apt-get install libopencv-dev

を行うと、OpenCV での開発に必要なプログラムがインストールできます。この後で、python用の opencv ライブラリをインストールします。

sudo apt-get install python-opencv

3、4 年前には、PHP でも簡単に OpenCV を利用するためのライブラリが提供されていたのですが、時代の移り変わりは早いもので、今日現在(2016/10/27)簡単に OpenCV を利用するための PHP 用のライブラリがみつかりませんでした。

本サイトの Raspberry Pi の記事は「お手軽」を趣旨としてるので、ここは Python のライブラリを利用することにします。

さて、Python での OpenCV の利用準備が整ったら、後はプログラムです。

まず、Python での顔認識プログラムですが、これは、画像ファイルから顔の位置と大きさを認識するプログラムです。

設置場所は、やはり、/var/www/HAL/apps を想定しています。

hal_eye.py
# -*- coding: utf-8 -*-
import cv2
import sys
# import os
cascade_path = "/var/www/HAL/apps/haarcascade_frontalface_alt2.xml"

devide = 1

color = (255, 255, 255) # color of rectangle for face detection

def face_detect(file):
    image = cv2.imread(file)
    image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    image_gray = cv2.resize(image_gray, (image.shape[1]/devide, image.shape[0]/devide))

    facerect = cascade.detectMultiScale(image_gray, scaleFactor=1.1, minNeighbors=1, minSize=(1, 1))

    if len(facerect) > 0:
        rect = facerect[0]
        cv2.rectangle(image_gray, tuple(rect[0:2]),tuple(rect[0:2]+rect[2:4]), color, thickness=2)

        rect *= devide
        height, width, channels = image.shape[:3];
        print str(rect[0]) + "," + str(rect[1]) + "," + str(rect[2]) + "," + str(rect[3]) + "," + str(width) + "," + str(height)

    else:
        print "";

    return image_gray

if __name__ == '__main__':
    param = sys.argv
    if (len(param) != 2):
        quit()  

    cascade = cv2.CascadeClassifier(cascade_path)

    output_img = face_detect(param[1])

    cv2.imwrite('/mnt/ramdisk_hal/hal_detect.jpg', output_img)

上記の

cascade_path = "/var/www/HAL/apps/haarcascade_frontalface_alt2.xml"

ですが、haarcascade_frontalface_alt2.xml は、OpenCV 用の汎用顔認識定義ファイルです。ソースコードをダウンロードすると中に同梱されているようですが、Source Forgeから以下でデウンロード出来るようです。(ただのXMLファイルです)

wget wget -O /var/www/HAL/apps/haarcascade_frontalface_alt2.xml http://opencvlibrary.svn.sourceforge.net/viewvc/opencvlibrary/trunk/opencv/data/haarcascades/haarcascade_frontalface_alt2.xml?revision=128

これで、人間の顔が写っている写真のパスをこの Python プログラムに第一引数として渡せば、顔の部分を認識して白枠で囲い、左上の座標、矩形の大きさ、画像自体の縦横のサイズを標準出力に出力した後、上記プログラムの最後の行にある /mnt/ramdisk_hal/hal_detect.jpg として出力してくれます。

/mnt/ramdisk_hal は、ソケットサーバー『HAL』で利用する汎用 RAM ディスクですので、自由にディレクトリを変えてもらってもかまいません。

なお、この Python用の OpenCV ライブラリでの顔認識は、かなり遅いです。100×100pixcels の画像で 0.5 秒程です。この後、PHP から raspistill コマンドを呼び出してカメラモジュールでの撮影からファイル書き出しまでを行いますが、こちらも 0.5 秒ほどかかるので、トータルで秒間 1 コマ程度のパフォーマンスしか出ないことは予めご了承ください。

Python の顔認識プログラムを PHP から呼び出す

さて、顔認識プログラムの準備が出来たら、これを PHP から呼びます。

Raspberry Pi のカメラモジュールは raspistill コマンドで写真撮影がで出来、-o オプションと -e オプション等で、出力ファイルの設定値を指定できます。

このように写真撮影してファイルを出力し、PHP から 出力ファイル情報を Python に渡して実行します。

hal_eye.php
<?php
namespace Feijoa\HAL;

// 必要なライブラリの読み込み
define("__HALROOT", "/var/www/HAL/");           // HAL までのパスを __HALROOT として定義 
define("__PROG", "HAL_EYE");               // データベース記録用のメタ情報
require(__HALROOT . "setting/requires.php");    // 必要なライブラリの読み込み

while(true)
{
    exec("sudo raspistill -o /mnt/ramdisk_hal/hal_eye.jpg -t 1 -w 100 -h 100 -e jpg -n");

    $value = shell_exec("sudo python /var/www/HAL/apps/hal_eye.py /mnt/ramdisk_hal/hal_eye.jpg");

    file_put_contents("/mnt/ramdisk_hal/hal_eye.data", $value);
}

shell_exec() は、Python からの標準出力値を PHP の変数に格納します。その後の file_put_contents() で、変数に格納された情報をそのままファイルとして書き出しています。

この書き出された値を、実際のサーボコントロール用のプログラムで読み込み、サーボモーターを回転させます。

顔認識状況の確認

ところで、これまでのプログラムで本当に顔が認識できているのか、実際に書き出された画像で確認してみたくなります。

/mnt/ramdisk_hal/hal_eye.jpg

に、認識画像が jpeg として書き出されていますので、これを WEB ブラウザでモニタリングしてみましょう。Apache などの WEB サーバーが起動していれば、ブラウザから簡単にこの画像をリアルタイム(といっても、秒間 1 コマ程度)できます。

まず、Apache 等の WEB サーバーのドキュメントルート下に、以下の HTML ファイルを作成します。

eye.html
<html>
    <head>
        <title>MyCamera1</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
</script>
<script>
    var temp = $("<img/>");

setInterval(function(){
    $.ajax({
        type:   "get",
        url:    "eye.php"
    })
    .done(function(result){
        temp.on("load", function(){
            $("#my-camera").attr("src", temp.attr("src"));
        });
        temp.attr("src",  result);
    })
}, 500);

</script>
    </head>
    <body>
        <img id="my-camera" src="">
    </body>
</html>

次に、上記 html の JavaScript から呼び出される PHP プログラム(画像ファイルを出力する)を作成します。

eye.php
<?php
$img = base64_encode(file_get_contents('/mnt/ramdisk_hal/hal_detect.jpg'));
echo "data:image/jpeg;base64,{$img}";

jQuery の Ajax でも簡単に通信できるよう、画像ファイルを Base64 エンコードして出力しています。

これで、WEB ブラウザから eye.html にアクセスすると、顔認識状況を画像で確認できます。例えば、http://192.168.1.**/eye.html などですね。

※Raspberry Pi をインターネットに公開している場合は、お部屋の中が筒抜けになってしまうので十分注意してください。

顔の回転制御プログラム

顔の回転を制御するプログラムは以下です。

場所はやはり、/var/www/HAL/apps 内を想定しています。かなり長いフラットなプログラムです。

global 宣言を多用していて無駄が多いのがおわかりいただけるでしょう。本来はクラスを作りたいところですが、何をしているのか把握しやすいように、敢えてフラットな設計にしてみました。あくまでもサンプルプログラムという位置づけです。

head_control.php
<?php
namespace Feijoa\HAL;

// 必要なライブラリの読み込み
define("__HALROOT", "/var/www/HAL/");           // HAL までのパスを __HALROOT として定義 
define("__PROG", "HEAD_CONTROL");               // データベース記録用のメタ情報
require(__HALROOT . "setting/requires.php");    // 必要なライブラリの読み込み

/**
 * 変数定義
 */

// PWM用GPIOピン番号
$PWM_H = HAL::PWM_H;    //18;
$PWM_V = HAL::PWM_V;    //19;

// サーボモーターの最小・最大duty(ms)
$servo_min = HAL::SERVO_MIN;    //0.5;
$servo_max = HAL::SERVO_MAX;    //2.4;

// サーボモーターのPWM Period(ms)
$servo_period = HAL::SERVO_PERIOD;      //20;

// デフォルトダンボー顔位置率(%)
$now_H = 50;
$now_V = 55;

// 顔認識→顔回転率変換用除算数
$divide_h = 10;
$divide_v = 10;

// 顔回転ステップ数
$step = 1;

// ループ内での顔追跡フラグ
$find_flag = false;

// 顔向け中フラグ
$status_find = false;

// ダンボー顔左右最大回転率
$h_range = 19;

// ダンボ―顔上方向最大回転率
$v_range1 = 6.5;

// ダンボ―顔下方向最大回転率
$v_range2 = 10;

// 顔探索方向
$h_vector = -1;

// 顔探索フラグ
$seek_flag = false;

// LED発光済みフラグ
$led_flag = false;

// 顔探索カウンター(左右1回振りワンセット)
$seek_count = 0;

// 顔探索サーボ回転インターバル(マイクロ秒)
$seek_interval = 50000;

// 顔探索間隔(秒)
$seek_time_distance = 60;

// 次回顔探索開始時間(unixタイムスタンプ)
$seek_weit = time();

// PWM信号実行最低インターバル(マイクロ秒)
$pwm_interval = 20000;

// 顔をデフォルトに戻す場合の顔非検出時間(秒)
$face_reset_interval = 10;

// 顔をデフォルト位置に戻すタイマー
$face_reset_timer = time() + $face_reset_interval;

/*
 * プログラム
 */

// GPIO セットアップ
wiringPiSetupGpio();

pinMode($PWM_V, HAL::PWM);
pinMode($PWM_H, HAL::PWM);

pwmSetMode(HAL::PWM_MODE_MS);

pwmSetClock(375);  // 50 Hz。ここには 18750/(周波数) の計算値に近い整数を入れる

$duty_horizontal = Servo::getDuty($now_H, $servo_min, $servo_max, $servo_period);
pwmWrite($PWM_H, $duty_horizontal);
$before_duty_h = $duty_horizontal;

$duty_vertical = Servo::getDuty($now_V, $servo_min, $servo_max, $servo_period);
pwmWrite($PWM_V, $duty_vertical);
$before_duty_v = $duty_vertical;

$before_x = (int)(HAL::DANBOARD_VISION_SIZE / 2);
$before_y = (int)(HAL::DANBOARD_VISION_SIZE / 2);
$before_w = 0;
$before_h = 0;

while(true)
{
    if(!file_exists(HAL::RAMDISK . "hal_eye.data"))
    {
        usleep($pwm_interval);
        continue;
    }

    $csv = trim(file_get_contents("/mnt/ramdisk_hal/hal_eye.data"));
    if(empty($csv))
    {
        usleep($pwm_interval);

        if($status_find === true && $face_reset_timer < time())
        {
            reset_face_position();
            $status_find = false;
            continue;
        }

        if($seek_weit > time()){ continue; }

        //seek();
        //usleep($seek_interval * 10);

        continue;
    }

    // 顔認識
    $status_find = true;

    // 顔をデフォルト位置に戻すタイマー設定
    $face_reset_timer = time() + $face_reset_interval;

    // 顔認識時LEDをイエローで点灯
    if($seek_flag === true && $led_flag === false)
    {
        $led_flag = true;
        flash_led();
    }

    // シーク開始タイムを延長
    $seek_weit = time() + $seek_time_distance;

    $data = explode(",", $csv);
    $x = (int)$data[0];
    $y = (int)$data[1];
    $w = (int)$data[2];
    $h = (int)$data[3];
    $img_w = (int)$data[4];  // 画角横
    $img_h = (int)$data[5];  // 画角縦

    // 顔向け済みチェック
    if($before_x === $x && $before_y === $y && $before_w === $w && $before_h === $h){ continue; }

    // 画像上の顔認識中心座標
    $x_center = $x + ($w / 2);
    $y_center = $y + ($h / 2);

    // 画像中心からのオフセットピクセル
    $pix_H = ($img_w / 2) - $x_center;
    $pix_V = ($img_h / 2) - $y_center + 10;

    // 首振り幅を指数関数で求める
    $H_range = (int)round(pow(abs($pix_H), 1.65) / 125);

    // ダンボー顔回転率(%)
    $x_adjust = ($pix_H / $divide_h) * $H_range;
    $y_adjust = ($pix_V / $divide_v) * 3;

    //echo "\n";

    $find_flag = false;

    // 水平方向回転
    control_H($x_adjust);

    // 垂直方向回転
    control_V($y_adjust);

    // LED発光
    if($find_flag === true){ flash_led(); }

    // 認識情報保持
    $before_x = $x;
    $before_y = $y;
    $before_w = $w;
    $before_h = $h;

    usleep($pwm_interval);
}

/**
 * 自動で顔を左右に振り、人間の顔を探す
 */
function seek()
{
    global $seek_flag;
    global $led_flag;
    global $h_range;
    global $v_range1;
    global $v_range2;
    global $h_vector;
    global $PWM_H;
    global $PWM_V;
    global $now_H;
    global $seek_count;
    global $seek_weit;
    global $seek_time_distance;
    global $servo_min;
    global $servo_max;
    global $servo_period;
    global $seek_interval;

    $seek_flag = true;
    $led_flag = false;

    for($i=0; $i<5; $i++)
    {    
        // 垂直方向
        $now_V = 50;
        $val_v = max(50 - $v_range2, min(50 + $v_range1, ($now_V + ($v_range1 * 0.75))));
        $duty_v = Servo::getDuty($val_v, $servo_min, $servo_max, $servo_period);
        pwmWrite($PWM_V, $duty_v);

        // 水平方向
        $now_H += $h_vector;
        $seek_h = max(50 - $h_range, min(50 + $h_range, $now_H));
        $duty_h = Servo::getDuty($seek_h, $servo_min, $servo_max, $servo_period);
        pwmWrite($PWM_H, $duty_h);
        //echo "seek: {$seek_h}% / duty: {$duty_h} \n";

        if(abs(50 - $now_H) > $h_range)
        {
            $h_vector *= -1;
            $seek_count++;
            if($seek_count > 1)
            {
                // 顔を正面に戻す
                reset_face_position();

                $seek_count = 0;
                $seek_weit = time() + $seek_time_distance;
                break;
            }
        }
        usleep($seek_interval);
    }
}

/**
 * 自動で顔を中央に戻す
 */
function reset_face_position()
{
    global $seek_flag;
    global $h_range;
    global $h_vector;
    global $PWM_H;
    global $PWM_V;
    global $now_H;
    global $servo_min;
    global $servo_max;
    global $servo_period;
    global $seek_interval;

    $duty_v = Servo::getDuty(55, $servo_min, $servo_max, $servo_period);
    pwmWrite($PWM_V, $duty_v);

    $h_vector = -1;
    if($now_H <= 50)
    {
        $h_vector = 1;
    }

    while(true)
    {
        if((int)round($now_H) === 50){ $seek_flag = false; break; }

        $now_H += $h_vector;
        $seek_h = max(50 - $h_range, min(50 + $h_range, $now_H));
        $duty_h = Servo::getDuty($seek_h, $servo_min, $servo_max, $servo_period);
        pwmWrite($PWM_H, $duty_h);

        usleep($seek_interval);
    }
    return;
}

/**
 * 水平方向の顔追従制御
 */
function control_H($x_adjust)
{
    global $h_range;
    global $h_vector;
    global $PWM_H;
    global $now_H;
    global $step;
    global $servo_min;
    global $servo_max;
    global $servo_period;
    global $find_flag;
    global $before_duty_h;

    // 水平方向
    $val_h = max(50 - $h_range, min(50 + $h_range, ($now_H - ($x_adjust / $step))));
    $duty_h = Servo::getDuty($val_h, $servo_min, $servo_max, $servo_period);
    //echo "val_H: {$val_h} / duty_H: {$duty_h}\n";
    if($before_duty_h !== $duty_h){
        pwmWrite($PWM_H, $duty_h);
        $now_H = $val_h;
        $before_duty_h = $duty_h;
        $find_flag = true;
    }
    //echo  "h= adjust: {$x_adjust} now_H: {$now_H} duty_H: {$duty_h}\n";
}

/**
 * 垂直方向の顔追従制御
 */
function control_V($y_adjust)
{
    global $v_range1;
    global $v_range2;
    global $PWM_V;
    global $now_V;
    global $step;
    global $servo_min;
    global $servo_max;
    global $servo_period;
    global $find_flag;
    global $before_duty_v;

    // 垂直方向
    $val_v = max(50 - $v_range2, min(50 + $v_range1, ($now_V + ($y_adjust / $step))));
    $duty_v = Servo::getDuty($val_v, $servo_min, $servo_max, $servo_period);
    //echo "val_V: {$val_v} / duty_V: {$duty_v}\n";
    if($before_duty_v !== $duty_v){
        pwmWrite($PWM_V, $duty_v);
        $now_V = $val_v;
        $before_duty_v = $duty_v;
        $find_flag = true;
    }
    //echo  "v= adjust: {$y_adjust} now_V: {$now_V} duty_V: {$duty_v}\n";
}

/**
 * LED発光
 */
function flash_led()
{
    exec("sudo php -q ". dirname(__FILE__) ."/../system/executable/status_yellow.php");
}

以下 3 項目が、サーボモーターの回転制御値(%)です。サーボモーターに無理がかかって「ジッジッ…」と音がなり続けてしまう場合は、下記の数値を小さくして下さい。

// ダンボー顔左右最大回転率
$h_range = 19;

// ダンボ―顔上方向最大回転率
$v_range1 = 6.5;

// ダンボ―顔下方向最大回転率
$v_range2 = 10;

このプログラムはすこし複雑(難しいではなく、単に複雑である)事をしていますが、やっている事自体はとても簡単です。

要所は下の 3 行です。

$seek_h = max(50 - $h_range, min(50 + $h_range, $now_H));
$duty_h = Servo::getDuty($seek_h, $servo_min, $servo_max, $servo_period);
pwmWrite($PWM_H, $duty_h);

1 行目で、最大値・最小値以内の回転率を求め、2 行目でその値をサーボモーターで有効な値のデジタル値に変換し、3 行目で指定の GPIO ピンから出力する PWM 信号の値を決めます。

プログラムの前半部分では file_get_contents("/mnt/ramdisk_hal/hal_eye.data") で、Python で認識した顔情報(座標、矩形の大きさ、画像サイズ)を取得し、その値に合わせて顔の回転を行う命令を後半の各関数に指示する、という流れです。

まとめ

以上のような感じで、一通りの動作確認が行えるはずです。

あとは、実際にダンボーにどのような動作や認識をさせるか、という段階になってきますので、ソケットサーバー『HAL』を使って、自由な機能追加を行ってみて下さい。

今後公開されるソケットサーバー『HAL』には、ダンボー用のプログラムも同梱される予定です。

なお、今回の顔認識で OpenCV が導入できました。OpenCV を使うと、raspistill コマンド無しで、カメラモジュールから直接画像の取得ができそうです。

ですから、顔認識速度についてはまだまだ改良の余地が十分あることを最後に申し上げておきます。

ちなみに、今回のダンボーハックの舞台裏はこんな感じです。病院の集中治療室に入院した患者さんみたいになってますね…

ダンボーの裏側

ダンボーハック~よつばと!リボルテックダンボーのロボット化計画―第3章― スカウター

公開日時:2016/10/27 21:06

よつばと!リボルテックダンボーを改造してみました

スカウターの作成

次は、広角レンズ用のマウンター(スカウター)を作ります。

ダンボーハックの第1章で、ダンボーの右目には Raspberry Pi 用のカメラモジュールが埋め込んでありますが、このカメラ、画角は思いの外狭いです。

下図が私の机周りの状況です。

机周り

この状態で普通にカメラからの視界を見ると

通常の画角

こんな感じで、顔が画角の外になってしまいます。

そこで、広角レンズを装着してみました。

広角レンズを装着

このように、顔がカメラの視界内に入ってきます。

と、いうわけで、この章では広角レンズを取り付けるためのマウンター(スカウター)の作り方をご説明します。

広角レンズの入手方法

では、広角レンズをどうやって入手するか、ですが、iPhoneが普及してくれたおかげで、安価に広角レンズが入手できるようになっています。

私が購入したレンジはこちらです。

広角、マクロ、魚眼レンズの3点セット(広角はマクロレンズと併用して使います)です。これが¥1000円ほどで手に入るのは、とてもお買い得ですね。

マウンターの制作

レンズが手に入ったら、後はそれをダンボーの頭に設置するためのマウンター制作です。

マウンターには、第2章で使ったプラ板セットのうちの、1.2mm の厚いプラ板を利用します。

前述のマクロレンズ用のマウンターの設計図は以下です。

マウンターの設計図

円形の切り出しがありますが、ここはアバウトでいいので小さめに切り出してから、半丸ヤスリで削って広げます。

アタリ線

削り出し中

こうしてある程度削り出したら、2つのパーツに切り分けて、接合部分を絶縁ビニールテープで貼り合わせて更に微妙な削りだしを行いながら、レンズがダンボーの目の位置にくるように調整します。

この際、レンズが少し両目の内側にくるくらいまでで止めておいてください。後で2つのプラ板をパテで固めることになりますが、位置の微調整が必要な時に、さらにプラ板を削ることはできますが、足りない分を盛るのはより大変になるからです。

また、最終的にスカウターは両面テープで固定することになるので、両面テープの厚さ(0.75mm程)が、スカウターと顔側面の貼付け部分の間に入ることも考慮してください。

削りだしから位置調整が終わったら、パテで固めます。

パテで固定

パテが固まったら両面テープを貼り付けて、ダンボーの顔に貼り付けてレンズの位置を調整します。黒いのが両面テープ(0.75mm厚)です。ホームセンター等で買い求めて下さい。この両面テープは、ダンボーの足の下の円形の台座を固定するのにも使います。これがないと、首のサーボモータの回転により作用・反作用の法則でダンボー全体が回転してしまいます。

両面テープ貼り付け

顔に貼り付けてレンズの位置調整

位置が決まったらマウンターにTS-46を吹いて着色して完成です。

スカウター装着後

ダンボーハック~よつばと!リボルテックダンボーのロボット化計画―第2章― ボディー

公開日時:2016/10/27 21:05

よつばと!リボルテックダンボーを改造してみました

ダンボーのボディーを作る

さて、次はボディーです。ダンボーのボディーには、赤外線受光モジュールと人感センサーを埋め込みたいのですが、「リボルテックダンボー」のボディは密閉されてしまっていて内部にアクセスできません。

そこで、プラ板とエポキシパテ、紙粘土を使って 1 から作ることにしました。

エポキシパテですが、別にこのパテに拘る必要はありません。私はあまり詳しくないので「プラモといえば TAMIYA」という子供の頃からのイメージで選定しました。

上記人感センサーには 2 つのオレンジ色のつまみ(半固定抵抗)がついていますが、半固定抵抗が手前に来るようにして基盤を裏から見た時、左側が感度調節で、右側が出力保持時間です。感度調節を最大にしておくと、10m くらい離れていても人体の赤外線を感知してくれます。出力時間は大きくするほどHIGH、LOWそれぞれの検出時間が長くなります。

赤外線受光モジュールは Amazon で購入した物が上手く動作しなかったので、秋月電子通商で次の物を仕入れて使いました。

赤外線リモコン受信モジュールPL-IRM1261-C438

赤外線リモコン受信モジュールPL-IRM1261-C438

電磁シールド付きで、その分だけ大きな穴を開ける必要があります(φ6mm)。もし、別の受光モジュールで、電磁シールド無しのものを利用する場合、この穴はφ5mmで良いかもしれません。

そしてここがいちばん重要。ダンボーの特徴である「お金投入口」を作るため、ホームセンターで 直径5mm のスリ割付止ネジを購入しました。プラスドライバー用のネジは沢山ありますが、マイナスドライバー用のネジってあまりないですね。多分コイツを見つけるのが一番大変かと思います。

スリ割付ネジ

これを使いましたが、Amazon での取扱は 5 パック 1 セットなのでかなり高くなってしまいます。近場のホームセンターで見つけたいところです。

あとは塗装用のスプレーです。TS-46 がほぼ、リボルテックダンボーのボディーカラーと一緒でした。

設計図は下記です。

ダンボーボディー設計図

厚さ 0.5mm のプラ板に細い油性マジックで、上記設計図通りのアタリ線を引いていきます。この線を引いたほうがダンボーの胴体の内側になります。油性マジックの黒い線は塗装用のスプレー TS-46 を吹いても透けてしまうため、外側にマジックの線がこないようにしています。

注意点として、「リボルテックダンボー」の胴体の横幅は 33mm なのですが、埋め込む人感センサーモジュールの横幅が32mmなため、2mm 大きくして35mm にしています。

ボディーアタリ線

切り出し後

ボディーを切り出したら、電動ドリルを使って各穴を開けますが、この時は、下に木っ端を敷いてください。下に空間がある状態で無理にドリルで穴を開けると、穴の周囲に亀裂が入ってしまいます。

穴を開けたら折り目に沿って、プラ板の内側に、切り離さない程度にカッターで線を入れておきます。こうしておくと、キレイにプラ板を折りやすくなります。

その後でエポキシパテを内側の折り目に沿って塗っていきます。エポキシパテは 2 材を混ぜ合えわせることで固まるようになります。

パテを盛る

感じとしては、少し固いトリモチのような粘着質な手触りです。手にパテがついてくると余計にベタベタと貼り付くようになり上手く伸ばしにくくなるので、そうなったら一旦手を洗って指先についているパテを落としましょう。そうすると手に貼りつかなくなって再び伸ばしやすくなります。

パテは硬化するとカッターやヤスリで削り落とせるようになります。

上記エポキシパテが硬化するのにかかる時間は 6 時間程です。ですから慌てる必要はありません。

パテを折り目に沿って盛り終わったら、実際にプラ板を折り曲げて箱型を作っていきます。

プラ板を折り曲げる

プラ版を折り曲げると簡単にプラ板にヒビが入って割れてしまいますが、パテを盛ってあるのでプラ板同士はつながったままです。

箱型を作ったら軽く輪ゴムを巻いて固定し、パテが乾くまで 6 時間程放置します。ただし、最初の 1 時間ぐらいはたまに様子を見て、箱型が型くずれしていないかチェックしてください。パテを盛ってから 2 時間位は微調整ができます。

このようにして箱型を作ったら、ボディーの外側の折り目に沿って、同様にパテを盛って行きます。

ボディーの外側にもパテを盛る

ボディーの裾の部分は外側にフレア状に開くようにしてパテを盛ります。また、裾の角はヤスリを軽くかけてラウンド加工をしておきます。

その後は再び 6 時間放置して乾くのを待ちます。

固まったら上下ひっくり返して、ボディー上部の折り目にもパテを盛ります。

ボディー上部の外側の折り目を盛る

ボディー底の組み立て

ボディー底部分も、ボディーと同様にパテを盛ります。次の写真では内側にテストでスプレーを吹いてありますが、スプレーしない方がパテを盛るのに都合がいいでしょう。

ボディー底部分

ボディー底部分のパテ盛り

ボディー上部の内側に厚いプラ板を仕込む

ボディーのパテが乾いたら、1.2mm 厚のプラ板をボディー上部の内側にパテで貼り付けます。

1.2mm厚のプラ板

このプラ板は、顔水平方向回転用サーボモーターのサーボホーンを固定するための補強材です。

1.2mm厚プラ板の貼り付け

盛ったパテを削り落とす

完全にパテが乾いて硬化したら、盛ったパテをカッターとヤスリを使って削り落とし、平らにしていきます。

カッターで削ぎ落とす場合は、カッターの刃先に指などがこないように注意して下さい。大怪我の元になります。ヤスリの平な面で削った方が安全です。

パテ削り落とし1

パテ削り落とし2

パテ削り落とし3

パテ削り落とし4

ボディーの内側は、半丸ヤスリの平らな面と丸い面を使い分けて削ってください。削り方としては、次のように半丸ヤスリでラウンド加工すると良いでしょう。キレイに削ったら、人感センサーが上手く嵌まるか当ててみて削りを調節してください。

パテ削り落とし5

パテ削り落とし6

キレイに削り終わったら、スプレー TS-46 で塗装します。

スプレー後

ボディーに部品を埋め込む

まず、ボディー上部にサーボホーンを取り付けます。

次の写真のようにサーボホーンがボディー上部の中央に来るように絶縁ビニールテープで固定し、ホーンの端から 2 番めの穴にビスを軽くねじ込んで目印をつけます。目印をつけたら一旦ホーンを外してから、目印の場所にビスを深くねじ込んで内側に貼り付けてある 1.2mm 厚のプラ板まで貫通させます。2 本共貫通させたら、ネジを外してサーボホーンを再びあて、ビスでホーンを固定します。

サーボホーン仮止め

ビス位置確認

ビスでサーボホーンを固定する

紙粘土を盛って、パーツを固定する土台を作る

次は、左腕のリボルバージョイントとコイン投入口用ネジの固定部分を作ります。

まず、紙粘土をボディーの上部左半分に押し込み、左腕のリボルバージョイントとコイン投入口を埋め込みます。

粘土を盛る

パーツの埋め込み

次に、赤外線受光モジュールにジャンパワイヤを繋ぎ、ボディー上部右半分に紙粘土を盛って赤外線受光モジュールと右腕のリボルバージョイントを埋め込みます。

まず、赤外線受光モジュールの脚を適切な長さに切りそろえます。

赤外線受光モジュール

次にジャンパワイヤーを接続して、赤外線モジュールの脚の付け根のアタリで折り曲げます。

脚折り曲げ

脚を折り曲げたら受光モジュールが外れないよう、ジャンパワイヤ―の黒いハウジングごと、絶縁ビニールテープで巻いて固定します。

赤外線受光モジュールの固定

あとはこれを紙粘土で左半身と同様に右のムネに埋め込みます。

受光モジュールの固定1

受光モジュールの固定2

この時、次の赤色の部分に「ふわっと軽いねんど」で、スペースをつくってねんどが固まってから、紙粘土で固定した方が良いです。作ったあとに気づいたのですが、紙粘土は固まるとカチコチになってしまうため、後で問題に気づいた時に赤外線受光モジュールが取り出せなくなってしまいます。

「ふわっと軽いねんど」でスペーサーを作っておけば、この部分を崩すことで赤外線受光モジュールが取り出せるようになります。

ふわっと軽いねんどでスペーサーを作る

そうして紙粘土を盛ったら、右腕のリボルバージョイント差し込みます。

右腕のリボルバージョイント

ボディー底面のパーツにも紙粘土を盛り、大腿部用のリボルバージョイントを埋め込みます。

ボディー底1

ボディー底2

この状態で 1 日ほど放置し、紙粘土が固くなるのを待ちます。

お腹に人感センサーを埋め込む

まず、人感センサーですが、白いドーム状のフレネルレンズを取り外します、このレンズは4つの脚をモジュール部分の穴に差し込んであるだけなので、軽く引き抜けます。

フレネルレンズを取り付けたままセンサーをねじ込もうとすると、どうやってもパテがパキッと割れてしまうので、必ず取り外しましょう。

フレネルレンズ取り外し

この人感センサーは、メラミンスポンジの膨張力で固定します。このため、万一モジュールが発熱してもメラミンスポンジが溶けてしまわないよう、人感センサーモジュールの裏側の基盤に絶縁ビニールテープを貼っておきます。

基盤に絶縁ビニールテープを貼る

また、人感センサーの四隅にある電解コンデンサが万一発熱しても大丈夫なように、メラミンスポンジの側面にも絶縁ビニールテープを巻いておきます。

メラミンスポンジの側面にテープを貼る

こうしてからダンボーボディーに人感センサーを埋め込みます。

人感センサー埋め込み

次の写真のように腰に開けた穴からジャンパワイヤーを通して外に出しておきます。

ジャンパワイヤーを通す1

ジャンパワイヤーを通す2

最後に底部分を取り付け、絶縁ビニールテープで貼り付けて固定します。

底部の固定1

底部の固定2

なお、大腿部のリボルバージョイントと底部の紙粘土の接合は、何度も取り外ししていると紙粘土が削れてゆるくなってしまいます。この場合は「ふわっと軽いねんど」を少量リボルバージョイントを差し込む穴の中にねじ込んでからリボルバージョイントを刺し、粘土が乾くまで放置するとキッチリ嵌まるようになります。

ダンボーハック~よつばと!リボルテックダンボーのロボット化計画―第1章― 頭部

公開日時:2016/10/27 21:04

よつばと!リボルテックダンボーを改造してみました

ダンボーハック開始

届きました。「よつばと! リボルテックダンボー [お化粧なおしBOX] 約130mm ABS&PVC製 塗装済み可動フィギュア (再販版)」です。

リボルテックダンボー

開封して立ててみました。顔の脇にあるスイッチで目が光ります。漫画の通りですね。

開封したダンボー

顔ハック

さて、ではハックしていきましょう。顔の底の蓋のネジは全部で 3 箇所です。1 つが電池ボックスの底にあるので注意して下さい。ネジをすべて外すと顔の底の蓋が外せます。

ダンボーの顔のネジ位置

顔の底の蓋を外してから、サーボモータを入れて位置合わせをしてみました。

SG90を当ててみる

んー…、ちょっと大きすぎるようです。顔の水平方向回転用のサーボモーターが、顔の中心からずれてしまいますね。SG90では少し大きすぎるようです。

もうちょっと小さいサーボモーターは無いかと探した所、秋月電子通商さんで取り扱いのこんなサーボモーターが見つかりました。

GWSサーボ PIC/STD/F(フタバ)

GWSサーボ PIC/STD/F(フタバ)

大きさ 22.8x9.5x16.5mm と、SG90の 31x11.8x22.2mm よりも 5 mm程高さが低いモーターです。トルクは0.7kg と SG90 の半分以下、お値段は SG90 の倍してしまいますがしかたありません。これを 2 つ買ってみました。そして再度サイズ合わせです。

再度サイズ合わせ

なんとなく良さそうですね。写真では顔水平方向のサーボは SG90 ですが、結局、両方共PIC/STD/Fを使いました。

なお、このPIC/STD/Fですが、かなり古いサーボのようなので、ひょっとしたら将来、手に入らなくなるかもしれません。その場合は、サーボ R/Cネットショップ ロビンさん等で、同様の小さいサーボモーターを探すと良いかと思います。(ROBIN 3.5g RB-S035D デジタル等が良いかもしれません)

サーボモーターのマウンター作成

さて、適切なサーボモーターが入手できたので、次はマウンターを作成します。顔水平方向回転用のサーボモーターを収納し、顔垂直方向回転用のサーボモーターと接続するためのケースです。これは、ケイヨーD2で買ったポリスチレン板(¥213)を利用しました。

ポリスチレン板

マウンター設計図

このポリスチレン板に細い油性マジックでアタリを付けてアクリルカッターで切り出し、電動ドリルで穴を開けます。

上記は全て、私がダンボーハックで実際に使った工具です。全てケイヨーD2で揃えました。

これらは Amazon プライムの対象外のようなので、近くのケイヨーD2で購入したほうが早いでしょう。別の商品でも構いませんが、ドリルはダンボーハック全体で、φ3、φ4、φ5、φ6、φ6.5 を使用します。

サーボマウンター部品

こんな感じになります。ご覧の通り、結構アバウトです。サーボモーターのサーボホーン(白い固定用具)は片方を切り落としてヤスリでラウンド加工し、マウンターの斜めに作ったスリットに嵌まるようにしておきます。

これを組み立てていきます。

まず、サーボモーターの 1 つの固定用の羽を削り落とします。ニッパ等で切り落としてからヤスリで削って平らにすると良いでしょう。

サーボモーターの加工

マウンターの組み立て方は以下の写真の通りです。絶縁ビニールテープをつかって貼り合わせていきます。

マウンター1

サーボホーンが外れないように蓋をします。

マウンター2

マウンタープレートで加工したサーボモータを挟み、ステンレス 小ネジ鍋で止めます。サーボモータの残っている羽が小ネジの上に乗るように配置します。

マウンター3

サーボモータのサイドと底にプレートを当て、絶縁ビニールテープで貼って固定します。

マウンター4

マウンター完成です。

マウンター5

粘土でもう一つのサーボモーターの固定場を作る

次は、顔垂直方向回転用のサーボモーターを固定するための土台を粘土で作ります。

使うのは「ふわっと軽いねんど【ブラウン】」(ダイソー)です。

まず、粘土の湿気がモーターを錆びさせることがないよう、ラップでくるみます。

サーボモーターをラップでくるむ

そして、顔の横に粘土を盛り付け、モーターを埋め込みます。この際、先程のマウンターから少し余裕を持たせるため、顔の前後中心よりも少し顔の前側にモーターを寄せました。

粘土を盛ってサーボを埋め込む

半日ほど放置して乾くのを待つと、サーボモーターの土台が完成します。

粘土乾燥後

この「ふわっと軽いねんど」は乾いた質感は固いスポンジ状で、カッターやドライバーでボロボロと崩すことができます。ですから、少し大目に盛って乾燥させてから崩し、サイド粘土を少しだけ盛って表面を整える、ということが簡単にできます。

モーターの固定性を高めるため、顔の内側に粘土を更に盛って壁を作りました。

固定力を強化

LEDと半透明塩ビシートで目を作る

次はダンボーの目です。漫画ではダンボーの目は顔横のスイッチを入れると光る設定になっているので、LEDを埋め込みます。ただし、両目ではなく片目(左目)だけです。

LED は、RGB LED を利用しました。様々なステータスを RGB の各値による色で表現できるので、RGB LED が便利です。

私が使用したのはコレです。カソードコモンタイプ(他の3本がアノード側)です。

RGBフルカラーLED 5mm4本足 OSTA5131A カソードコモン | 秋月電子通商¥50

これに、

LED光拡散キャップ(5mm) 白 日本製 | 秋月電子通商¥30

このキャップを被せて光を拡散させます。

さて、ではまず下準備です。LEDの脚が粘土に触れないように、絶縁ビニールテープを巻いて保護します。

LEDの金具をテープで保護

そうしたらLEDの根本でLEDの脚を折り曲げ、絶縁ビニールテープで足がショートしないよう適度に離して覆い、顔に埋め込めるようにします。なんか、カオナシみたいですね…

LEDの脚を曲げて顔に埋め込めるようにする

次は、塩ビシートを使います。この塩ビシートもケイヨーD2で買いました。(¥213)

塩ビシート

これを、適度な大きさで切り出して目の部分に当ててみます。

はめ込み

適度な大きさであることが分かったら、この表面を粗目のサンドペーパーで削り、透明度を落とします。写真左が粗目加工後です。透明度が落ちているのがおわかりいただけるでしょうか?

サンドペーパー粗目加工前と後

以下が、粗目加工前と後の比較写真です。

粗目加工前

粗目加工前

粗目加工後

粗目加工後

LED が見えづらくなっているのが分かるかと思います。

カメラの埋め込み

もう片方の目(右目)には、Raspberry Pi のカメラモジュールを埋め込みます。例によって、腐食防止のためラップでカメラモジュールを覆います。

Raspberry Pi カメラモジュールの保護

私は以前から購入してあった赤外線カメラモジュールを利用しました。ですが、赤外線照明がなければどのみち暗闇では撮影できないので、普通のカメラモジュールでも構いません。

なお、付属のカメラ用ケーブルは 155mm と短いため、長めのケーブルを用意したほうが良さそうです。20 円程しか違わないので、私は 100cm のケーブルを利用しました。

この Raspberry Pi 用のカメラモジュールのレンズの周りの円形の突起が、ほぼ、ダンボーの目の穴と同じサイズです。(カメラレンズ周りの円形突起の方が若干小さめ)

そうしたら、このカメラを先程のように紙粘土で土台を作って目に埋め込みます。やはり、絶縁ビニールテープで固定します。

粘土でカメラの固定台をつくる

カメラケーブルとりまわし

サーボモーターと左目 LED をセットし、干渉など無いかチェックします。

サーボマウンターとの干渉チェック

100 均で売っているメラミンスポンジを切り出して、左目 LED の上に嵌め、光が漏れないようにします。

メラミンスポンジ

メラミンスポンジで蓋をする

ひとまずこれで、ボディーの上に設置して立たせてみます。

サーボホーンをネジで止める

途中経過

良さそうですね。

明るさセンサーと赤外線LEDの設置

最後に、頭部に明るさセンサーと赤外線LEDを設置します。

まず、上記 2 つを通すための穴を開けます。位置は下記のとおりでなくともかまいませんが、特に赤外線LED用の穴が、目に埋め込んでいるカメラモジュールと干渉しないよう注意して穴を開ける必要があります。

頭部の穴あけ

穴あけにあたっては、本来は外側に平らな木っ端をあて、顔の内側からドリルで開けたいところですが、内側からは位置を特定しにくいため、顔の外側からあまり力を加えずにドリルで開けました。

写真をみていただくと分かりますが、このため、左上の穴(明かりセンサー用の穴)の左下にヒビ割れができてしまいました。

これを隠すため、マスキングを行ってから次章で紹介するタミヤのプラモ用スプレーTS-46を吹きました。このスプレーは、このリボルテックダンボーの材質の色とほとんど同じなので、よく目を凝らして見ないと色の違いがわからないくらいです。

スプレー用マスキング

さて、穴を開けたら明るさセンサー(CdSセル)と赤外線LEDを準備し、絶縁ビニールテープで固定して周りを「ふわっと軽いねんど」で固めます。

CdSセル(フォトレジスタ・明かりセンサー)

一応リンクは貼りますがちょっと高いのでコレでなくても構いません。あまり安いものはちゃんと動かなかったりするのでこちらを選んだだけですので、もし、近場で購入できるのであればそれを使って下さい。

赤外線LED

5mm赤外線LED OSI5LA5113A (10個入)

秋月電子通商さんの説明に「5mmタイプ高輝度赤外線LEDです。投光器や赤外線リモコンなどの用途に最適!」とあるので、こちらのリンクを貼っておきます。

定格電流は100mAですが、PWM出力では1,000mAとなっています。(データシート

半減角度は15°(つまり左右で30°)なので結構狭いです。できるだけ、LEDの中心線が目的の電気製品の方を向くような角度で使って下さい。

これを絶縁ビニールテープで頭部に固定し、周りを「ふわっと軽いねんど」で固定してください。なお、赤外線LEDはアノードとカソードが区別できるようにタックシールを貼ったりメモをとっておくなどしておいてください。アノードがGPIOからつながる抵抗の先、カソードがGNDにつながります。CdSセルに極性はありませんので、こちらは脚の区別をする必要はありません。

赤外線LED1

これを絶縁ビニールテープで巻いて固定した後、頭部に取り付けます。

赤外線LED2

CdSセル1

CdSセル2

ねんどで固定

最終的に、こうなります。

顔、完成

ダンボーハック~よつばと!リボルテックダンボーのロボット化計画―第zero章― ダンボーに至る道

公開日時:2016/10/27 21:01

ダンボーに至る道

当サイトの製品である“ソケットサーバー『HAL』”の大枠が出来上がってきたのが 8 月の終わり頃のことですが、人は目的地にたどり着くと、次の目標を見つけ始めます。

買ったきり机の上に積んであった「実例で学ぶRaspberry Pi電子工作 作りながら応用力を身につける (ブルーバックス) 」に、やっと目を通すことができました。

この本は、基本的には関連書籍である「Raspberry Piで学ぶ電子工作 超小型コンピュータで電子回路を制御する (ブルーバックス)」で紹介されていた回路の応用例が紹介されています。

この本の中で私が特に目をひかれたのは 8 章の「6 脚ロボットを操作してみよう」に紹介されている 6 サーボモーターとサーボドライバーを使ったロボットの作例でした。

サーボモーターを制御するには PWM という、デジタル信号で擬似的にアナログ信号を表現する方法を使うのですが、Raspberry Pi B+ より前は 1 つ、B+ 以降は 2 つまでしか、PWM 信号を出力できません。(ソフトウェア的にシミュレートすることは可能ですが、CPU の負荷状況により信号幅が不規則に広がってしまうため、細かい制御には向きません)

ですが、この本で紹介されているサーボドライバーを使えば、I2C通信を使って最大で16個のサーボモーターを制御出来るようです。

ここ最近は「ロボット」が注目を集めていて、デアゴスティーニから「週刊ロビ」が刊行されたり、Softbankから「ペッパー」、シャープから「ロボホン」などなど、各社から様々なロボットが発売され、ロボットのカンブリアン・エクスプロージョン的な状況になってきました。

そうした状況を見ていると「ロボットといえばやはり二足歩行、僕も二足歩行ロボットを作ってみたい」と思ってしまうのが男というもの。本で紹介されていたサーボドライバーとマイクロサーボモーター 10 個を衝動買いしました。

足首左右・前後、膝、大腿骨と、片脚 4 個、併せて 8 個使用予定ですので、サーボモーター(SG90)5 個入りのパックを 2 つ買いました。

SG90 は 1 個¥400 円程で、普通に手に入るサーボモータの中では恐らく最安だと思います。

他の記事もそうですが、当サイトの記事は「出来るだけ入手しやすい部品を使って、出来るだけ安価に抑え、出来るだけ簡単に作る」ことを目指しているのでこのモーターを選びました。

ちなみに、当サイトで主に Amazon リンクを貼っているのは勿論アソシエイトも目的ですが、何よりも「入手のしやすさ」が他の手段に比べて突出しているからです。私は田舎住まいなのですが、田舎では欲しいものが全く手に入らなくて途方に暮れます。今回の「ダンボーハック」でも様々な部品を取り寄せるのに何度も車を遠くまで走らせては無駄足を踏み、本当に苦労しました。大都市圏の方にはわからないかもしれませんが、田舎者にとって Amazon は本当に便利なんです。

勿論、まず地元のお店で探すことにはしていますが、ガソリン代と時間まで考えたら田舎の地元での買い物はかなり分が悪い選択でしょう。また、電子工作というと秋月電子通商さんや千石電商さんなどなど、多くの通販サイトも頼りになりますが、いかんせん、配送料がバカになりません。

私は Amazon プライム会員なので、大抵の送料は無料ですから、どうしても Amazon 頼みになってしまいます。特に、あれこれと試行錯誤している時は毎日のように1品ずつ購入していたりするので。

秋月さんや千石さん等を利用するのは、ある程度まとめ買い出来る時にした方がよさそうです。

ところで、購入した後に他のサイトの記事などを見ていたら、SG90 はトルクが低い(1.8kgf・cm)ので、SG92R(2.5kgf・cm)の方がパワーがあって良いということでした。

SG92 は 1 個 500 円ほどで、ほんの少し高いだけです。

「失敗したかなぁ?」とも思いましたが、とりあえず、実際に動かしてみないとどうなるかわからないので、そのまま到着を待つことにしました。

二足歩行ロボット作成計画

二足歩行ロボット作成にあたり、筐体には粘土を用いることにしました。

本当は3Dプリンタが使えればそれが良いのですが、今の私には手がでませんし、当サイトの記事を読んだ方でもなかなか所持している方は限られるでしょう。

ですが、粘土で作れるなら本当に誰でも試してみる事ができます。

木を削ったり、あるいはプラ版で整形も考えましたが、とりあえず粘土が一番簡単そうだという結論に。

そこで、近くのお店から 3 種類の粘土を買いあさりました。

まず、「真っ白でなめらかな紙ねんど(ダイソー)」

真っ白でなめらかな紙ねんど(ダイソー)

特徴は固まると石膏みたいな質感でカチンコチンになります。固まった後は結構な重さがあります。

次、「ふわっと軽いねんど(ダイソー)」

ふわっと軽いねんど(ダイソー)

特徴は固まってもスポンジみたいな質感で、とても軽いです。固まった後、カッターやドライバーなどでボロボロと削り崩すことができます。崩した後さらに粘土を盛って整形できます。

次、「色つきかる~い紙ねんど カラーふわふわかる~ん(メガドンキ)」

色つきかる~い紙ねんど カラーふわふわかる~ん(メガドンキ)

特徴は固まるとかなり固くなる割にとても軽いです。デメリットとして、とにかく縮みます。こんなに小さくなるの?ってくらい小さくなります。また、色付きとのことですがインキを混ぜ込んであるだけなのでアチコチに色移りします。

粘土比較

粘土比較

こんな感じで、サーボモーターを固定することを想定して型を作ってみました。アバウトに作ったというのもあるのですが、それでも「色つきかる~い紙ねんどカラーふわふわかる~ん」の縮みがかなり酷いことがわかると思います。

なお、右から 2 番めの斑の粘土は、「真っ白でなめらかな紙ねんど」と「ふわっと軽いねんど」をブレンドしてみました。結果として、全くなじまずにすぐにボロボロに崩れてしまう状態で、使い物にならない事がわかりました。

粘土の特性は、大体こんな感じです。

サーボモーター、到着から検品まで

SG90 10個

と、いうわけでサーボモーター 10 個到着です。

まずすべきことは、何よりも検品。不良品があったらできるだけ早く連絡しなければなりません。

とりあえず、こんな回路を組みました。

サーボモーターテスト回路

テストプログラムは「Raspberry Piで学ぶ電子工作 超小型コンピュータで電子回路を制御する (ブルーバックス)」のサンプルプログラム 08-04-servo.py です。(正確には、これを PHP に移植して改造したものを利用しています)

これは、半固定抵抗の値を ADコンバーターの MCP3208 で読み取り、その値をサーボモーターの Duty サイクルに変換して PWM 信号を出力し、サーボモーターの回転を制御する回路とプログラムです。

SG90仕様書

これで、ひたすら 10 個のサーボモーターについて、動作確認を行いました。幸いにも、不良品は一つもありませんでした。

『さて、これで明日から制作に入れるぞ…』などと思いながらその日は布団に入りました。

いきなりの方向転換

次の日の朝、目が覚めて思ったことは「よし、計画を見直そう!」でした。

実は、夜寝ている時、浅い眠りの時間に夢の中であれこれと試行錯誤をしていたのです。プログラミングに深く携わった者は、そういう技を自然と身につけます。

『骨格は紙粘土で作るとして SG90 がこのぐらいの大きさだから、そうすると足先はこのぐらいの大きさになって、重さはこれぐらいになって、そうすると脚全体ではこのぐらいの…』と考えていくと、どう考えても物理的に無理な気がしてきました。

SG90 はマイクロサーボという名前にはなっていますが、当初思っていたよりもずっと大きなものでした。これで二足歩行用の脚をつくるとなると、どう考えても脚だけで 15cm 近くにはなりそうです。紙粘土で整形していくとなると結構な重さでしょうし、まず、精度が出ません。

実際に手に取るまではわからなかったものが、手にしてリアルに成ったために予想が出来るように成ったわけです。

それまで大して気にしていなかったのですが、サーボモーターって、回転軸は片方にしか無いんですよね。

サーボモーターの軸

これが、両方に突き抜けて軸があるならある程度アバウトでも固定できるでしょうが、片方だけだと上手く支えきれそうにありません。3Dプリンタが使えればマウンターを作って反対側に◎のような構造をつけることで、両側で支えて回転させることも可能かもしれませんが、紙粘土整形ではそれは難しそうです。

それで『これはどうも無理そうだし、やるとしても手間と時間がかかり過ぎる…』という結論に至りました。そんなに手間暇かけていたのでは、当初の「出来るだけ簡単に作る」の趣旨から逸脱してしまいます。

かと言って、既にサーボモーターは購入してしまったし、何か作らないことにはこのままでは引っ込みが付きません。

それで考えたのが「1からつくるんじゃなくて、何か既製品を改造すれば整形の手間が少ないので簡単にできるんじゃないか?」という事でした。

多分、昔、cubic9.com さんの 赤外線学習リモコンR2-D2を見ていたのが影響したんだと思います。

となったら、サーボモーターを仕込める筐体探しです。「なんかいいのないかなー…」と google と amazon で検索し始めました。

で、みつけたのが、これ。

130mm…、どのくらいの大きさかな? ってことで、おおよそのサイズ把握です。

ダンボーサイズ把握

身長が実寸で 130mm になるようにプリントしています。元印刷・出版業界出身で Illustrator の古いバージョンを所持しているのが功を奏しました。

最低でも、頭に 1 個、胴体に 1 個は、SG90 が入れられそうです。なんか可愛いし、これにしましょうかってことで、試しに購入してみることにしました。

購入時、Amazonのシールが貼られている限定モデルと比較したのですが、1度で上手く作れる自信がなかったので、少しだけ安いノーマルモデルにしました。本当は Amazon限定 モデルの方がかわいくていいです…(今は Amazon限定モデルの方がロープライスの物がありますね)

また、ダンボーには「ミニ」や「プラモデルタイプ」もあるようですが、ミニですと 80mm なのでサーボを入れることは厳しそうです。

もっというと、このダンボーハックでは結局、SG90は 1 個も使いませんでした。理由は別の記事で詳しくお話しします。

余談

ちなみに、この時点では「ダンボー」なる物が一体何であるのか、全く知りませんでした。後日「よつばと!」の登場キャラクターだということを知ることになります。「よつばと!」という名前は聞いたことはあったのですが、多分萌え漫画だろうと思っていたので全く読んだことはありませんでした。

今回のダンボーハックにあたり、一応、「ダンボーとは何か?」くらいは知っておきたかったので、ネットカフェに通って「よつばと!」(あずまきよひこ著)を全巻読破したのですが、今までの僕のイメージは完全に誤解だったことに気づくとともに、その面白さに虜になりました。「よつばと!」の細かい解説はここでは省きますが、まだ読んだことが無い方は是非とも読んでみることをおすすめします。何処かに忘れてきた遠い昔を思い出すことができるでしょう。心が疲れている方に、特におすすめです。

また、ダンボーハック中に見つけたのですが、「よつばと!」特設サイトで、いくつかのお話が試し読みできます。

記事リンク