Yura YuLife

ITエンジニアの覚え書き

ROS Pythonにおけるuint8[]の扱い方

ROSでuint8[]型を利用しているトピックをPythonで使う場合にハマった点です。

環境

rospyにおけるuint8[]型の扱い

ROSのメッセージ型に関するリファレンスを参照すると、uint8[]型について以下のような記述があります。

uint8 has special meaning in Python. uint8[] is treated as a Python bytes so that it is compatible with other byte-oriented APIs in Python.

rospy treats uint8[] data as a bytes, which is the Python representation for byte data. In Python 2, this is the same as str.

Pythonでは、ROSのuint8[]型がbytes型(Python 2系ではstr型)として扱われるらしく、これは時に厄介な問題を引き起こします。

発生した問題

とあるセンサー群の測定値を測定するメッセージ型を以下のように定義しました。

foo_package/msg/FooSensor.msg

uint8[] value

そして、以下のようなシンプルなPublisherとSubscriberを作成しました。

publisher.py

import rospy
from foo_package.msg import FooSensor

def publish():
    publisher = rospy.Publisher('/foo', FooSensor, queue_size=1)
    value = FooSensor(value=[100, 110, 120])
    r = rospy.Rate(1.0)
    while not rospy.is_shutdown():
        publisher.publish(value)
        r.sleep()

publish()

subscriber.py

import rospy
from foo_package.msg import FooSensor

def callback(data):
    rospy.loginfo(data)
    rospy.loginfo(data.value)

def subscribe():
    rospy.Subscribe('/foo', FooSensor, callback)
    rospy.spin()

subscribe()

publisher.py, subscriber.pyを実行すると、以下のような2つのINFOレベルのログが出力されます。

  • [INFO] [WallTime: 1461145922.975544] value: [100, 110, 120]
  • [INFO] [WallTime: 1461145941.678116] dnx

data自体を出力すると、valueにint型の数値が入っているのが確認できるのですが、data.valueを出力するとdnxという文字列が出力されてしまい、配列の要素をint型として利用することが出来ません。

対処方法

対処方法はあまり根本的なものではないのですが、valueの各要素のASCIIコードが本来のintの値なので、ord関数で変換してあげることでint型の配列を得ることが出来ます。

def callback(data):
    int_array = [ord(n) for n in data.value]
    rospy.logwarn(int_array)

再度実行すると[100, 110, 120]という配列が出力されます。

ただし、この処理は直感的でなく、uint8[]型以外に対してはエラーを返すため、あまりオススメできません。出来れば、メッセージ型の定義部分でuint16[]等を利用することで回避するのが好ましいと思われます。

参考URL

BitBucketとWerckerでウェブサイトを自動デプロイ

タイトルの通り。BitBucketWerckerFTP(lftp)で静的なウェブサイトを自動デプロイする方法です。

ちなみにBitBucketとWerckerを使っているのは、プライベートリポジトリを無料で使えるから。

BitBucketの設定

  • Gitのリポジトリを作って、ウェブサイトのファイルをコミットしておきます

Werckerの設定

Applicationの作成

f:id:yurayur:20160410183320p:plain

  • 画面上部のCreateからApplicationを追加します。
  • Choose a repositoryでは、先ほど作成したBitBucketのリポジトリを選択します。
  • Ownerの設定は自分のアカウントか、共有で管理するならばOrganizationを選択します。
  • Configure accessは、recommendedの設定をそのまま利用すれば大丈夫です。

デプロイ設定

アプリケーションを作成したら、SettingsのTargetsから、デプロイターゲットの設定をします。

f:id:yurayur:20160410183806p:plain

  • Deploy target nameには適当な名前を設定します。
  • Auto deployにチェックを入れておくと、ビルド成功時に自動でデプロイが行われます
  • また、デプロイ対象となるブランチ名(通常はmaster)を入力します。

環境変数の設定

SettingsのEnvironmental Variablesに以下のKey, Valueのペアを登録しておきます。

  • FTP_SERVER_URL: サーバのURL(ftp.example.comなど)
  • FTP_USERNAME: FTPのユーザ名(Protectedを推奨)
  • FTP_PASSWORD: FTPのパスワード(Protectedを推奨)

f:id:yurayur:20160410184231p:plain

環境変数をProtectedにしておくと管理画面等で変数の内容が表示されないので、パスワード等の情報は必ずProtectedにしておくことを推奨します。

wercker.ymlの追加

リポジトリのルートにwercker.ymlとして、以下のファイルを追加します。

# This references a standard debian container from the
# Docker Hub https://registry.hub.docker.com/_/debian/
# Read more about containers on our dev center
# http://devcenter.wercker.com/docs/containers/index.html
box: debian

# This is the build pipeline. Pipelines are the core of wercker
# Read more about pipelines on our dev center
# http://devcenter.wercker.com/docs/pipelines/index.html
build:
    # Steps make up the actions in your pipeline
    # Read more about steps on our dev center:
    # http://devcenter.wercker.com/docs/steps/index.html
  steps:
    - script:
        name: echo
        code: |
          echo "Build finished!"
deploy:
    steps:
      - install-packages:
        packages: lftp
      - script:
        name: upload files via FTP
        code: |
          lftp -u ${FTP_USERNAME},${FTP_PASSWORD} -e "set ssl:verify-certificate no;mirror -Rev -X wercker.yml アップロード元(リポジトリ)のディレクトリ/. アップロード先(サーバ)のディレクトリ/." ${FTP_SERVER_URL}

上記の設定ファイルによって、リモートリポジトリへのpush時にWerckerで以下の内容が実行されます。

  • ビルドとデプロイを行うための、debianのboxを作成
  • ビルド時にecho "Build finished!"を実行
  • デプロイ時にlftpをインストール
  • lftpコマンドでリポジトリ内のファイルをサーバへミラーリングアップロード(ただしwercker.ymlはアップロードしない)

デプロイの実行

上記の設定が済んだら変更内容をcommitし、リモートリポジトリのmasterブランチへpushを行います。

ビルドやデプロイに失敗するとWerckerからメール通知が来るので、エラーメッセージを読んで適宜修正を行います。

slackへの通知

wercker.ymlに以下の行を追加すると、デプロイ(もしくはビルド)時にslackへ通知を飛ばすことができます。

    after-steps:
        - slack-notifier:
            url: $SLACK_WEBHOOK_URL
            username: Wercker

slack側で事前にIncoming WebhookのURLを取得し、Werckerの環境変数SLACK_WEBHOOK_URLとして当該URLを登録しておくことでslackに通知が飛びます。

f:id:yurayur:20160410185520p:plain

参考URL

ニューラルネットで声から体調を推定するシステムを作りました

IDCフロンティア主催のIoTと健康をテーマにしたハッカソン、サバフェス 2016で Healthy Voice Netというシステムを作ったので紹介します。大雑把に言うと、ニューラルネットワークを用いて声色を分類することで、声から体調が分かるシステムです。

プレゼンテーション

デモ動画

ソースコード

github.com

概要

Healthy Voice Netは、体調による声色の違いをニューラルネットワークを使って学習させることで、声から体調を推定できるシステムです。

f:id:yurayur:20160326235431p:plain

見た目はこんな感じ。

ニューラルネットの学習

データの用意

体調の良い時、寝不足の時、風邪気味の時の声をたくさん録音しておきます。今回は「おはよう」という決め打ちの文言を、それぞれ約30回ずつ録音したファイル(wav, mono, 16bit, 44.1kHz)を用意しました。データ数は多ければ多いほど良いと思いますし、ノイズへの耐性を強くするために、様々な環境で録音するほうが好ましいです。

音声の前処理

前処理として、録音した音声ファイルをFFTにかけ、さらに音量を正規化します。これは、体調の良し悪しが音声のスペクトルに現れるという仮定のもと、波形の違いをニューラルネットに学習させるためです。音声の録音にはPyAudioを、FFT変換にはscipyをそれぞれ利用しています。

ニューラルネットの構成

ニューラルネットは、4層の全結合のものを利用しています。

  • 入力層: 20000次元 (1Hz〜20kHzの各周波数の音量に対応)
  • 中間層1: 100次元
  • 中間層2: 20次元
  • 出力層: 3次元 (良好、寝不足、風邪気味の各体調に対応)

ニューラルネットの生成、学習にはChainerを利用しました。

学習の実行

約100個の音声ファイルに対して60世代ほど学習をさせました。学習にかかる時間はCPUを利用しても10分程度です。学習データが少ないためか、かなり過学習になる傾向があり、50〜60世代くらいまでは精度が上がっていくのですが、逆にそこから先はテストデータに対する精度が落ちてしまいました。本来ならば現在の100倍〜1000倍くらいの学習データが必要だと思います。

体調の推定

システムの構築

システムの構築には以下のデバイスを利用しました。

LEDとスイッチは以下のように配線しています。

f:id:yurayur:20160326231721p:plain

LCDの表示方法については以下の記事を参照してください。

yura2.hateblo.jp

上記のようにデバイスを構成して、Raspberry Piでpythonスクリプトを動かすと、デモ動画のように動作します。

推定精度

ノイズの少ない環境においては、僕の3つの声色はかなり高い精度で推定できました。特に寝不足風の声に関しては、かなり声色に癖があったためか、ほぼ100%の確率で正しく推定できています。一方で、ノイズに対する対策を一切行っていないので、周辺の雑音が多い環境では普通の声が風邪気味と推定される確率が高かったです。

クラウドとの連携

ブラウザでの結果確認

今回のハッカソンがIDCフロンティア主催のサーバーフェスタということで、IDCFクラウドを利用してデータの表示や他のシステムとの連携を行っています。

推定された体調や録音された音声は、以下のページからブラウザ上で確認可能です。

上記のページでは単に結果を見られるだけでなく、ページ内のフォームで正しい体調を指定することで、ニューラルネットの学習用データが追加され、推定精度を高めることができます。ページの表示にはdjangoを利用しています。

myThingsとの連携

myThingsのIDCFクラウドチャンネルを利用することで、体調が推定された際に、myThingsからGmailやSlackなどのサービスへ通知を飛ばすことが出来ます。面倒なのかと思いきや、環境構築用のスクリプトが用意されていたので、ものの1時間程度でスマホにプッシュ通知を飛ばすことができました。しかも、クーポンを利用すると半年くらい無料でサーバが利用できるので超オススメです。

www.idcf.jp

さいごに

本システムを持って乗り込んだサバフェスですが、なんとIDCF賞をいただくことができました。叙々苑で美味しい焼き肉食べてきます!

参加賞としてRaspberry Piやセンサー、microSDカード等を提供してくださった主催者、スポンサーの皆様、色々とお話をさせていただいた参加者の皆様、どうもありがとうございました!

サバフェスは本当に素敵なイベントです!次回以降も是非参加したいと思います!

ROSのbagファイルから特定トピックをcsv形式で出力する

めちゃくちゃ簡単。

環境

bagファイルをcsv形式で出力

以下のコマンド1行でいける。

$ rostopic echo -b bagファイル.bag -p /トピック名 > csvファイル.csv

例えば、Twist形式だと以下のようになる。

$ rostopic echo -b bagファイル.bag -p /velocity

%time,field.linear.x,field.linear.y,field.linear.z,field.angular.x,field.angular.y,field.angular.z
1458645072419031373,0.133069615205,0.0,0.0,0.0,0.0,0.5369403653
1458645072485691182,0.143589122067,0.0,0.0,0.0,0.0,0.447242712703
1458645072552957164,0.153924085326,0.0,0.0,0.0,0.0,0.377736128001
1458645072620157095,0.164047896997,0.0,0.0,0.0,0.0,0.322948435252
1458645072687306707,0.173941054616,0.0,0.0,0.0,0.0,0.279093465935
1458645072754349028,0.183588971233,0.0,0.0,0.0,0.0,0.24350186043
...

特定の速度成分のみを取り出すのも簡単。

$ rostopic echo -b bagファイル.bag -p /velocity/linear/x

%time,field
1458645057721221180,0.191296330546
1458645057788266707,0.18462966388
1458645057856087891,0.177962997213
1458645057922422050,0.171296330546
1458645057989690875,0.16462966388
...

参考URL

aitendoの脈波センモジュールをArduinoで使ってみる

aitendoの脈波センモジュールArduinoに接続して脈波を取ってみます。

必要なもの

接続方法

f:id:yurayur:20160320013142p:plain

基板にマイナスマークのついている端子をArduinoGNDに、プラスマークの端子を5Vに、S(シグナル)の端子をアナログ(A0〜A3)のどこかに接続します。

f:id:yurayur:20160320014026p:plain

スケッチのダウンロード

続いて、Githubのリポジトリからスケッチをダウンロードします。

$ git clone https://github.com/WorldFamousElectronics/PulseSensor_Amped_Arduino.git

クローンされたディレクトリを開き、その中のPulseSensorAmped_Arduino_1dot4ディレクトリをArduinoワークスペースにコピーします。

$ cd PulseSensor_Amped_Arduino
$ cp -r PulseSensorAmped_Arduino_1dot4 ~/Documents/Arduino

スケッチの編集

ArduinoでスケッチファイルPulseSensorAmped_Arduino_1dot4.inoを開き、以下の行を編集します。

// pulsePinの値をセンサーを接続したピン番号に変更する
int pulsePin = 0;
void loop(){

// 中略

// LEDを光らせないので以下の1行をコメントアウト
//  ledFadeToBeat();  
}

スケッチの実行

スケッチを実行し脈波センサに指を当てると、シリアルモニタに測定値が流れていきます。

S911
S911
S335
B104
Q572
S0
S158
S550
S891
S911

Sから始まるのがセンサーの測定値、Qから始まるのが前の心拍からの経過時間、Bが現在の心拍数を表しています。

うまく脈波が取れない場合は、下図のように結構強めに指を押し当てるのがポイントです。

f:id:yurayur:20160320014838j:plain

シリアルプロッタで可視化

以下のようにプログラムを修正すると、シグナルの値のみをシリアルプロッタで可視化することが出来ます。

PulseSensorAmped_Arduino_1dot4.ino

void loop(){

// 中略

// 以下の1行をコメントアウト
//        serialOutputWhenBeatHappens();

}

AllSerialHandling.ino

void sendDataToSerial(char symbol, int data ){
// 以下の1行をコメントアウト
//    Serial.print(symbol);

    Serial.println(data);                
  }

f:id:yurayur:20160320015640p:plain

一応パルスらしきものが取れていますね。

参考URL

iframeを含んだページでBootstrapのメニューが開閉しない

Bootstrapで、iframeが含まれているページを作った時に、navbarが開閉できないという問題に遭遇しました。

f:id:yurayur:20160311011430p:plain

問題のiframeは以下の通り。

<iframe src="hoge.html" class="hoge" name="hoge" width="340" height="200">

原因は単純で、</iframe>で閉じるのを忘れていただけで、以下のように修正したら無事動くようになりました。

<iframe src="hoge.html" class="hoge" name="hoge" width="340" height="200"></iframe>

参考URL

ArduinoでSRF02のI2Cアドレスを書き換え

Arduino超音波距離センサ SRF02のI2Cアドレスを書き換える方法です。

元のアドレスを確認

デフォルトではSRF02のアドレスは0xE0(224)なので、I2CScanerのスケッチを利用すると、0xE0の上位7bitである0x70(112)にデバイスが見つかります。

シリアルモニタの出力結果

I2C device found at address 0x70  !
done

I2Cアドレスを変更

同一センサを複数個利用するケースでは、各センサのアドレスをそれぞれ別の物にする必要があります。

以下のスケッチを実行すると、SRF02のアドレスが224から226に変更されます。

変更後のアドレスを確認

再度I2CScannerを実行すると、確かにアドレスが226(上位7bitは0x71=113)に変更されています。

I2C device found at address 0x71  !
done

参考URL