うごくものづくりのために

技術的な備忘録がメインです。

Pythonのbytesの生成に関して

勘違いしていたので備忘録。

bytes(255)

b'¥xff'

とはならず、

b'¥x00¥x00¥x00 ... ¥x00'

と、255個の¥x00で満たされたbytesになる。

a = 255
a.to_bytes(1, "big")

とすれば

b'¥xff'

が得られる。

参考

python3ならintとbytesの変換が楽勝になる - BlankTar

4. 組み込み型 — Python 3.6.1 ドキュメント

Python pandasのメモ

Python pandasが難しいので、メモ。

データ構造

Series : 1次元のデータ
DataFrame : 2次元のデータ

時間系

Time Series / Date functionality — pandas 0.20.3 documentation

TimestampとPeriodの2種類がある。 Timestampは、ある時点を表す。 Periodは、ある期間を表す。

TimestampをDataFrameのIndexとして取ると、DateTimeIndexとなる。 時系列データはこれで表すと良い。 DateTimeIndexで表現されるデータを処理するための関数群が用意されている

リサンプリング

pandas.DataFrame.resample()を使う。 アップサンプリング・ダウンサンプリングともに可能。

リサンプリング時のデータの補完/集計方法はpandas.Resamplerのメソッドとして定義されている

アップサンプリングの例(線形補間)

import pandas as pd

time_index = pd.date_range("2017-10-01 10:00:00", periods=3, freq="S")
print(time_index)

df = pd.DataFrame([0, 100, 300], index=time_index)

print("-----------------------")
print("Before resampling")
print(df)

df_resampled = df.resample("500ms").interpolate("linear")

print("-----------------------")
print("After resampling")
print(df_resampled)
DatetimeIndex(['2017-10-01 10:00:00', '2017-10-01 10:00:01',
               '2017-10-01 10:00:02'],
              dtype='datetime64[ns]', freq='S')
-----------------------
Before resampling
                       0
2017-10-01 10:00:00    0
2017-10-01 10:00:01  100
2017-10-01 10:00:02  300
-----------------------
After resampling
                             0
2017-10-01 10:00:00.000    0.0
2017-10-01 10:00:00.500   50.0
2017-10-01 10:00:01.000  100.0
2017-10-01 10:00:01.500  200.0
2017-10-01 10:00:02.000  300.0

リサンプリングの注意点

サンプリング周期によっては、元データと同じ時間がリサンプリング後に現れない場合がある。(1秒間隔のデータを300ms周期でリサンプリングした場合など )
その場合、リサンプリング後に現れなかった時間のデータは補完されない。

import pandas as pd
import matplotlib.pyplot as plt

time_index = pd.date_range("2017-10-01 10:00:00", periods=5, freq="S")
print(time_index)

df = pd.DataFrame([0, 100, 300, 50, 0 ], index=time_index)

df_500ms_resample = df.resample("500ms").interpolate("time")
print("-----------------------")
print("500ms resampling")
print(df_500ms_resample)


df_300ms_resample = df.resample("300ms").interpolate("time")
print("-----------------------")
print("300ms resampling")
print(df_300ms_resample)

plt.clf()
df_500ms_resample.plot()
df_300ms_resample.plot()
plt.show()
DatetimeIndex(['2017-10-01 10:00:00', '2017-10-01 10:00:01',
               '2017-10-01 10:00:02', '2017-10-01 10:00:03',
               '2017-10-01 10:00:04'],
              dtype='datetime64[ns]', freq='S')
-----------------------
500ms resampling
                             0
2017-10-01 10:00:00.000    0.0
2017-10-01 10:00:00.500   50.0
2017-10-01 10:00:01.000  100.0
2017-10-01 10:00:01.500  200.0
2017-10-01 10:00:02.000  300.0
2017-10-01 10:00:02.500  175.0
2017-10-01 10:00:03.000   50.0
2017-10-01 10:00:03.500   25.0
2017-10-01 10:00:04.000    0.0
-----------------------
300ms resampling
                            0
2017-10-01 10:00:00.000   0.0
2017-10-01 10:00:00.300   5.0
2017-10-01 10:00:00.600  10.0
2017-10-01 10:00:00.900  15.0
2017-10-01 10:00:01.200  20.0
2017-10-01 10:00:01.500  25.0
2017-10-01 10:00:01.800  30.0
2017-10-01 10:00:02.100  35.0
2017-10-01 10:00:02.400  40.0
2017-10-01 10:00:02.700  45.0
2017-10-01 10:00:03.000  50.0
2017-10-01 10:00:03.300  50.0
2017-10-01 10:00:03.600  50.0
2017-10-01 10:00:03.900  50.0



<matplotlib.figure.Figure at 0x11268e7f0>

500msサンプリング時

f:id:tilt_silvie:20171001165109p:plain

300msサンプリング時

f:id:tilt_silvie:20171001165117p:plain

これに対する今のところの対策は、一回十分に短いサンプリング周期でアップサンプリングして、その後所望のサンプリング周期にダウンサンプリングするという手法でなんとかなる。

Trelloのカード移動をSlackに通知してくれるbotを作った。

Python3 + Flask + py-trello + slackerでTrelloの更新情報をslackに流す(1) - うごくものづくりのために

Python3 + Flask + py-trello + slackerでTrelloの更新情報をslackに流す(2) - うごくものづくりのために

上記の記事で作成したシステムを整理して、botとしてまとめてGithubで公開しました。

github.com

できること

Trelloの指定のボードで、ToDoリストからDoingリストへ、またはDoingリストからDoneリストへのカードの移動があったときに、Slackに任意のメッセージをPostする

できないこと

上記以外のこと

インストール方法、使い方等

Githubのreadmeに書いてあるので、読んでください。 英語がつらいという人はTwitter(@silvie_c)に教えてください… 日本語のreadmeを作るかもしれません。

機能追加はある?

あるかもしれませんが、あまり期待しないで待っていてください。

Python3 + Flask + py-trello + slackerでTrelloの更新情報をslackに流す(2)

前回 の記事で、py-trelloでwebhookの登録をし、Flaskでサーバを立ててTrelloの通知を受け取ることができました。

今回は、拾ってきたTrelloの通知情報をslackに投げるところを作りたいと思います。

ゴール

Trelloのウェルカムボードのカードの移動を、Flaskで作ったサーバにWebhookで通知させる。 サーバに通知がきたら、slackにPostする。

Trelloのwebhookから必要な情報だけを抜き出す

カードの移動に関する通知だけを抜き出す

Trelloのwebhookは、登録したボードに関する更新情報をすべて通知します。 今回必要なのはカードの移動だけなので、通知のうちカードの移動に関するものだけを抽出します。

通知がTrelloのどのアクションと紐付いているかは、webhookで届くデータの以下に格納されています。

action -> display -> translationKey

カードの移動に関する通知の場合は、このフィールドが action_move_card_from_list_to_list になります。

詳細な情報を抜き出す

今回は、以下の4つのデータを取ってくることにします。

  • 移動されたカード名
  • 移動前のリスト名
  • 移動後のリスト名
  • カードを移動させた人の名前

カード移動に関する通知を受け取ると、上の4つの情報を表示するように webhook_server.pywebhook()を変更しました。

webhook_server.webhook()

@app.route('/webhook', methods=['POST'])
def webhook():
    if request.method == 'POST':
        action_type = request.json["action"]["display"]["translationKey"]

        if action_type == "action_move_card_from_list_to_list":
            card_name = request.json["action"]["data"]["card"]["name"]
            before_list = request.json["action"]["data"]["listBefore"]["name"]
            after_list = request.json["action"]["data"]["listAfter"]["name"]
            user_name = request.json["action"]["memberCreator"]["username"]
    
            message = user_name + "さんが「" +card_name + "」を「" + before_list + "」から「" + after_list + "」へ移動しました!"
            print(message)

        return '', 200
    else:
        abort(400)

動かすとこんな感じ。

f:id:tilt_silvie:20170922160809p:plain

いい感じですね。

slackerを用いてslackに投稿する

Slack API Tokenを取得する

以下のを参考に、SlackBotアカウントの作成を行います。

PythonでSlackbotを作る(1) – ビットログ

Slackbotのアカウントができたら、APIトークンを secret_token.pyに追加しておきます。

slack_token = "#Your slack token HERE#"

trello_api = "#Your trello API HERE#"
trello_token = "#Your trello token HERE#"

実装

slackerライブラリを用いてSlackに投稿する仕組みは、以下の記事を参考にします。

PythonでSlackbotを作る(3) – ビットログ

先程実装したwebhook()の中で表示したメッセージをそのままslackにPOSTしてみます。

webhook_server.py

rom flask import Flask, request, abort
from slacker import Slacker
import secret_tokens

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    if request.method == 'POST':
        action_type = request.json["action"]["display"]["translationKey"]

        if action_type == "action_move_card_from_list_to_list":
            card_name = request.json["action"]["data"]["card"]["name"]
            before_list = request.json["action"]["data"]["listBefore"]["name"]
            after_list = request.json["action"]["data"]["listAfter"]["name"]
            user_name = request.json["action"]["memberCreator"]["username"]

            message = user_name + "さんが「" +card_name + "」を「" + before_list + "」から「" + after_list + "」へ移動しました!"

            # print(message)
            slack.chat.post_message("sandbox", message, as_user=True)

        return '', 200
    else:
        abort(400)

@app.route("/webhook", methods=["HEAD"])
def webhook_creation():
    if request.method == "HEAD":
        return "", 200
    else:
        abort(400)


if __name__ == '__main__':
    slack = Slacker(secret_tokens.slack_token)
    app.run(port=5000, host="0.0.0.0")

結果

f:id:tilt_silvie:20170922185149p:plain

Trelloでカードを動かした情報を無事Slackに投稿できました!

ゴール達成!

参考

PythonのslackbotライブラリでSlackボットを作る - Qiita

PythonでSlackbotを作る(1) – ビットログ

PythonでSlackbotを作る(2) – ビットログ

PythonでSlackbotを作る(3) – ビットログ

Python3 + Flask + py-trello + slackerでTrelloの更新情報をslackに流す(1)

弊チーム(https://github.com/SSL-Roots/Roots_home/wiki)では、タスクの管理にTrelloを使っています。 また、最近チャットツールとしてslackを導入したので、チームの活動に関する全情報をslack上に集約したいと思っています。

そこで、TrelloでタスクがDoneリストに入ったらslackで報告してくれるbotを作ります。 Trelloとslackの連携は公式でサポートされていますが、通知が英語だし味気ないので、タスク完了したら可愛い女の子が褒めてくれる仕組みにしたいです… ちなみに、RaspberryPi3 上で動かします。

というわけで。

ゴール

Trelloのウェルカムボードのカードの移動を、Flaskで作ったサーバにWebhookで通知させる。 サーバに通知がきたら、slackにPostする。

今回の記事では、Flaskでサーバを立てて、そこにTrello Webhookの通知がくるところまでを目指します。

インストール

Python3, pip3は入ってるという前提です。

sudo pip3 install flask py-trello slacker

Flaskでサーバを用意

Trello Webhookを受け取るためのサーバをFlaskで用意します。

webhook_server.py

from flask import Flask, request, abort
import pprint

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    if request.method == 'POST':
        pprint.pprint(request.json)
        return '', 200
    else:
        abort(400)

@app.route("/webhook", methods=["HEAD"])
def webhook_creation():
    if request.method == "HEAD":
        return "", 200
    else:
        abort(400)

if __name__ == '__main__':
    app.run(port=5000, host="0.0.0.0")

Trello Webhookの登録

Trello Webhookに、作ったサーバを登録します。 py-trelloを用いてTrello APIにアクセスしますが、その際にAPIキーとトークンが必要になります。 APIキーとトークンの取得方法は、以下のページを参考にしてください。

Trello の Webhook API をPHPで受け取ってみた - きじとら

APIキーとトークンの情報をまとめたsecret_tokens.pyを作っておきましょう。

secret_tokens.py

trello_api = "#Your API Key HERE#"
trello_token = "#Your token HERE#"

さて、Webhookの登録をします。 Webhookの登録には、WebhookサーバのURLと、通知をもらいたいアイテム(ボードだったり、カードだったり)のIDが必要です。 py-trelloを使ってアイテムのIDを取得する方法は以下のページに詳しいです。

Python + py-trelloで、Trello APIを使ってみた - メモ的な思考的な

今回は、Trelloに登録したときに必ず存在するはずの、ウェルカムボードを登録します。

trello_regist_webhook.py

from trello import TrelloClient
import secret_tokens

def main():
    server_url = "http://[自分のグローバルIP]:5000/webhook"

    client = TrelloClient(
        api_key=secret_tokens.trello_api,
        token=secret_tokens.trello_token
        )

    all_boards = client.list_boards()

    for board in all_boards:
        if board.name == "ウェルカムボード":
            welcome_board = board
            break

    webhook = client.create_hook(callback_url = server_url, id_model = welcome_board.id)

    if webhook == False:
        print("Failed webhook registration")
    else:
        print("Success!")

if __name__ == "__main__":
    main()

先程作ったwebhook_server.pyを実行させた状態でtrello_regist_webhook.pyを実行すると、Webhookが登録されます。

python3 webhook_server.py &
python3 trello_regist_webhook.py

Success!と表示されればWebhookの登録が正しく完了しています。

実験

さて、webhook_server.pyを起動している状態で、Trelloのウェルカムボード上で何かしてみましょう。 試しに、一番左の「やってみる」リストから、真ん中の「やったこと」リストにカードを移動させてみましょう。 すると…

f:id:tilt_silvie:20170917191224p:plain

Trelloの更新情報がJSONで送られてきました!

実際は、Python内部では送信されたJSONをパースして辞書形式で持っています。 あとは、必要な情報をその辞書から抜き出してやればOKです。

今回はここまで。 次回はslackに投稿する部分を作って、システムを完成させます。

参考

Python + py-trelloで、Trello APIを使ってみた - メモ的な思考的な

Trello の Webhook API をPHPで受け取ってみた - きじとら

GitHub - sarumont/py-trello: Python API wrapper around Trello's API

trelloのboardにwebhookを登録する - pblog

2017/09/22 記事修正

slackbotライブラリの代わりにslackerライブラリを使うことにしましたので、タイトルを修正。

MakerFaireTokyo2017に出展しました。

8/5,6に行われた、MakerFairTokyo2017に、RoboCup SSLチーム Rootsとして出展してきました!

f:id:tilt_silvie:20170809002654j:plain

はじめに、展示に足をお運び頂いた皆様にお礼を伝えたいと思います。
展示を見て笑顔になって頂いたり、「すごいねー」と子供たちが食いついてくれたりしたことが展示者としての何よりの喜びでした。 少しでも皆様にRoboCupSSLの魅力がお伝えできたことを嬉しく思います。興味を持っていただいた方は、ぜひ本当の試合を見にJapanOpenなどの大会にお越しください!


今回の展示は、RoboCup SSL OBチームであるOP-AmPとRootsの共同でRoboCupSSLの実演を行いました。

このような展示会に出向いてRoboCup SSLの実演をしたのは、おそらく僕達が日本で初めてになります。 当然前例のない試みですので、不安だらけでした。 フィールドカメラの画角は足りるのか、画像処理の設定がうまくいくのか、電波は混線しないか等々…
しかし流石、OBチームが2チーム集まっただけあります。苦労はありましたが、OP-AmPさんの協力もあり、大会の際よりもむしろスムーズに設営が終わったのではないでしょうか。 あまりない経験だと思うので、設営に関する情報も後ほどまとめてPublishしようと考えています。

f:id:tilt_silvie:20170809002203j:plain

展示は、ロボットの実演やパーツごとの分解展示、動画での紹介を行いました。実演展示は、Rootsがディフェンス、OP-AmPがオフェンスという形で、1−2パスシュートのデモを行いました。また、OP-AmPさんの世界唯一のカーブシュートや、ロボットがボールを所定の位置まで運ぶBall Replacementの実演も行いました。

やはりと言うべきか、お客さんに一番興味を持っていただいたのは実演展示でした。1−2パスシュートが決まる姿を見て、その場で足を止めて見てくださる方が多かったです。 さらにロボットが完全自律でサッカーをしているとわかると、驚くお客さんがたくさん。
普段の大会だとRoboCupをご存知のお客さんが多いので、初めて見る方の反応は非常に新鮮でした。驚いたり笑顔になって頂いていたのが嬉しかったですね。


反省はいろいろありますが、実演展示でOP-AmPさんにおんぶにだっこだったのが、ありがたくも悔しいところです… 次の展示のときまでに対等に渡り合えるように実力をつけないとなぁ…と感じています。

最後になりますが、展示においでいただいた方々、MFT運営の皆様、そしてOP-AmPさん、Rootsのメンバー、本当にありがとうございました。そして、お疲れ様でした!

来年のMFTでまた会いましょう〜!

GoogleProtocolBuffer(C++)で、NULL文字(/0, 0x00)を含むデータをパースする方法

GoogleProtocolBufferのC++版で、NULL文字を含むデータをパースする際にハマったのでメモ。

socket通信のrecv()で受け取ったデータをParseFromString()でパースしようとした際、 データに"0x00"が出現するとヌル文字(\0)だと解釈して、パースが正常終了しませんでした。

結論としては、char型の配列をstring型に変換し、かつ変換の際にサイズを指定してやればOKでした。

  /*** 初期設定などなど省略 ***/

  char buf[BUF_SIZE];
  ProtobufClass proto;  // プロトコルバッファのメッセージ解析用インスタンス
  size_t recv_size;

  recv_size = recv(sock, buf, sizeof(buf), 0);

  std::string str(buf, recv_size);
  proto.ParseFromString(str);