日記とか、工作記録とか

自分に書けることを何でも書いてゆきます。作った物、買ったもの、コンピュータ系の話題が多くなるかもしれません。

Logicool G602マウスの修理(挫折編)

(あとからつけた)目次

  1. Logicool G602マウスのチャタリングの修理(解析編) - 日記とか、工作記録とか
  2. (今回)Logicool G602マウスの修理(挫折編) - 日記とか、工作記録とか
  3. Logicool G602マウスの修理(ゾンビのように復活編) - 日記とか、工作記録とか

前回のつづきです。

windvoice.hatenablog.jp

マウスのスイッチの種類が判別できましたので、スイッチを購入しました。ばら売りされていたのでそちらを選択。

既設のスイッチを取り除くための半田ごてと半田吸い取り線も購入。YouTubeなど眺めていると、半田ごては温度調整ができるものがよいようなのでそちらを選びました。

白光 ダイヤル式温度制御はんだこて FX600

白光 ダイヤル式温度制御はんだこて FX600

ホーザン ハンダ吸取線 NO.3738

ホーザン ハンダ吸取線 NO.3738

しかし、半田吸い取り線を当てて半田ごてをあてても、スイッチを取り外せず…… 吸い取り線はちょっとだけハンダを吸い取ったのですが、しかし穴の中に残ってしまったようです。30分くらい悪戦苦闘したのですがやっぱりだめ。基板が熱で焦げてしまったりしています。

作業前

f:id:WindVoice:20160831223229j:plain

作業後

f:id:WindVoice:20160831223931j:plain

どうにもならず、だんだんやけになってきてニッパで脚を切ってから半田ごてを当てて取り除いてみたところ、3本のうち2本までは抜けたのですが、最後の1本がどうしても抜けず。iPhoneにつけたマクロレンズで写真を撮ると惨状がよくわかります(肉眼で見るとそこまでひどくは見えないのですが)。

強引に抜いた脚

f:id:WindVoice:20160901203458j:plain

最後の1本がダメなのは、おそらくニッパをあてたときに脚が変形して穴から抜けなくなってしまっているからですかね。どうにかなるものなら修理を続けたいのですが。方法がわからずいったん放置となっています。

Logicool G602マウスのチャタリングの修理(解析編)

(あとからつけた)目次

  1. (今回)Logicool G602マウスのチャタリングの修理(解析編) - 日記とか、工作記録とか
  2. Logicool G602マウスの修理(挫折編) - 日記とか、工作記録とか
  3. Logicool G602マウスの修理(ゾンビのように復活編) - 日記とか、工作記録とか

自宅のPCではいわゆるゲーミングマウスを長いこと使っています。ゲームをそれほどするわけではないのですが、補助ボタンがたくさん付いていると、ブラウザの進む戻るとか、ウインドウを閉じるなどよく使う操作をショートカットできて便利です。今使っているのがLogicool G602です。無線マウス、適度な重さ、大き目のボディは掴みやすいなど気に入っています。

が、マウスは消耗品です。何年使ったか覚えていませんが、左クリックの調子がおかしくなってきました。普通にクリックしたはずなのにダブルクリックとして扱われます。いわゆるチャタリングノイズです。以前Raspberry piの回路にスイッチを設置した時にはプログラム側からこれを防止しました。

windvoice.hatenablog.jp

マウスは消耗品ということで、このマウスを買い換えました。同じものがまだ販売していたので再度注文。めでたしめでたし…… だったのですが、よく考えたらもしかして直せるのではないかと思いつきました。今回はマウスの分解です。マウスは裏のツルツルしたソールを剥がすと、固定ネジが出てきます。これを外すと、本体が上下に分かれて基盤が出てきます。

f:id:WindVoice:20160827224956j:plain

クリック部分のスイッチはこんな配置です。

f:id:WindVoice:20160827225354j:plain

左右のクリックは同じ型番のスイッチでした。ここのWebサイトを参考に分解しましたが、部品変わっていませんね。数字の刻印が違うのはシリアル番号でしょうか。

f:id:WindVoice:20160827225657j:plain

分解する前は、ホコリなどでスイッチの接触がおかしくなっているのでは、と想像していたのですが、本体の中は綺麗なものでホコリが原因とは考えられませんでした。となるとスイッチの摩耗などを疑うことになるのでしょうか。スイッチを交換できるか型番で探してみたところ、なんとAmazonにこんなものまで置いてあるんですね…… 驚きです。

代わりのマウスは買ってしまいましたが、それほど高い部品でもないので交換にチャレンジすることにします。現在部品の到着待ちです。もしうまくいかなかったら、次はレーザ発信器を使って何か遊びを考えることにします。

Javascriptでチェスクロック(対局時計)を作成しました

今回はRaspberry piからは離れてJavascriptなど……

前置き

趣味で少し将棋をやるのですが(もっぱら見る将ですが。最近はアゲアゲさんのチャンネルなど……)、将棋やチェスでは持ち時間があり、この時間内に対局を終わらせることが求められます。例えば持ち時間10分であれば、一局100手あまりの対局の時間配分を考えて、あまり悩まずに指し続けなければなりません。時間切れになったらそこで「切れ負け」です。

ルール的には持ち時間が尽きたらその後は一手30秒、などの方法がとられることもありますが、いずれにせよこれを管理する対局時計というものが必要になります。例えばこんなの。

これはよく使われている一般的なもので「お願いします」なんて自動音声がでたり、秒読みをしてくれたりもする高機能な製品です。便利ですしおそらくボタンも丈夫に作ってあることでしょう(※自分の手を指したら対局時計の上部にあるボタンをパン、とたたくと相手方の時間が減り始める仕組み。けっこうぞんざいに扱われる宿命のボタン)。そして結構いいお値段もします。きっと大量生産できないので単価が高かったり、そもそもネット将棋ならこれは必要なかったりで、そんなに売れないのかと思います。

それでも作ってみる?

仕組みはそれほど難しいものではないので、jQuery + Javascriptでごりっと作ってみました。Amazon Web Services(AWS)にある自分のサーバに置いてあります。安易ですが「ちぇくろ」と名前をつけました。

ちぇくろ

画面表示はこんな感じです。シンプル・イズ・ベター。

f:id:WindVoice:20160809132309p:plain

最初の設定画面だけAmazonのバナーがでますが、対局中は非表示ですのでご容赦を…… だって遊び用のインスタンスとはいえ、料金も少しかかっていますからね。ナニトゾナニトゾ。

特にiPhone / iPadなどのモバイルで便利と思います。将棋盤の隣にこれを置いておいて、一手さしたら画面をタップ、という感じで使います。タップするときは落ち着いてやらないと、スワイプと判断されてうまく動かなかったりしますのでご注意ください。今後気が向いたらアップデートをしようとおもいます。

スペシャルサンクス

プログラムは自前で書けるのですが、音とかフォントはふぉんとにどうにもならないのでお借りしています。
些細なリンクで済みませんがせめてもの感謝ということで……

秒読みの効果音はこちらから
 ⇒ select02.mp3 / select07.mp3 / blip01.mp3

タップしたときの効果音は効果音ラボさんから
 ⇒ cursor8.mp3

残り時間表示のための7セグフォントは消し缶詰-keshikan.netさんから
 ⇒ DSEG7Classic-Bold.ttf

追記

Android端末をお持ちの方は動作確認して頂けると助かります。私は持っていなくて確認がとれていません。iPhoneSafariでも動いていますし、jQueryは互換性も考えられているはずで、たぶん大丈夫とは思うのですが。

Raspberry pi + Twitter : 謎の例外が飛んできても動揺せずにしゃべり続ける

動作を安定させるために

しばらく動かしたままにしておいた @RaspiParrot ですが、いつの間にか止まっていました。

twitter.com

何か動作不具合が起こっているに違いないのですが、ログではTwitterにアクセスしたところで突然死んでいることしかわかりません。Python-TwitterモジュールはTwitter APIのエラーが発生した場合にTwitterError Exceptionをraiseするようになっており、私のプログラムも例外処理をしているのですが、今回は突然停止していることからそれ以外の例外が発生しているとみられます。このままでは原因追求ができないので、標準エラー出力をファイルに取得しておきました。

pi@pi3:~/twitter$ more boot.sh 
#!/bin/bash

./RaspiParrot.py 1>> log/stdout.log 2>> log/stderr.log &

pi@pi3:~/twitter$

で、今回停止した時に取得された標準エラー出力がこれ。実際にはもっと長いのですが要点を抜粋です。

Traceback (most recent call last):
  File "./RaspiParrot.py", line 201, in CheckMentions
    for state in api.GetMentions():
  File "/usr/local/lib/python3.4/dist-packages/twitter/api.py", line 3430, in GetMentions
    resp = self._RequestUrl(url, 'GET', data=parameters)
  File "/usr/local/lib/python3.4/dist-packages/twitter/api.py", line 4772, in _RequestUrl
    resp = requests.get(url, auth=self.__auth, timeout=self._timeout)
  File "/usr/lib/python3/dist-packages/requests/api.py", line 60, in get
    return request('get', url, **kwargs)
  File "/usr/lib/python3/dist-packages/requests/api.py", line 49, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/lib/python3/dist-packages/requests/sessions.py", line 457, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/lib/python3/dist-packages/requests/sessions.py", line 569, in send
    r = adapter.send(request, **kwargs)
  File "/usr/lib/python3/dist-packages/requests/adapters.py", line 407, in send
    raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', gaierror(-2, 'Name or service not known'))

つまり接続エラーな訳ですね。今のところ、DHCPIPアドレスの解放と再取得の動作が怪しいと感じていますが、原因追求のためにもう少し細かいエラーを取得することにします。他の問題も起こるかもしれませんし。こんな感じで未知のExceptionも最低限ログが残るようにします。

    try:
        api = twitter.Api(consumer_key=consumer_key,
                       consumer_secret=consumer_secret,
                       access_token_key=access_key,
                       access_token_secret=access_secret,
                       input_encoding=encoding)
        logger.info(u"GetAPI:正常終了")
    except TwitterError as e:
        logger.warning(u"twitter.Api error: %s" % ( e.message ))
    except Exception as e:
        logger.warning(u"Exception[0]%s [1]%s [2]%s" % (sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))

これで再始動して様子見です。少なくとも、エラーが発生してもそのまま死なずにリトライすることを期待しています。さて、何が釣れますやら……

オウムらしからぬ適当なことを喋る

目次 [Raspberry pi 3 + Python = Twitter Bot]

  1. Twitterの開発者登録からツイートをポストするまで
  2. Python-Twitterモジュールの情報まとめ
  3. メンションをもらったら返答するようにした
  4. 異常終了の原因を追及できるように例外の処理を追加
  5. マルコフ連鎖モジュールのPython3対応作業

前回までに、マルコフ連鎖に関連するモジュールをいくつか改造しまして、動作するようにしました。これは、ツイートを自動生成する処理の基板にあたるものです。今回はようやく独自のツイートを生成して投稿するところを作ろうと思います。

つくっていて急に心配になったのですが、オウムも場合によっては人語を喋りますよね。九官鳥じゃなかったかと心配になりました。他所様の動画ですが、これすごいですね。オウムが笑うと子供がつられて笑うので、ますますオウムが笑うようになるという…… 恐ろしい連鎖です。

youtu.be

ツイート生成の方針

これまでに、トレンドワードを取得してリストにするところまではできていました。これをツイートにまとめて投稿することもできます。

f:id:WindVoice:20160724212621j:plain

トレンドを取得すると、キーワードだけでなく、関連する情報もついてきます。キーワードは trend.name という名称で取り出せるのですが、他にも trend.url という名前で、関連ツイートを検索するURLが取得できます。このURLをSearchして、他のツイートをまとめ、それをマルコフ連鎖モジュールで「学習」したあと、自分なりのツイートを生成することにします。できあがりはこんな感じです。

f:id:WindVoice:20160724212910j:plain

ただ、取得したツイートを学習させるだけだと、他の方のツイートをそのままパクツイしたようなものも生成されてしまいます。それに他人を中傷するツイートも少なからずあるので、安易にマネしてしまうのは危ないこともあります。そんなわけで、カタカナに展開することでインパクトを和らげることにしました(まぁ、オウムですし……(逃避))。ただ、全部カタカナだと何を言っているのかわからないので、トレンドワードそのものは残しておくことにしています。

twitter.com

技術的なところ

マルコフ連鎖は、前回Python3対応の作業をした python-markovchains と、その前提モジュールとなる py-extractword を使用します。

具体的には、こんな流れになりますね。あとはひとつひとつ地道に作っていくだけです。

  • 地域の指定を「日本」にしてトレンドワードを取得(twitter.GetTrendWoeid)
  • trend.url を使用してトレンドワード関連ツイートを取得(twitter.GetSearch)
  • ツイート群からテキストを取り出し(status.text)、マルコフ連鎖の邪魔になるゴミを取り除く
  • テキストを全部連結してmarkovchains.analyze_sentence()で学習させる
  • markovchains.make_sentence()で文章を作成する。ほどよいのができるまでリトライ
  • MeCabを-Oyomiオプションで起動して、読み仮名を取得する
  • できあがったツイートを投稿

今回のハマリポイント

markovchains.make_sentence()が、無限に長い文章を生成して応答を返さなくなることがあります。プログラムがCPUを100%使用する状態のまま止まってしまい原因を探るのに苦労しました。そういうときはpython -m pdbを使用してデバッガから動作を確認するのが早道かとおもいます。

この問題は、python-markovchainsのmake_sentence()関数の中に処理を追加して対処しました。ツイッターではどうせ140文字を超えたらツイートできないのですから、漢字を含めて100文字くらいで終わってくれないと、使えません。ある程度長くなったところで諦めて結果を返すように動作変更しました。GitHUBからforkしたのは今回が初めてですが、やってみるものですね。

次回予告?

気の向くままに作業していますが、それでもフォローしてくれる人がいるので、フォローが届いたらフォロー返しをしようかと思います。多くのBotはこちらから積極的にフォローしていきますが、そこまでやっても得るものがあるかどうか…… しばらくはやめておきましょう。

Raspberry Pi 3 Model B (Element14)

Raspberry Pi 3 Model B (Element14)

Raspberry pi 3 + Twitter [RaspiParrot] 他のプログラムのPython3対応作業

目次

  1. Raspberry pi 3 + Python = Twitter Botの起動(とりあえず投稿するところまで)
  2. Python Twitterのメモ
  3. Raspberry pi 3 + Python = Twitter Bot(Mentionをとりあえず返せるところまで)
  4. 例外の処理を追加

現在の作業目標は、Twitterのトレンドワードに関連したツイートを多数集めて、それらに使われている単語を合成し、何か面白そうなツイートができないかな、というものです。

トレンドワードに関連するツイートを収集

Twitterにはトレンドという情報があります。多くの人がつぶやいているキーワード(ニュースとかテレビ番組とかの話題が多い)がリストになったものです。この情報の入手は簡単で、Twitter APIとしてすでに実装されていますので、割合スムーズに収集することができました。部分的なソースを引用しますが、GetTrendsWoeid()という関数が、Twitter APIの下のマニュアルの機能を呼び出しています。WOEIDというのは、地球上の特定の場所を示す郵便番号的なもののようです。私のソースでは23424856を使っており、これは日本を示すWOEIDです。都道府県単位でも変更できるようです。

dev.twitter.com

def CheckTrendWord(api):
    '''
    時々トレンドを入手してコメントしたりする
    '''
    global trendticker
    try:
        logger.debug(u"trendticker:%s" % ( trendticker ))
        if trendticker != 0:
            trendticker -= 1
        else:
            trendticker = int(inifile.get("trend","frequency"))
            # woeid = 23424856は、日本のトレンドワード
            trend = api.GetTrendsWoeid(woeid=23424856)
            # トレンドに関連したツイートを調査
            AnalyzeTrendTweet(api, trend)
            # トレンドワード一覧をツイート
            PostTrendWord(api, trend)
    except TwitterError as e:
        logger.warning(u"CheckTrendWord Error:%s" %( e.message ))

で、とりあえず @RaspiParrot はトレンドワードを並べて定期的にツイートする機能を実装しています。Botにありがちな機能ですね。昨日はポケモンGO関係のキーワードばかりになっていてテストとしてはイマイチな感じでした。

f:id:WindVoice:20160723225643p:plain

しかし、正直これだけだと、フォローしたいアカウントとは言えないですね。もっと面白くしなければ……

独自のツイートをつぶやかせたい

マルコフ連鎖、という技術があります。たくさんの既存の文章から、単語と単語のつながりに関する情報を学習して、ある単語の後には別のある単語が来ることがある…… といった情報を集積することで、今度はその情報をもとになんとなく意味が通りそうな文章を生成できるというものです。単語と単語のつながりしか見ていないので、文章や文脈の全体としては、まとまった内容になりません。何も知らずに読んでいると、酔っているか、頭のおかしい人が支離滅裂なことを言っているように見えます。でも、今作っているのはBotですし、名前もオウム(Parrot)ですので、その辺りはご愛嬌ということにしまして、ともかく独自の文章を生成するプログラムの作成を進めることにします。

実は何年も前に書いたマルコフ連鎖のプログラムが手元にあるのですが、Perlで書かれているので、Pythonに書き換えるのも手間がかかります。どうしたものかと思っていたらすでに作成している方がいたので、GitHubにあったソースコードをforkさせてもらうことにしました。

github.com

すぐ使えるかと思ったのですが、Python 2系列で使うことを目的にしていて、そのままでは動作しませんでした。また、MySQLPostgreSQLPythonモジュールがインストールされていないと、これらのデータベースを使わない場合でも動かない、というちょっと困った依存についても排除したかった(だってそれらをインストールするにはまたPython3の壁に突き当たるので……)ので、forkして修正させてもらいました。その私のレポジトリがこちら。

github.com

まだ広汎なテストはできていませんが、少なくともサンプルレベルであれば動きます。前提パッケージになっているpy-extractwordの方もPython3向けに修正が必要でしたのでforkしました。情報を集積するデータベースとしてMySQLPostgreSQL は使いたいのであれば使える(はず)ですが、テストはしていません。私としては、使わずに済ますつもりでいます。

github.com

さらに前提としてMeCabpython-mecabも必要なのですが、こちらもPython3で動かすのに苦労したものの、なんとかなりました。ネットを検索すると他の方の足跡とともに多数の情報が見つかりますので、ここには書きません。必要でしたら探してみてください。

準備完了……? のつもりで次回予告

これで作りたいプログラムの前提になるモジュール類は揃ったと思いますので、ようやくトレンドツイートを収集して、学習して、なんとなく今はやりのツイートっぽい発言をするオウムの作成を目指そうと思います。

Raspberry pi 3でTwitter [RaspiParrot]

#おそるべしPokemon Go…… ってのはさておきまして。

安普請だったのでもう少し堅牢に修正

昨日動かしたままにしておいたRaspiParrotなのですが、謎のエラーで停止していました。

2016-07-21 18:40:40,130 INFO ----- OneCycle開始 -----
2016-07-21 18:40:40,131 INFO Twitter APIの使用を開始します。
2016-07-21 18:40:40,146 INFO Starting new HTTPS connection (1): api.twitter.com
2016-07-21 18:40:40,842 INFO 返答済みのMention:[2016-07-21 17:52:19] WindVoice_en
2016-07-21 18:40:40,843 INFO 返答済みのMention:[2016-07-21 16:21:50] WindVoice_en
2016-07-21 18:40:40,843 INFO 返答済みのMention:[2016-07-21 15:06:49] WindVoice_jp
2016-07-21 18:40:40,844 INFO 返答済みのMention:[2016-07-21 10:32:28] WindVoice_jp
2016-07-21 18:40:40,845 INFO 返答済みのMention:[2016-07-21 09:19:22] WindVoice_jp
2016-07-21 18:40:40,845 INFO ----- OneCycle終了 -----
2016-07-21 18:43:40,945 INFO ----- OneCycle開始 -----
2016-07-21 18:43:40,946 INFO Twitter APIの使用を開始します。
2016-07-21 18:43:40,962 INFO Starting new HTTPS connection (1): api.twitter.com
pi@pi3:~/twitter$

なぜ謎のエラーなのかといいますと、記録が残っていないためです。上のとおり、ログは突然途切れています。サドンデスですね。これでは原因が突き止められないので、まずは情報採取をしっかりやりましょうということで改造します。

例外を処理

Pythonには「例外」という機能があります。何かまずいことが発生したときに、本来のプログラムの段取りとは別に、「突然ですがここで緊急ニュースです」とでもいった調子で始まる処理のことです。どんな「例外」が発生しうるのかはそのときやっている処理によるのですが、Python-Twitterの場合は、"TwitterError"という名称の「例外」が定義されており、これが発生(raise)します。下のリンクはソースコードです。ところどころにraiseという命令が置いてあります。

python-twitter/api.py at master · bear/python-twitter · GitHub

さて、例えばtwitter.api()を呼び出してkeyからアクセス権を取得するとき「アクセスしすぎでしょエラー」が発生することがあります。15分で15回以上アクセスすると、Twitter側でそれ以上の処理を拒否するようです。この場合、しばらく待ってリトライするのが正しい処理ということになりますね。

下のようにtry~catchを使用しました。しばらく待ってリトライするので、ログに残すだけでよいはずです。なお、発生した例外に対応する処理が作られていない場合、プログラムは強制的に終了させられます。RaspiParrotのような常駐プログラムでは、これは致命的ですので注意する必要があります。

def GetAPI():
    '''
    Twitter APIを準備する部分
    '''
    global inifile
    logger.info("Twitter APIの使用を要求")
    consumer_key    = inifile.get("keys", "TWEETUSERNAME")
    consumer_secret = inifile.get("keys", "TWEETPASSWORD")
    access_key      = inifile.get("keys", "TWEETACCESSKEY")
    access_secret   = inifile.get("keys", "TWEETACCESSSECRET")
    encoding        = 'utf-8'
    try:
        api = twitter.Api(consumer_key=consumer_key,
                       consumer_secret=consumer_secret,
                       access_token_key=access_key,
                       access_token_secret=access_secret,
                       input_encoding=encoding)
    except TwitterError as e:
        logging.warning(u"twitter.Api error: %s" % ( e.message ))
    except Exception as e:
        logging.warning(u"不明なエラー")

GitHub更新しました

更新したソースコードGitHubに上げておきました。
github.com