ラピロを復活させる(2016年版:Raspberry Piからラピロを制御する)
久しぶりにラピロを復活させることにしました。前にちょっと書きましたが、色々機能や追加パーツを載せすぎたせいでだいぶ動かしにくくなってしまっていたので、今回は出来るだけ機能をシンプルにしていきたいと思います。
せっかくなので、イチから設定を始めてRaspberry Piからラピロの動きを制御するところまで一気にやってしまいたいと思います。
まずは最新のNOOBS(2016/06/25時点ではv.1.9.2)を落としてきて、Raspbian OSをSDカードにインストール。なお、今回利用しているのはRaspberry Pi 2になります。3ではないのでご注意。
インストールが済んだら、Raspberry PiにUSB無線LAN子機を挿して、こんな感じで固定IPを割り振ります。ちなみに、自分が使っている無線LAN子機は以下です。
固定IP設定が済んだら、以後の作業は普段自分が使っているPCからSSHでログインして行います。
$ ssh pi@192.168.24.xxx
まず、Raspberry Piのベースをアップデートしておきます。
$ sudo apt-get update $ sudo apt-get upgrade -y
それから、raspi-configで以下のように設定します。
$ sudo raspi-config
- 3 Boot Options > B1 Console
- 5 Internationalisation Options > I2 Change Timezone > Asia > Tokyo
- 6 Enable Camera > Yes
- 9 Advanced Options
- A4 SSH > Yes
- A6 I2C > Yes
- A7 Serial > Yes
設定完了したら一度再起動。再ログインしたら、必要なものを諸々インストールしていきます。
まず個人的にエディタはvim派なので、vimをインストールしておきます。
$ sudo apt-get install vim -y
それから、I2C関係で必要なツールをインストール。普通にラピロを動かすだけなら不要ですが、どうせこの後すぐにI2Cを使ってなんやかんや拡張していくことになるので。
$ sudo apt-get install i2c-tools $ sudo apt-get install python-smbus
続いて、Python関係で必要なものを入れていきますが、最新版のRaspbianでもPythonのデフォルトは3系ではなく2.7.9です。個人的には3系にしたいのですが、ちょっとこれから使う予定のデバイスとかのライブラリが3系に対応していない可能性があるので、今回は話をややこしくないために、このまま2.7.9を使っていきます。
まず、もはや自分の中ではこれがないとどうにもならないぐらいに多用している、Python WebサーバのBottleをインストール。ラピロを動かすのに必須なわけではないですが、やっぱりブラウザから動作検証できると色々ラクなので。
$ sudo pip install bottle
他にもrequestsとpyserialが必要なのですが、”$ pip freeze”で確認したらどちらも初めから入っているようなのでインストールなしで進みます。
ではでは、Webブラウザからラピロを制御するプログラムを書いていきます。とりあえずサーバ用ファイルのディレクトリを作成して移動します。
$ cd $ mkdir -p server/python $ cd server/python
サンプルはこんな感じです。
$ vim rapiro_server.py
# -*- coding: utf-8 -*- from bottle import route,run,request,response,hook import serial import time import json com = serial.Serial('/dev/ttyAMA0', 57600, timeout=10) @hook('after_request') def header_json(): response.content_type = 'application/json' ########################################## def control_response_json(value): obj = {'control':value} return json.dumps(obj) @route('/v1/robots/rapiro/control/power') def control_power(): status = request.query.status; if status == 'off': com.write('#S') elif status == 'on': com.write('#D') return control_response_json("power") @route('/v1/robots/rapiro/control/init') def control_init(): com.write("#M0") time.sleep(2) com.write("#PR000G000B000T010") time.sleep(1) com.write('#S') #emotion_none() return control_response_json("init") @route('/v1/robots/rapiro/control/stop') def control_stop(): com.write("#M0") return control_response_json("stop") @route('/v1/robots/rapiro/control/forward') def control_forward(): com.write("#M1") return control_response_json("forward") @route('/v1/robots/rapiro/control/back') def control_back(): com.write("#M2") return control_response_json("back") @route('/v1/robots/rapiro/control/right') def control_right(): com.write("#M4") return control_response_json("right") @route('/v1/robots/rapiro/control/left') def control_left(): com.write("#M3") return control_response_json("left") @route('/v1/robots/rapiro/control/banzai') def control_banzai(): com.write("#M5") return control_response_json("banzai") @route('/v1/robots/rapiro/control/head') def control_head(): value = request.query.value; command = "#PS00A" + value.zfill(3) + "T005" com.write(command) return control_response_json("head") @route('/v1/robots/rapiro/control/left_hand') def control_left_hand(): value = request.query.value; command = "#PS07A" + value.zfill(3) + "T005" com.write(command) return control_response_json("left_hand") @route('/v1/robots/rapiro/control/right_hand') def control_right_hand(): value = request.query.value; command = "#PS04A" + value.zfill(3) + "T005" com.write(command) return control_response_json("right_hand") @route('/v1/robots/rapiro/control/left_shoulder') def control_left_shoulder(): value = request.query.value; command = "#PS05A" + value.zfill(3) + "T005" com.write(command) return control_response_json("left_shoulder") @route('/v1/robots/rapiro/control/right_shoulder') def control_right_shoulder(): value = request.query.value; com.write("#PR000G000B000T010") command = "#PS02A" + value.zfill(3) + "T005" com.write(command) return control_response_json("right_shoulder") ########################################## run(host='192.168.24.xxx', port=10080, debug=True)
サンプルにしては色々書き過ぎてしまいましたが、最初は”@route”でつらつら書き連ねている中の
- control_stop
- control_forward
- control_back
- contorl_right
- control_left
- control_banzai
あたりだけで試してみると良いと思います。
早速実行。。。といきたいところですが、その前に、GPIOのシリアル通信をコンソールとして使わせないように設定してやる必要があります。これをやっておかないと、Raspberry Piからラピロ本体のArduinoに対して意図しないデータが送られてしまい、ちゃんと制御することができません。
これは、以前は”rpi-serial-console“というツールを使っていたのですが、どうやら最近のOSでは仕組みが変わったようで、上記のツールで設定してやろうとすると”/etc/inittab is missing”となってしまい、設定ができません。
ということで、こちらを参考にさせていただき、以下のようにして設定を変更しました。今回は特にツールのインストールは不要で、そのまま実行できます。
$ sudo vim /boot/cmdline.txt dwc_otg.lpm_enable=0 console=tty1 console=serial0,115200 root=/dev/mmcblk0p7 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
上記の”console=serial0,115200″となっているところをまるっと削除します。参考にさせていただいたところでは、この部分は”console=ttyAMA0,115200 kgdboc=ttyAMA0,115200″だったのですが、Raspberry Pi 3のリリースとかの関係でまた状況が変わったようです。ということで、ここで紹介している手順はRaspberry Pi 2だとうまくいっていますが、Raspberry Pi 3だとまたちょっと変わるのかもしれません。
さて、まだ終わりではありません。続けて以下のコマンドを実行して、再起動します。
$ sudo systemctl stop serial-getty@ttyAMA0.service $ sudo systemctl disable serial-getty@ttyAMA0.service $ sudo reboot
これでシリアル周りの設定は完了です。再起動が完了したら、先ほどのサンプルプログラムを実行します。
$ python /home/pi/server/python/rapiro_server.py Bottle v0.12.9 server starting up (using WSGIRefServer())... Listening on http://192.168.24.xxx:10080/ Hit Ctrl-C to quit.
問題なく起動してくれたら、同じローカルネットに繋がっているPC上のブラウザで”http://192.168.24.xxx:10080/v1/robots/rapiro/control/banzai” のように打ち込めば、ラピロが両手をふりふりしてくれるハズです。
ふりふり。
ちなみに、サンプルソースの中の”control_power”と”control_init”を試したい場合は、ラピロのArduino側のソースを変更してやる必要があります。loop関数の中のcase式の中で、以下のように”#S”と”#D”の場合を追加します。
... void loop() { int buf = ERR; if(Serial.available()) { if(Serial.read() == '#') { while(!Serial.available()){} switch(Serial.read()) { ... case 'S': Serial.print("#S"); digitalWrite(POWER, LOW); break; case 'D': Serial.print("#D"); digitalWrite(POWER, HIGH); break; default: Serial.print("#E"); break; } } } ... } ...
これは、ラピロの全身のサーボモータへの通電のON/OFFを任意で制御するために追加しています。基本OFFにしとかないと、常時サーボモータが「ジー」という音を立てて、なんだか落ち着かないので。
ということで、ラピロを動かすところまでは復活させることができました。ここからまた、表情をつけたり、喋らせたり、というところを追加していきたいと思います。
ディスカッション
コメント一覧
サーボモータの「ジー」を止めたいのですが、ご指示通りサンプルソースを追記し、rapiro_server.pyを立ち上げ、同じローカルネットに繋がっているPC上のブラウザで”http://192.168.24.xxx:10080/v1/robots/rapiro/control/power”を打ち込んだ後、どうすれば良いのですか? 具体的にお教えください。
ちなみに ”banzai” 動作は確認(成功)しています。
kikyo様
最近ラピロを触っていないのでうろ覚えになってしまいますが。。。Arduino側のソースも書き換え済みということでしたら、”http://192.168.24.xxx:10080/v1/robots/rapiro/control/power?status=off”と打ち込んでみてもらえませんか? rapiro_server.pyの@routeの中で”status = request.query.status;”や”value = request.query.value;”のように書かれているものを試すときには、HTTPのGETリクエストを投げる時に、先ほどのような”?status=off”や”?value=60″の形でパラメータを渡してあげる必要があります。
hidaka_hiroji様
早速御教示を頂きありがとうございます。
返信が遅れましたこと、深くお詫び申し上げます。
お教えいただきました通り、http://192.168.xx.xx:10080/v1/robots/rapiro/control/power?status=off
のように power? + status=off のアドレスをブラウザに打ち込みましたが、ジー音は変化ありません。端末上では信号は届いているように思えるのですが(端末上ではbannzaiの時と同じようなTEXTの変化があります)。
kikyo様
うーん、原因の切り分けが必要ですね。それでは、”power?status=off”は一旦置いておいて、この記事のrapiro_sever.pyはそのまま使っているという前提で、”http://192.168.xx.xx:10080/v1/robots/rapiro/control/head?value=120″だとどうなりますか?これでラピロが首だけをちょっと横に向けてくれるようなら、パラメータの渡し方はやっぱり間違ってないことになるので、Arduinoボード側のソースコードの変更で何かミスがある可能性があります。この場合は、一旦Raspberry Piとの連携は置いておいて、Arduinoボード単体で、コマンド”#S”を送ったときにジー音が確かに鳴り止むかを確認した方がよいかと思います。一方、”head?value=120″でもラピロが何も反応しないがら、パラメータの渡し方か、もしくはrapiro_sever.pyのいずれかにミスがある可能性があります(私が間違っている可能性もあります)。地道にprintを使って、ちゃんと意図した条件分岐に処理が進んでいるかを確認した方が良いかと思います。
hidaka_hiroji様
早々懇切丁寧なお教えありがとうございます。
”head?value=120″で首を横に向けてくれました。
Arduinoボード単体で、コマンド”#S”を送ったときにジー音が確かに鳴り止むかを確認しました。IDEのツールで「シリアルポート」は反応しないので「COM4」で接続し”#S”を送信しましたが無反応でした。
“#M5″も無反応なので、RAPIROのACアダプターの電源をOFFにし(勿論ジー音も止まります)、”#M5″を送ると目の色が緑に変わります。
更にチャレンジしたいと思います。
今後ともよろしくお願い申し上げます。
kikyo様
うーん、「ACアダプタを繋いでいるときは”#M5″も無反応」というのが気になりますね。。。ちなみにArduinoボード単体でテストするときは、Raspberry Piとの接続は物理的に外していますか? Raspberry Piと繋いだ状態でACアダプタの電源を入れてしまうと、Arduino経由でRaspberry Piにも十分な電力が供給されてしまい、先にArduinoとRaspberry Piの間で通信が確立され、結果、PCのIDEからの命令を受け付けられなくなる可能性はないかな、と思いました。
hidaka_hiroji様
ありがとうございます。
仰せの通りArduinoからの6芯ケーブルを抜き”#s”を送るとジー音が止みました。
尚、大文字の”#S”は駄目でした。(#Eが表示されました)
(念のため、電源供給を、PC(usb)からと、ACアダプターからの順序を入れ替えて試しましたが共に駄目でした。)
大文字不可がヒントかなと思い、続けてチャレンジします。
取り急ぎご報告いたします。
今後ともよろしくお願い申し上げます。
hidaka_hiroji様
解決しました。
申し訳ありません。。。単純なミスでした。
Arduino側のソースの変更追加箇所のうち、case ‘S’: の ‘S’ が ‘s’ と小文字になっていました。
大変なお手数をお掛けし本当に申し訳ありませんでした。
深くお詫び申し上げます。
でも、おかげ様で沢山なことを教えていただきました。 ありがとうございます。
夢は大きく、会話のできるロボットを目指して頑張りたいと思います。
今後ともよろしく御教示のほどお願い申し上げます。
kikyo様
いえいえ、kikyo様の現象の報告が適切でしたので、こちらもアドバイスしやすかったです。ありがとうございました◎
会話ロボットは、私も「なんちゃって」を作ってみては使わなくなる、ということを繰り返しています。良い感じのものができあがったら、是非教えてください (^^
ピンバック & トラックバック一覧
[…] 前回ラピロの動作を復活させることはできたので、次はラピロの表情(文字列表示含む)を復活させます。実質やることは、過去にやった2つの内容の再検証です。 […]
[…] ということで、「今回搭載したジェスチャセンサと、前の動作とか表情づけを組み合わせるとこんなことができますよ」というデモです。 […]