RaspberryPI 3でUSB音声出力の覚書

RaspberryPI3にアキバで買ってきたUSB DACと片側12.5Wのステレオアンプをつないで音を出してみた。
コマンドラインは、

$ aplay -D plughw:2,0 test.wav

出ないとダメだった。DACとアンプをつないだ後、デバイスの指定がplughw:2,0でなければ、ならなかった。

音量調整は、

$ alsamixer -c 2

でする。家でやる限りは、10%程度でよかった。12.5Wのパワーはすごかった。大きなパワーにすると、少し音が割れ気味なのは、スピーカのせいか。

足郎2、再開!!

「サリーと教授」がM-1の一回戦を通過するという妨害の事態が発生したために、昨日、その二回戦が終わるまでの2ヶ月、足郎2が放置された。結果、二回戦は通過しなかったが、サリー(ヒューマノイドロボットNAO)のロボットネタは相当進化したので、それはそれで良かった。

で、今日から、足郎2の再開である。悲しいことに、相当記憶の中から消えているので、中止前の状況を頭の中で再現するのに、今日1日くらいはかかりそうだ。

机の上で動かすのは大変なので、大学の研究室に持っていこうと思う。

Twitter APIの活かし方についてのメモ

この間、Twitter APIをロボットに生かすためのいろいろな試みをしてきたが、ここにきて、来月冒頭にあるM-1 (サリーと出演する)の準備に入らなくてはいけないので、一旦中断しなければならず、これまでのところを簡単なメモにしておこうと思った。

(足郎2も中断していて、これも年末から年明けにかけてのR-1用に仕上げたいのだが、そちらも滞ってしまっている)

基本的に、やりたいことは、劇場などで、お客さんにお題をハッシュタグ付きでツイートしてもらって、ロボットがそれをストリーミングで受け取って、いろいろ答えて行くというシステムだ。

理想的には、NaoQiの自前のライブラリの一つに組み込んで行くことだが、それはC++で書かなければならない。curlのライブラリで、ツイートするところまでは難なくできたのだが、ストリーミングでつまづいた。

そこで、javaでやろうと思った。開発はjavaの方が圧倒的に楽なので。javaでは、ツイートもストリーミング受信も問題なかった。twitterライブラリも使わなくてうまくいった。それというのも、twitterのjavaライブラリ、tweet4jがロボット(nao)の32ビットOSでつまづいているようだったから、それを使わない方向にした。ところが、パソコン上で開発したライブラリ抜きのjavaのツイッタープログラムが、ただのtweetでは、問題なくロボットのos、linuxのgentooでは、ツイートはできるのだが、ストリーミングがうまくいかない。いくら考えても、理由がわからない。

まだ、試みることはあるかもしれないが、当面の課題があるので、ここは一旦止めることにしたのだ。

TwitterStreamingによるツイートイベントの取得

Twitter Botの前の記事の仕様では、bot側から@aigeininへのツイートを取得しなければならなかった。そのために、お題が投稿されたタイミングがわからないから、15秒おきにツイートを取りに行っていた。これが面倒だった。

そこで、TwitterStreamingAPIを使って、投稿のイベントを取得することにした。

twitter4jのサンプルにちょっとだけ手を加えたものは次のようになる

public static void main(String[] args) throws TwitterException {
    TwitterStream twitterStream = new TwitterStreamFactory().getInstance();
    twitterStream.setOAuthConsumer(consumerKey, consumerSecret);
    twitterStream.setOAuthAccessToken(new AccessToken(accessToken, accessTokenSecret));
    
    AiGeininBot2 aig = new AiGeininBot2();
    
    StatusListener listener = new StatusListener() {
        // フィルターをかけたツイートが取れると、このリスナーが呼び出される
        @Override
        public void onStatus(Status status) {
            // ツイート内容がStatusで与えられる
            System.out.println("@" + status.getUser().getScreenName() + " - " + status.getText());
            // statusを与えて、次のメソッドで処理する
            aig.execNazokake(status);
        }

        @Override
        public void onDeletionNotice(StatusDeletionNotice statusDeletionNotice) {
            System.out.println("Got a status deletion notice id:" + statusDeletionNotice.getStatusId());
        }

        @Override
        public void onTrackLimitationNotice(int numberOfLimitedStatuses) {
            System.out.println("Got track limitation notice:" + numberOfLimitedStatuses);
        }

        @Override
        public void onScrubGeo(long userId, long upToStatusId) {
            System.out.println("Got scrub_geo event userId:" + userId + " upToStatusId:" + upToStatusId);
        }

        @Override
        public void onStallWarning(StallWarning warning) {
            System.out.println("Got stall warning:" + warning);
        }

        @Override
        public void onException(Exception ex) {
        }
    };
    twitterStream.addListener(listener);
    // ここで @aigeinin 向けたツイート、リプライだけを取得するためのフィルターを作る
    final String[] TRACK = { "@aigeinin" };
    FilterQuery filter = new FilterQuery();
    filter.track(TRACK);
    // ここでフィルターを組み込む
    twitterStream.filter(filter);
}

javaでツイッターbotの作成

サリーのネタは、お客さんからもらったお題に、即座に謎かけで答えるというものだが、このデータを作っているうちに、どうせなら、twitterのbotも作成しようという気になって、javaで作成した。

その要点を記録しておこうと思う。javaで全てのことができるのが素晴らしい!!

アカウントは、 @aigeinin で、このアカウントを作成すると、それに伴って開発者サイトにログインできる。そのサイトで、作成するbotのアプリケーションを登録すると、4つのキー文字列がもらえる。私の場合、javaで作成するので、twitter4jのライブラリを使ってアプリを作成する。

twitter4jは、以下のサイトからダウンロードできる。
http://twitter4j.org/ja/index.html

初歩的な使い方、作り方はネットにたくさん情報があるので、ここでは記載しない。

まず、ツイッターオブジェクトを作成する。
Twitter twitter = new TwitterFactory().getInstance();
4つのキーをセットする(それぞれの文字変数に入れておく)
twitter.setOAuthConsumer(consumerKey, consumerSecret);
twitter.setOAuthAccessToken(new AccessToken(accessToken, accessTokenSecret));

ツイートを最新の20個取得する。
List<Status> statuses = twitter.getMentionsTimeline();
statusとは、以下よく出てくるが、ツイート(返信も含む)のことだ。statusesを一個一個表示(一部の内容だけ)させると以下のようになる。

for (Status status : statuses) {
    System.out..println(
        "《Statusの表示》\n"
        + "getName > " + status.getUser().getName() + " : \n"
        + "getScreenName > " + status.getUser().getScreenName() + " : \n"
        + "getInReplyToScreenName > " + status.getInReplyToScreenName() + " : \n"
        + "getInReplyToStatusId > " + status.getInReplyToStatusId() + " : \n" // 返信じゃない -1
        + "getInReplyToUserId > " + status.getInReplyToUserId() + " : \n"
        + "getCreatedAt > " + status.getCreatedAt().toString() + " : \n"
        + "getText > " + status.getText() + " : \n"
        + "getId > " + status.getId()
    );
}

status IDが大事だ。全てのツイートは、このIDを持っている。もしこのツイートが、他のツイートの返信ならば、getInReplyToStatusId()にその元ツイートのIDが入っている。新規ツイートならば、この値は-1だ。このことはとても重要。

getScreenName()が、@で始まる、いわゆるユーザーIDだ。ツイートの内容は、その全体が、getText()で取得できる。

ツイートのStatus IDがわかると、twitterオブジェクトから、
twitter.showStatus(statusID)
で、そのもとツイートがstatusで取得できる。showなのだが、statusそのものが取り出せるところが、最初戸惑った。

新規のツイートは、tweetにツイートすべきテキストを入れて、
twitter.updateStatus(tweet);
でできる。

リプライ(リプ、返信)は、まず、返信の内容を、tweetにテキストで入れて、StatusUpdateオブジェクトを作成する。
StatusUpdate supd = new StatusUpdate(tweet);
そのオブジェクトに返信の対象となったもとツイートのIDをセットする。
supd.inReplyToStatusId(status.getId());
そして、オブジェクトを引数にツイートさせる
twitter.updateStatus(supd);
となる。

リプライのところは、詳しいマニュアルがないので、勘でやったらうまくいったという感じだ。

これだけで、ツイッターbotのほとんどのことができるはずだ。

足郎2が進まない

本当は、ハード的には組み上がった足郎2を本格的に動かしていなければならない状況なのだが、予想外の事態が発生したために、そちらは全く進まなくなった。

というのは、私的ロボットのサリーと私のコンビ(サリーと教授)が漫才グランプリのM-1の一回戦に合格してしまったのだ。それ嬉しいことだが、合格するとは正直思っていなかった。そもそも、ロボットのコンビで漫才と言えるのかどうか、それすら怪しかった。が、演芸ロボットを普及していかなければならないという使命感で、出たのだ。

普通、二人でやる漫才で、着飾ったロボットを抱えて順番待ちするのは恥ずかしかったが、まあ、一度だけだと思った。合格を知ったときは、腰を抜かすほど驚いた。プロでも落ちるM-1なのだ。

10月に2回戦があるので、そのために、ネタのデータやプログラムを改定していかなければならない。それに忙殺されている。だから、足郎には手が回らないというわけだ。

COSMの改訂

足郎2を動かす前に、足郎制御言語cosmとそのインタープリターを改訂する。実際に動かし始めるとそれに集中したいからだ。

(1)サーボグループの角度定義に配列を設定できる。
ロボットのある一連のサーボを現在の状態から別の状態に変化させるのは、%defangles で、その角度群を定義するのだが、同じグループを連続で滑らかに変化させる場合に、いちいちそれを定義し直していくのは面倒なので、角度名の後に連続で定義し、配列に入れる。

(2)全ての定義の中で、空白を無視するようにする

今までは、定義の中では、空白は使えなかったが不便なので。

例えば、結局以下のように定義できる。

%defangles right_invpen[3] {
#右に傾ける動きをする      [0]     [1]       [2]
    RightUpperRight:  -$righ,  $righ2,   $righ3;
    RightUpperLeft:   +$right, $righ2_1, $righ3;
    LeftUpperRight:   -$left,  $left2,   $left3;
    LeftUpperLeft:     $left,  $left2,   $left3_1;
    LeftKneeFront:    -$bent,  $bent2_1, $bent3;
    LeftKneeBack:      $bent,  $bent2_2, $bent3;
    RightLowerRight:   $right, $righ2,   $righ3;
    RightLowerLeft:   -$right, $righ2,   $righ3_1;
    LeftLowerRight:    $left,  $left2_2, $left3;
    LeftLowerLeft:    -$left,  $left2,   $left3_2;
}

垂直直立の確認

朝から、足郎2の垂直直立の調整をやっている。

これまで、三角定規なのでやっていたが、足郎2は背が高いので、オモリをぶら下げた紐で確認するのがいい。紐に電池をぶら下げて垂直を見ている。前後左右に確認しなければならない。その度に、サーボからのリードの長さを変える。

単に一つのリードの長さで、一つの垂直が決まるのではない。そこに足郎2の複雑さがある。膝の関節は単純だが、他は色々関連している。上板で左右の足がつながっているので、一つの垂直がある意味全体のリード線の影響を受けていると言っても良い。

ただ、そういう、相互依存性が、これだけの体をか弱いサーボで動かすことができる理由でもある。一つのサーボに重さや動きが集中していると、支えきれなくなる。

動作上の組み立ては終わった

動作に関わる部品の組み込みが終わった。
足の裏につけるゴムの、ネジ穴部分のくりぬきに今回は、彫刻刀を使ったら、割と綺麗に仕上がった。

垂直に立てるのが苦労するところ。
ただ、今回は、フレームが業者に作ってもらい、精密なので、重りをつけて鉛直に垂らした糸で、ほぼ正確に垂直を捉えて、リード線の調整をした。

あとはネジを完全に固定して、プログラムで動かすだけだ。

ただ、ここにきて、NAOの方の業務ができたので、動かすのはしばらくお預けになる可能性がある。

RaspberryPIで二つ目のI2CチャンネルをPCA-9685用に動かす

足郎2では、左右の足を別のスレッドで動かし、かつそれぞれ別のI2Cチャンネルを使うことにしている。

RaspberryPIで二つ目のI2Cチャンネルを動かすことについては、
http://robo.genv.sophia.ac.jp/wp_robot/?p=1645
にだいぶ書いておいた。

ただ、これをサーボドライバPCA-9685用に動かすためには、一つ面倒な作業がある。それは、I2Cのデフォルトである1チャンネルは、すでにRaspberryPIのボード内でプルアップされているのであるが、この第二チャンネルは、プルアップされていないので、外部プロアップをしなければならないのである。

ただ、秋月電子のボードには、プルアップ用の抵抗がすでに取り付けられていて、このJ1とJ2を、直結すればSDAとSCLの二つのピンがプルアップされる。

直結する方法に戸惑ったが、ハンダで無理やりすれば良いのだと了解して、そうしている。今の所不都合はない。

二足歩行の意味

なぜロボットに二足歩行をさせるのか?

単純に考えれば、人が二足歩行しているからそれを真似させるということだ。見る側も、ロボットが四足歩行をしているよりも、二足歩行をしている方が、自分に対する親近感、共感を覚えやすいから、惹きつけられる。それを期待しているわけだ。

単なる移動であれば、四足歩行でも、多足歩行でも良いような気がする。

しかし、逆振り子型の二足歩行にこだわっていると、二足歩行には別の意味があるように思える。移動には、二足歩行の方が、他の多足歩行よりも簡便であり、省エネルギーなのではないかということだ。

モアイの移動を、逆振り子でやっている動画を先にお見せした。逆振り子は、あの深刻な重量のモアイ像をいとも簡単に移動させてしまう。

人間の歩行も、極めて省エネではないか。3足歩行はイメージがわかないが、四足歩行は、確かに安定している。チータは、確かに四足歩行で、100キロを超えるスピードを出す。それはすごい。しかし、二足歩行と四足歩行を同じ速さで、同じ重さで、測ったら、距離あたりのエネルギーは二足歩行の方が小さいのではないか?

そう思って検索したら、そういう研究論文がすでに発表されていることがわかった。
http://www.afpbb.com/articles/-/2255112?pid=1790817
二足歩行の必要エネルギーは四足歩行の四分の一というわけだ。
それが二足歩行への進化の要因だという考えには、批判もあるようだが、二足歩行が省エネルギーという事実が重要なのである。

やはりそうなのだ。予想は正しかった。つまり、ロボットの二足歩行へのこだわりは、省エネルギー型移動への模索という意味を持っている。

足郎2は今作っているところだが、足郎1でわかったのは、体を揺らして、ほんのわずかの前進操作をするだけで、歩み出すということだった。

足郎2が完成すれば、あの大きくて長い足が、いとも簡単に歩き出す様子をお見せできるはずだ。

NAOもそうだがホンダのASIMOも、その二足歩行は、人の動きを真似るためのただのパフォーマンスに見えて仕方がない。効率的移動という目的意識性を感じないのだ。

私は、このサイトに、逆振り子の理論から、足郎0(1本足ロボット)、足郎1と逆振り子型移動の理論とモデルを示してきた。この方向を、さらに徹底して追求していきたい。

足の配線完了

サーボドライバーボードのPCA-9685は片足それぞれについている。足郎1のように、上板上のコンピュータ側におくと配線が混雑してダメだからだ。ドライバーボードは16個のサーボモータを制御できるが、片足は10個しかついていない。この二つのボードは、RaspberryPIのI2Cの二つのポートを使う予定だ。RaspberryPIには、デフォルトでは1個のI2Cしか動かせないが、もともと2つついているのでその二つを動かす。

したがって、RaspberryPIそのものは、一つしか必要ないのだが、会話や知的な機能を担うために二台乗せてある。

当面は一つしか動かさない。

電池ボックスを股間に設置するのだが、いったんRaspberryPI を外さないとつけれない。

そのあと、サーボと関節をつなぐワイヤーをセットすれば、動作に関する部分は終える。

サーボモータをつける

今日は、この夏一番の暑さだった。

そんな中、サーボモータを全部つけた。
うわ板の真ん中でぶら下げているのだが、左右のバランスは完璧に取れている。当たり前だが。

何だか、恐竜のベロキラプトルのような格好なのだ。ジュラシックパークに出てきた(笑)

明日は、動きの部分だけは仕上げたい。