ラピロを復活させる(2016年版:Raspberry Piからラピロを制御する)

th_rapiro-reboot

久しぶりにラピロを復活させることにしました。前にちょっと書きましたが、色々機能や追加パーツを載せすぎたせいでだいぶ動かしにくくなってしまっていたので、今回は出来るだけ機能をシンプルにしていきたいと思います。

せっかくなので、イチから設定を始めて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にしとかないと、常時サーボモータが「ジー」という音を立てて、なんだか落ち着かないので。

 

ということで、ラピロを動かすところまでは復活させることができました。ここからまた、表情をつけたり、喋らせたり、というところを追加していきたいと思います。