パーツ買い集めてキーボード (Ergodox) 組み立てるのがめっちゃ楽しい話
先月 Ergodox を組み立てたのだけど,プリント基板を発注したりはんだ付けしたりと,なかなか楽しい体験だったので記録を残そうと思う.
先にでぃすくれいまー書いとくと,自作キーボードのガチ勢な方々は基板設計から始まるっぽい*1から,僕がやった「パーツ買い集めて組み立てる」というムーブはプラモを説明書通りに作っただけで,エンジョイ勢レベルになると思う.技術的にすごいことをやっているわけではない.そこをご理解頂いた上でお読みください.
久々にブログ書くんで戯言欲を持て余し,クソ長く,いらん無駄話も入るけどまぁよかろ.
Ergodox ってなんじゃいな
Ergodox についての詳しい説明は省くが,僕が 2016 年頃から愛用している,オープンソースのセパーレト式メカニカルキーボードである.
キーボードがオープンソースとはどういうことかと言うと,キーボードのプリント基板 (PCB) が GitHub に公開されていたり,マニュアル*2をもとに有志が組み立て手順をネットに上げていたり,キーボードファームウェアとしてこれもオープンソースの qmk_firmware を採用していたり,みたいな.PCB にキースイッチを初め諸々のパーツをはんだ付けで接続していくことになるのだが,必要なパーツは秋葉原や通販で揃えることができて,あっちこっち探し回るのが RPG みたいでわくわくする.
ファームウェアは C 言語で書かれていて,自分好みにキーレイアウトをカスタマイズすることが出来る*3.僕のキーレイアウトは qmk_firmware を fork して自分のレポジトリに置いているのでよかったら見てみてください.QWERTY ベースなので基本フツーだけど.
見たとおりキーの数が多くて(片手 38 キー x 2 = 76 キー),特に親指周りが充実しているのが嬉しい.基本的に US キーボードレイアウトで使っているが,もちろんあらゆるキーボードと同様に Karabiner-Elements と組み合わせることで Mac 純正日本語キーボードのように左右親指で英数/かな切り替えを実現するなどの拡張も可能.そもそも qmk_firmware 上の定義だけでもわりと色々できてびっくりする.たとえば音量やマウス操作も特定キー組み合わせに割り当てることができるし,僕は Copy/Cut/Paste や Ctrl+F1 (Mac のアプリケーションウィンドウ切り替え) を親指ワンストロークで発行できるようにしている.
さらに Layer を切り替えることでレイアウトをがらっと変えることも出来る.僕は Layer #2 はメールを流し読みして削除フラグ立て等のアクションが片手でできるモード,Layer #3 は数値と記号だけのテンキー状態にしている.
ただ充実しすぎて,正直五段目のキーや親指島,多段レイヤーを 100% 活用しきれてないと思う.なんか面白いアイディアあったら教えてください.詳しい説明は省くとか言いながら割と語ってしまった.
Ergodox 組み立てに至る経緯
さて.Ergodox はオープンソース...だけど,僕は軟弱者なので,Ergodox EZ の組み立て済バージョンを購入して三年半ほど使っていた.三万円くらいしたけど毎日仕事に遊びに活用してきたから完全に減価償却済である.
それが昨年,ついに壊れた.というかキーがいくつかチャタリングを起こすようになってきて,僕が構造をよく理解しないまま,はんだ付けされたキーを外そうと基板を無駄に焼いて動かなくしてしまったので純然たる自爆であるとも言える.
で,軟弱者なので次の Ergodox EZ を購入した.旧 Ergodox EZ は基板だけがダメになってることが予想できたので,購入ついでに Ergodox EZ 販売元である ZSA Technology Labs に「基板だけって売ってないの」と聞いてみた.基板だけ買って復活させれば,自宅と会社,両方に Ergodox のある生活が送れるためだ.すると,
返信で CEO Erez さんが直々に「オープンソースだから基板 manufacture すればよくね」とメールくれたので「確かに.そうするわ!」とお返事.Best of luck! :) とのこと.利益にならないのに親身になってくれてやさしい.
なるほど製造業者に基板を発注して新しいやつを入手しつつ,旧 Ergodox EZ を分解してケーブルやケースを流用すればいいだろう.他の人の組み立てブログを読んでケース結構高ぇなと思った記憶がある.他の電子部品はどうだろう? 理想的には基板だけ新しくして既存のパーツは旧 Ergodox EZ から desoldering して回収すれば動かなくもないと思う.でもきれいにはんだ付けを取り外せるスキルは無いし,せっかくだから電子部品は新しく買い集めて作ってみるか.
ようやく始まりである.
基板の発注
まずは何はともあれ PCB の発注.調べたところ,Ergodox の PCB 元データは確かに GitHub 上にあるが,どうやらガーバーファイルという形式で発注しなければならないらしい.そこで,KiCad をダウンロードして Ergodox PCB ファイルを開く.最初混乱したんだけど,面白いことに Ergodox は 1 枚の基板の両面がそれぞれ左手用と右手用になっている.だから,2 枚同じ基板を発注すればいい*4.セパーレト式キーボードだとよくあるのかな.
で,これをガーバーファイル形式で出力する(参考: ガーバーファイルを出力する方法 – Feedback & Ideas for seeed).これでよし.
次に,発注.選んだ製造業者は,Speeed というところが運営する Fusion PCB なる基板製造サービス.個人利用の小規模スケールで基板作ってくれるサービスけっこうあるっぽい.
これまた「ガーバーファイルたくさんあるんすけど... どれ渡せばいいんですか (´・ω・`)」とメールで担当者に泣き付きながら教えて頂き,無事発注完了.担当者は中国の方だったみたい.世界はやさしい.
以下のファイルを zip にまとめて web から基板製作を依頼.送料込みで $73.2 (=~ ¥7900) くらいかなぁ.
人生をやっていると,確か一週間くらいで中国から基板が届く.結構速い.
ちなみに発注後,実はガーバーファイルも GitHub レポジトリに存在することに気が付いたので KiCad のくだりは完全に無駄足であった.でもまぁ基板設計 CAD いじくり回すとかいい経験だろう.KiCad 回路図のネットワーク配線情報が一部 S 式? で書かれているようで脳の配線がバグる.
なお本件は総じてこのような無駄足による経験が積めて最高だった.仕事だと,なかなか知識欲ドリブンの無駄な遠回りを楽しめない.
パーツを揃える
Ergodox 基板をゲットしたので,その上にはんだ付けしていくパーツを揃える.公式サイトによると全パーツリストは以下の通り.
整理する.
個数 | 値段小計 | 英語名 | 解釈 | 入手場所 |
---|---|---|---|---|
1 | ¥7900 | Pair of pcbs (one for each hand) | PCB 基板 1 組(左右の手で計 2 枚).先の段落で発注したやつ | Fusion PCB にて最小発注枚数の 5 枚で注文 |
1 | ¥1200 | Teensy USB Board, Version 2.0 | Teensy なる USB マイコン | Teensy 2.0 USB Keyboard Mouse AVR for arduino ISP Board - AliExpress からゲット |
24 | ¥0 | Teensy header pins, male (unless pre-installed) | Teensy を基板から浮かせる形で指すトゲトゲしたやつ | ↑で Teensy 2.0 買ったら付いてた |
1 | ¥635 | MCP23018-E/SP I/O expander | 最大 16 本の I/O を I2C プロトコルを使って 2 本で読み書きできるようにしてくれる | IC I/O EXPANDER I2C 16B 28SDIP MCP23018-E/SP - マルツオンライン *5 |
76 | ¥8500 | Cherry MX switches | 各キーに一個一個つけるキースイッチ*6.好みは無数だけど僕は Cherry 赤軸を選択 | 10ケセット Cherry MXキースイッチ(赤軸) メカニカル押しボタンスイッチ ジェイダブルシステム を 8 個注文 |
76 | ¥200 | 1N4148 diodes, SOD-123 package (Surface mount) or DO-35,(0.3” pitch) (through hole) | 76 個のダイオードを全キーにちまちまと付ける.ダイオードは電流を一方向にしか流さない.最初にエンドレスはんだ付けで心を折ってくる | 秋月電子で 50 個セットのを 2 パック |
2 | ¥100 | 2.2k Ω resistors (red, red, red) | 抵抗.そうそう抵抗値によって線の色が違うんだった.中学の授業でやったな | 秋月電子で最小単位 100 本 |
3 | ¥0 | 3mm T1 LEDs | これ結局買ってない.なんか基板上に付ける場所なくない? USB 挿した時に光らせる部分だと思う | -- |
3 | ¥100 | 220 Ω resistors, or match to LED. (red, red, brown) | ↑の 2.2k とは違う 220 抵抗.線の色が赤・赤・茶だ | 秋月電子で最小単位 100 本 |
5 | ¥0 | Short jumpers (You can also use the clipped off legs from your resistors) | これなんだかよくわからずに秋月で「ジャンパワイヤ」みたいなの買っちゃったんだけど,不要だった.抵抗はんだ付けして切り取ったゴミ...じゃなくて足部分を使えば OK | -- |
1 | ¥100 | 0.1 µF ceramic capacitor (marked “104” for 10*104 picofarad) | セラミックコンデンサ.カイガラムシみたいな形してやがる.必須じゃないけどあればベター*7とのこと.電圧を安定させる効果があるらしい? | 秋月電子 |
1 | ¥110 | USB mini B connector WM17115 | USB mini B のメス.お使いの PC から繋ぐ口 | 千石電商 (秋月電子にはなかった (間違えて表面実装のモデルを買ってしまったため through hole モデルを買い直すという二度手間が発生)) |
1 | ¥256 | USB mini B plug with short cable (such as H2955) | これ意外と難関.作り進めてようやく何のことか理解したのだが,USB mini B ケーブルをご用意してUSB ケーブルを切ってほぐして繋ぐという外科手術を行う必要がある.分解するのけっこう大変なので,もしかすると USB mini B オス側を買って,自分でケーブル繋いだほうが良いかも知れない | エレコム USBケーブル 【miniB】 USB2.0 (USB A オス to miniB オス) ノーマル 0.5m ブラック U2C-M05BK |
1 | ¥0 | USB cable male A to male mini B | これは要はあれです.お使いの PC とキーボードを繋ぐやつ | 旧 Ergodox EZ についていたやつをそのまま利用 |
2 | ¥700 | 3.5 mm TRRS sockets, CP-43514*8 | 左右のキーボードを接続する際の受け口.調べて驚いたんだが,TRRS って イヤホンを差し込むあの丸いジャコっとした手応えのやつの規格らしい.不思議! | CONN JACK 4COND 3.5MM R/A SJ-43514 - マルツオンライン |
1 | ¥0 | Cable with two 3.5 mm TRRS plugs | 左右のキーボードを繋ぐ線.↑の TRRS sockets 間を繋ぐよ | 旧 Ergodox EZ についていたやつをそのまま利用 |
1 | ¥0 | Ergodox Keyboard case | 見た目を左右するやつですね.キーボードケース.僕は旧 Ergodox EZ のケースが流用できるぜと気楽に考えていたんだが,後述するようにこれが大変だった. | 旧 Ergodox EZ についていたやつをそのまま...いや,一部加工して利用 |
76 | ¥0 | MX Keycaps | キースイッチの上に被せるやつ.僕は Ergodox EZ 無刻印の黒が気に入ってるのでそいつを流用.無刻印であるとはすなわちノーヒントで組み立てる必要があるということ | 旧 Ergodox EZ についていたやつをそのまま利用 |
実物写真などは,はんだ付けしていく段階で改めて乗せる.
値段(通販の場合は個々の送料含む)を合計すると ¥19800 程になる.一方で組み立て済 Ergodox EZ を買うと,付属パーツ等を含めて $325 (=~ ¥35000) くらい*9.自作すると Ergodox EZ で買う時と比べて 60 % 程度の価格で入手できるが,激安!ってほどじゃないし時間もかかるので,組み立てプロセス自体に楽しみを見いだせる人向けの選択肢かなぁやっぱり.細々した電子部品は誤差レベルの安さなので,基板をもっと安価に作る手段があれば,ほぼキースイッチの値段だけで作れそうな気もする.あ,あと僕は特殊だったけど,ふつうはこれに加えてキーボードケースも何らかの方法で入手する必要があるな.
秋月電子と千石電商は通販もやってるみたいだけど,僕が買ったのは秋葉原にある実店舗.秋月電子・千石電商に足を運ぶついでに,秋葉原から歩いて行ける自作キーボード専門店 遊舎工房 にも寄ってみたり.
楽しい.
はんだ付けの素振り
そもそもはんだ付け自体がたぶん学生時代以来*10だしあのへんの所作 (?) がよくわかっていないので,いきなり貴重なパーツをダメにする悲劇を避けるべく,まずは安い工作キットを買って,なんとなく素振りをしておいた.
HiLetgo® 5個セット NE555 + CD4017 流水ランプ ライトLEDモジュール DIYキット 電子プロダクションスイート [並行輸入品]
できた.
はんだごてを買ったので工作して遊んだ pic.twitter.com/VXlnXnerB7
— 俺のミームを受けてみろ (@T_Hash) December 5, 2019
楽しい.
はんだ付け
まずは道具を買いましょう
何はともあれはんだごて.僕は一度よくわからん安いの買っちゃったんだけど,最初から白光 (HAKKO) の温度調節機能付きのやつを買ったほうが間違いないと思う.
はんだ付けは 270 ℃,はんだを溶かして除去する時は最高の 500 ℃でやっていた.以下,その他に僕の買ったもの.
- 白光(HAKKO) 巻はんだ 150g 直径0.8mm FS402-02
- はんだごて台: goot はんだこて台 ST-11
- 繊細なピンセット: エンジニア 鉄腕ピンセット PT-17
- 神ニッパー: goot ニッパー YN-10
- パチンと切ったあと破片が飛び散らない.すごい.
- 3M シールはがし クリーナー30 強力 ハードタイプ 100ml Cleaner30 MINI
- Ergodox "EZ" を分解する際には裏のシールを剥がしてネジを外す必要があるのだが,このシールが結構べたつく.なのでお掃除のためにシールはがしを購入.もっちもちの粘着剤が落ちて気持ちいい.
- はんだ吸い取り器: エンジニア ハンダ吸取器 SS-01
- 後述するが,旧 Ergodox からケースを入手するために,すべてのキー(つまり 76 個である)のはんだ付けを取り外す必要があった.
以上の仲間たちを使ってはんだ付けしていく.
組み立て手順は何度も言及している Ergodox 公式サイト のほか,旧 Massdrop のページ がわかりやすい (archive だけど).まぁでも,実際の作業をイメージするためには動画がベストかも.
難しいパーツがある場合は ErgoDox Assembly Part 7: Soldering the MCP23018 I/0 Expander - YouTube この人のシリーズがいいと思う.個々のパーツをはんだ付けする手順を丁寧に解説している.
ダイオード * 76
最初のステップがダイオード付けなんだけど,最初の最初で「ダイオードの陰極・陽極」と「基板の表・裏」をちゃんと理解してスタートするところがちょっとハードル高い(気がする).
そもそもダイオードとは,という説明を Wikipedia のダイオード項目 から引用する.
アノード(陽極)およびカソード(陰極)の二つの端子を持ち(この用語は真空管から来ている)、電流を一方向にしか流さない。すなわち、アノードからカソードへは電流を流すが、カソードからアノードへはほとんど流さない。このような作用を整流作用という。
つい先日会社で低レイヤー勉強会があり,ちょうど量子力学のバンド理論と半導体,PN 接合ダイオードあたりを勉強した所なのでタイミングがよかった.
さて,まず実際のはんだ付け作業における「ダイオードの陰極・陽極」について.ダイオードを付ける穴は各キーの下部にあるんだけど,よーーーく見ると,はんだ付けの穴の周りの金属が「丸」と「四角」で微妙に差が付けられている.ダイオードのカソード(Cathode/Kathode: 陰極)側には黒線などで印が付けられているものらしい.
ところで,ダイオードには "through hole (足を基板にぶっ刺すやつ)" vs "surface mount (表面実装)" という区別がある.ダイオードにはというか,割とよくある分類っぽい.僕は深く理解せず through hole を選択したから足をぶっ刺して裏からはんだ付けする形で作業を進めたけど,surface mount の方が(ちまちまとして難しくなりそうだけど)すっきりまとまって良かったかも知れない *11.
次に「基板の表・裏」について.未来の話をすると,ダイオードを付け終わったら基板をひっくり返すことになる.別の表現をすると,ダイオードより後のはんだ付けは,すべて「ダイオードとは逆側に付けていく」.だから右手用に使う基板であれば "LEFT HAND" と印刷されている側にダイオードを 38 個つけて,左手用に使う基板として "RIGHT HAND" とある面にダイオードを 38 個付けるのである.
気合.ちなみに全キーにダイオードが必要な理由は Keyboard Matrix Help の 8. Getting Rid Of Ghosting and Masking
に書いてる.
先に「ダイオードを付け終わったら基板をひっくり返すことになる」と書いたとおり,このタイミングで基板を裏返す.次の電子部品「抵抗」以降のはんだ付けは,すべてダイオードとは反対側にやっていく.
抵抗 (2.2k Ω * 2 / 220 Ω * 3)
ダイオード地獄を終えたら抵抗.こいつは楽チン.はんだ付けぐあいもちょうど良い.ダイオードづくしの中ですっごく爽やかな存在だ.
基板上,左側にはんだ付けされたやつが 赤・赤・赤 すなわち 2.2k Ω 抵抗で,中央と右側にはんだ付けされているのが 赤・赤・茶 こと 220 Ω 抵抗.
ここで一点,気をつけるべきことがある.先の部品リストで Short jumpers
* 五本というものがあったのだが,You can also use the clipped off legs from your resistors
とあるように,はんだ付けを終えた抵抗たちの足をパチンと切ったら,それを捨てずに取っておく.
そんなもん何に使うのかというと…
見えるかな.切り取った抵抗の足を使って,左右基板 3.5 mm TRRS ソケット取付箇所のまわりにある白い丸で囲われたニ穴を連結させてやる.色んなことするんだな.
3.5 mm TRRS ソケット
じゃあ次は二穴連結によってお膳立ての終わった 3.5 mm TRRS ソケットをはんだ付け.
そろそろはんだ付けにも慣れてきた.
Teensy 2.0
Teensy 2.0 は Ergodox の頭脳であり,Ergodox を "programmable" にしてくれている立役者だ*12.Teensy 2.0 は ATmega32U4 プロセッサ が乗っている 8bit マイコンボードで,キー入力をコンピュータに送るシグナルに変換する.ATmega32U4 は Arduino シリーズの一部にも使われているものらしい.
Teensy のバージョン 2.0 は少々時代遅れだったりする?のか,入手経路が乏しくてちょっと困った.スイッチサイエンスは v3.x+ 系からしか売ってないっぽい.千石電商にそれらしきパーツも見つけたが,確か Teensy 2.0++ だった.結局 Teensy のために Aliexpress アカウント作ったよ...
これをこうして
こう(はんだ付け)じゃ
ところで Ergodox はメカニカルキーボードの一種である.メカニカルキーボードのメカニカルたる所以は,各キーが物理的なスイッチとして機能するところ,と理解している.知らんけど.すなわち,メカニカルキーボードのキーが押されて通電して,それが Teensy への入力 (I/O) として現れる.
でも,Ergodox のキーは全部で 76 個,片手でも 38 個.Teensy はそんなに沢山のキー I/O を受け取れるものだろうか?Teensy and Teensy++ Schematic Diagrams から,Teensy 2.0 のスキーマを見てみよう.
右側の F0, F1... B0 と並んでいるところが I/O ピンのはずで,せいぜい 20 個かそこらといったところ.1 I/O を 1 キーに割り当てるためには全然足りない.
ここで Ergodox はどうしているかというと「組み合わせ」を使っている.Ergodox はキーの配置が独特だけど,配線としては片手あたり 6 x 7 のマトリックスで定義されている.6 x 7 の組み合わせでキーを特定するから,受け取る I/O としては片手 6 + 7 = 13 個の I/O を割り当てれば事足りる.先述の回路図で "ROW", "COLUMN" 系のラベルを繋いでやれば,以下のような図が現れる*13.
これで 6 x 7 (以下) のスイッチ ON/OFF を 13 個の I/O で賄うことが出来た.Teensy は受け取る I/O からプログラムにもとづいて,どのキーが押されているのかという情報を逆算することが出来る.
この"プログラム"が qmk_firmware であって,先に僕の GitHub レポジトリを例としてあげたように,勝手にカスタマイズしてビルドすることが可能.ビルドされたファイルは Teensy Loader というアプリケーションを使って Ergodox に送り込むとラク.その後 Teensy 2.0 上の物理的なボタン (リセットボタン) を押すと,送り込んだプログラムが有効になる.
右手はこれでいいが左手はどうするの,という疑問が浮かぶかも知れないがそれは後で.
USB mini B の工作
これが意外な難所.本件のゴールは単純で,はんだ付けの方向の都合上 Teensy に付いてる USB mini B の口がキーボードに対して「ヨコ」を向いちゃうので,ケーブルを継ぎ足して「タテ」というか上っかわに口を開けたい,というだけのもの(と理解している).
ところが単純に mini B ケーブルを突っ込むと,上の図からわかるように,もう根本の"持ち手"だけでサイズオーバーしてしまう.だから...
あとは...
最後の USB mini B の口は穴にぶっ刺してはんだ付けする必要があるので「基板実装」型じゃなくて「through hole」型を選ぶこと.以上.電子工作に慣れていないこともあってか,このフェイズが結構面倒だった*14.
MCP23018-E/SP I/O Expander
さて次は I/O Expander だが... 僕はこれの存在意義が今ひとつピンとこなくて,ずっともやもやしていた.でも今回組み立てを機に調べてみて,なんとなく自分なりに腑に落ちたので,ちょっと整理してみようと思う.
右手キーボードには Teensy 2.0 がいて,キー入力をシグナルに変換してくれていた.右手側のキー入力をわずか 13 個のピンに繋ぐだけで捌き切ることができた.ありがたい.でも左手はどうするの?という疑問が宙ぶらりんになっていた.左手のキー入力は誰が面倒を見てくれるのか?左手にももう一個 Teensy 2.0 を配置する?それとも,全キーからの出力を何本ものケーブルで右手側に繋ぐ?...どれもうまくない.そこで,ヒーローの如く颯爽と登場するのが MCP23018-E/SP I/O Expander である.
僕は Teensy 2.0 を右手側の基板にくっつけた.一方で MCP23018-E/SP I/O Expander は左手側にある.これは偶然ではない.
MCP23018-E/SP I/O Expander のデータシート から仕組みを読み解いてみよう.まず MCP23018-E/SP の E
は temperature range であり,E = -40°C to +125°C
を表す./SP
は Package を示しており,SP = Skinny Plastic DIP (300 mil Body), 28-Lead
だそうだ.SPDIP package とも記載される.アーキテクチャとしては MCP23018
の箇所だけ気にすればいい.以下の通り.
大切なのは,Open-drain と記載されている右側の「GPA0-GPA7, GPB0-GPB7 の計 16 本」と,I2C と書かれているボックスから出ている「SCL (Serial Clock Line), SDA (Serial Data Line)の 2 本」.MCP23018-E/SP I/O Expander を使うと,最大 16 の GPIO (General Purpose I/O) を,わずか 2 本の SCL, SDA ラインで読み書きすることが出来るようになる.16 I/O あれば Ergodox 片手分である 6 + 7 = 13 本の I/O をカバーできる.これで左手 38 キーを賄う I/O 13 本をわずか 2 本に集約して,I2C と呼ばれるシンプルなプロトコルで TRRS を介して「右手側 (Teensy が存在する側)」に送ることが出来る.だから,PC から Teensy の存在する右手側のみに USB ケーブルを繋げば,両手分のキー入力がゲットできるという戦略なのでした.
そして各ピンの名前.
というわけで,MCP23018-E/SP I/O Expander をはんだ付けしていく.
ところで,MCP23018-E/SP I/O Expander では,上の画像の黄色で囲った 3 穴だけ穴の周辺にはんだ付けする金属部分がない. これどうするの? と迷ったけど ErgoDox Assembly Part 7: Soldering the MCP23018 I/0 Expander - YouTube を見ると飛ばしていいらしい.INTA, INTB, NC を利用していないっぽい.
次に,MCP23018-E/SP I/O Expander の謎の場所にセラミックコンデンサ(キャパシタ)を追加する.
参考に General Purpose I/O: How To Get More | Hackaday らへんを読んでいると,カウンターチップ CD4017 が言及されている.これは,先の "はんだ付け練習台" として作ったおもちゃに使われていた電子部品だ."CD" は CMOS Decade counter IC の冒頭から来ている.閑話休題.コンデンサ(キャパシタ)は電荷を蓄え安定させる電子部品(ref: コンデンサとは? | 村田製作所 技術記事).何故 I/O Expander のこの足にくっつけるの? 先の足の配置と対応させると何か見えるかなと思ったが,これもともと裏側で Teensy のはんだ付けに使われてた穴だし何の意味あるのかよくわからない.電圧を安定させる効果があるらしい,というところまでしか理解できていない...
ケースをめぐる困難・分解編
さて.基板にパーツをはんだ付けして作っていくのはいいが,最終的に剥き出しのまま使うわけにも行かない*15.キーボードケースが必要になる.実は Ergodox GitHub レポジトリにはキーボードケースの設計図も入ってるから,これをどうも 3D プリンタで出力すればケースが出来上がるっぽい.
僕の場合は幸いというか何というか三年半使ってきた旧 Ergodox EZ のケースがあるので,これを流用すりゃ楽勝じゃん,と.
これが地獄の始まりだった.
ネジを外して分解していっても,一向に古い基板がケースから外れてくれない.よくよく構造を観察すると,下から「基板 - ケース - キースイッチ」の順になっていて,キースイッチでケースの枠を挟み込んではんだ付けされている.つまり,全部の取り付け済キーのはんだを一個一個溶かし(desoldering)て基板から取り外し切らないとケースがフリーにならない.まじかよ.
というわけで...
最初は goot はんだ吸取り線 CP-30Y を使っていたんだが,吸い取り器の方が効率良かった.はんだごてで溶かしたところをすかさず吸い取る.単にバネじかけで空気を吸っているだけでけっこうギミックがチャチだから,数をこなすと割とすぐヘタる.
無駄に時間をかけて全キーを desoldering,ケースを入手.
一番簡単と思っていたケースの分解が一番大変だったという.まるで人生のようだ.
ケースをめぐる困難・合体編
ケースが分解できたらあとは被せるだけ... と思いきや,いくつか問題に遭遇.どうやら Ergodox EZ はオープンソースの PCB ボードをそのまま使っているのではなく,たぶん組み立ての効率のためだと思うが,いくつかカスタマイズしているようだった.
たとえば↑のような違いがある.ケースを被せると出っ張りがぶつかったりするので
これを削ったり,
ケースの穴が Teensy RESET 物理ボタンの場所からずれていて,
はんだごてで開通 (& カッターで削る) したり.
色々苦戦しましたが,なんとか収まる.
キースイッチ
ようやくクライマックス.僕は Cherry MX 赤軸 が好み.
こいつを下から「基板 - ケース - キースイッチ」の順になるようにケースを挟み込んでぶっ刺して,はんだ付け.76 キー全部!
ゴールが近いのと,キーをひとつひとつ付けているという実感から楽しさがマックス.
いえーい
ジグソーパズルのキーキャップ
キースイッチがついたので,この時点で USB ケーブルを接続して,ちゃんとキー操作が検知されることを確認.おk.
いよいよ最後はキーキャップを被せていくだけ.無刻印を選んだので,どのキーがどのキャップかわからなくてほぼジグソーパズルでしたね.
完成!
いまのところ問題なく動いてます.なんか嬉しい.
いじょ
途中でけっこう語るべきことは語ってしまったのでシメに書くべきことは特段... まぁ,ぼくも社会に出て働き始め 2020 年でまる 10 年になります.早々に世界からフェイドアウトするかと思いきやしぶとく楽しく生きています.やっていきましょう.
*1:自作キーボード設計入門 は僕も買わせていただきました.KiCad の使い方なんかは特に参考になった
*2:先述の公式サイト 2.2 ASSEMBLY GUIDE が一応公式なものになるだろうか
*3:まぁ別に C の知識は必要ないけど
*4:でも最低発注枚数が 5 枚だったので,左手と右手に使ったあと 3 枚余ってるんだよな
*5:秋月電子に MCP23017 はあったが MCP23018-E/SP がない
*6:標準 Ergodox レイアウトだと 76 個だと思うんだけど,公式ページでは 76-80, depending on your layout との記載.80 個使うレイアウトもあるの?
*7:Not strictly necessary but suggested
*8:FC68129 will also work if its extra pins are snipped off
*9:Ergodox EZ は台湾からの発送となり,送料無料.ありがとう台湾
*10:大学院時代に生体分子の質量計測器を自作する研究室にいたのでちょっとだけやった記憶があるが,もうその前といえば中学校とかに遡ってしまう
*11:Ergodox EZ を分解してみたところ surface mount タイプのダイオードでした
*12:たぶん
*13:図は https://kandepet.com/dissecting-the-ergodox-the-ergonomic-programmable-keyboard/ より拝借
*14:ニッパー捌きに自信がないから予備のために無駄に mini B ケーブル買っちゃって余ってる
インド出張から得た英会話の学び
三週間ほど海外に出張していた.インドである.趣旨としては,僕が持ついくつかの領域における専門知識と経験をあちらのエンジニアに展開する,先生役として呼ばれた形..になるだろうか.単にセミナーとかを聞くだけの出張なら今夏にも行ったところだが,三週間みっちり先生をやるとなるとプレッシャーが違うし,事前にインドのチームと喋ったところ僕に対する期待値が異様に高く,胃の痛い思いをしながら突入することになった.
午前は講義を行い,午後は行列のできる Hash 相談所,夜はホテルに戻って翌日のトレーニング資料を作る... というすたーたっぷ勤務時代を彷彿とさせる社畜業に勤しみ観光どころではなかった (たまたま向こうで働いていた友達に会ったのと,インドチームとビール飲みに繰り出したりはした) ので,費やした時間分は役に立ったことを願う.黒い髭をたくわえた厳ついエンジニアも鋭い眼光の優秀な若者もみんな良い人で,英語の多少の不備は保管してくれるし理解しようとしてくれる.僕としてはけっこう気に入ったのでまた呼ばれたら行きたいと思う.
で,英語読み書きはそれなりに馴染んではいたけど,講義から質疑応答から (日本語でも苦手な) 酒の席の雑談から,全部英語で聞いて喋ってを何週間も続けた経験は人生で初と言ってよく,それなりに学ぶところがあったので箇条書きで投げることにする.思い出したらこっそり追記する.
it depends 超便利
- 何か聞かれたときに (特に技術的な質問とか) その場で Yes/No やら確定した答えを返せないことあると思うんだけど,そういうときにとりあえず「場合によるな」と前置きして,それぞれの "場合" を説明して対話を続ける
- その場で説明出来なくても "it depends" だけ返せば "depends on what?" みたいに返してきて会話が前に進む
I mean... / what I said is... で説明を補足する
- こちらの喋った内容がどうも相手にピンと来ていないようであり,かつすぐに質問が返ってこないときは,同じ内容を別の言い方で伝え直す
- この項目に限らずだけど日本語でもスムーズなコミュニケーションのためにやることだなこれ
well... / let me xxx... で場を繋ぐ
- 講義中に質問を受けたり,説明のために準備が必要なときに口ずさむ (?) フレーズとして使っていた
- トラブル調査するときにログファイル開きながら let me check the log file... とか
- これはどちらかというと相手に何か伝えるというより,自分のための面が大きい.少なくとも僕は,脳内で常に英語での思考を続けてリズムを保たないとダメになるのでやっていた
進捗どうですか ... How's xxx going? / あるいは単に How is it going?
- It's going well, 終わった話だったら It went well, 問題があれば we have some problems
- xxxer than I expected
I'd like to を多様
- ネイティブ的に自然かどうかは知らないけど,よく使ってた.あとなんか聞かれたら I prefer
I feel (felt) でほんのり意見
Let's say, で例示
- 一般的な説明をしたあと「例えば」で具体的な話をする流れはよくあると思う.そこで仮定を置くときに lets' say... が便利だった
- for example, でもいいんだけど,気分的には for example でビシッと個別例を出すというよりはもう少し軽く,仮に xxx だとしよう.そんなときは yyy になるんだよ.という短いフレーズの例示に使いやすいのかなという印象を受けた
学校英語で習ってそのまま忘れてた isn't it? みたいなお尻に付ける質問文, 実戦では便利
- 疑問文にし忘れて喋り始めてしまったときに最後に付ければ体裁が整う
- 文法用語的には tag question というらしい
seriously? はほぼ「マジで?」みたいに使える
- seriously↓, seriously↑!? などイントネーションでニュアンスが変わるところも「マジで」っぽい
- really? も同様に使える模様
聞き取れなかったら sorry? で聞き返す
- ほんと実感したけどわからないとき聞き返すの超大事.わからないのは普通.
- お互いに一発でコミュニケーションが通らないことを想定して喋るようにする
- pardon? は失礼という話をどこかで聞いたし実際向こうでも使ってる人は見なかったので, sorry (あるいは I'm sorry?) 一本で聞き返し続けた
- 速度が速すぎたら sorry, could you speak slowly? とお願いする
- 特定の単語だけ聞き取れなかった (たとえば it was caused by xxx, で xxx が聞き取れなかったとする) のなら,sorry, you said... it was caused by... what? とわからないところを what で置き換えて疑問文ぽいイントネーションで聞き返せば伝わった *1
- ほんと実感したけどわからないとき聞き返すの超大事.わからないのは普通.
言葉としては聞き取れたけど意味がピンとこなかった時は
- what do you mean? とか,what do you mean is ... とこちらの言葉で言い換えてみる
- I couldn't get what you said, のあとにそれってつまりこういうこと? と続けるのでもよい
- get を「理解する」という文脈で使えるようになってからテクニカルな内容を話すのがすごい楽になった
- あとは for example? でもう少し細かい話を聞き出す
ok, ok... で相槌を打つのらくちん
- 「それな」でビシっと同意したいときは "exactly"
- 何か許可とか同意を求められたときは "sure"
- ところでインド人のジェスチャーで最初混乱するのが 頭をゆらゆらとする,一見 No に見える Yes で,会話の中でもよく頭をゆらゆらしてる.僕も影響されて最後の方はゆらゆらしてた気がする
"cool"
- くぅ, みたいな発音で相槌に使っていた.ok, よりも会話の流れを終わらせる傾向が強い
- 女の子はあまり使わないみたいだった
講義の中で does it make sense? so far any question? とかで質問を引き出してみる
- これは先生役で行ったのでよく使ったフレーズ
- とはいえ基本的にみんな積極的に講義中に割り込んで質問してくるので,日本人相手みたいに質問ターイムを設ける必要はあんまなかった
インドはヒンディー語が公用語として定められているものの,実際は地方ごとにまったく別の言語を喋っていて,オフィスに居るエンジニアたちもそれぞれの home town language が異なるそうだ.そこでコミュニケーションを取るためには bridge language としての英語を使うしかない.そういう意味で彼らにとっても英語は「第二外国語」と言ってよく,ぼくのような外国人が英語に不慣れなところを見せても非常に寛容だったし,お互いのことを理解しようとする姿勢はとても優しかった.インド英語は発音が独特でかつめっちゃ速い,とよく言われるし実際にインド人の間で本気で交わされる英語の会話には付いていくのが難しいが,特に educated な人は基本的に理解されやすい英語を喋るすべを知っているようで*2,僕が「ちょっとわからん」という反応をすると,次からはとても聞き取りやすく喋ってくれる.
一方で Uber のドライバーとかローカルのお店の店員はそもそも英語なのかヒンディー語なのかまた別の言語なのかすらわからず,あるドライバーはマイクつけてて電話の相手と喋りながらおもむろに僕に話しかけて来るので辛かった*3.
ところで「日本っていくつ言語があるん?」という質問に最初は戸惑ったが,彼らは同じ国の中でも違う言葉を喋るのが当たり前なのだと気が付く.「みんな日本語使ってるよ.方言 (dialect) でイントネーションとかアクセントは違うけど,書き言葉はどこでも伝わる」と答えると「one country one language, それは最高やね」みたいに感心していた.
雑多な話題
あと日本に関する話題で面白かったのは
- イギリスには Queen がいるけど,日本にも King がいるよね,そんで今度 Akihito 辞めるんでしょ (よく知ってる)
- 日本の学校ではサムライ・クラスルームがあるのか? ニンジャ・クラスルームは?
- 剣道なら授業あるけど..
- 下ネタは世界共通.天丼も笑いの文脈として共通
- 宗教の話をわりとカジュアルに振られるのがちょっと困った
- 日本では過労死がひどいらしいが,働きすぎた人が道端で死んでるのか
- useful Japanese として "do-mo" を教え,日本では respect を示すため名字に "-san" を付けるのだと伝えた結果 "do-mo, Hashimoto-san" と呼びかけられる事案が発生
出張以前の英会話のお勉強
基本的にお席に座って地道にやる "お勉強" が大好きな人間なので,この特性を利用して,現場に飛び込む以前になるべくパフォーマンスを高めようと試みた.ひとつは専門知識のぶらっしゅあっぷとトレーニング計画 *4 だが,英語力という面でも何かやっておこう,と考えた.
- きちんとしたビジネス英会話力を付けるべく Berlitz 英会話 で短期集中受講
- 英会話教室もいくつか比較した結果,Berlitz に決めた.マンツーマンでじっくり喋ることが出来るのと,"Berlitz Method" と呼ばれる,雰囲気で会話を進めずに文法的に正しい文を作るまでしつこく繰り返させられるというスパルタ方式が気に入った
- ビジネス英語は必要なくて雰囲気で会話できれば良いのなら,後述の DMM 英会話がちょうどよいと思う
- Berlitz は老舗だけあり歴史的経緯を拭い去れていないシステムがちょっと非効率だけど,先生やカウンセラがとても協力的で僕の事情やニーズに合わせてスケジュールをカスタマイズしてくれ,心地よくレッスンを受けることが出来た
- Gaba もマンツーマンでコスト的にはほぼ Berlitz と並んでおり,システム的にはむしろ洗礼されていたのだが,営業の押し付けが肌に合わなかった *5 のと,個室ではなくて適当に区切られたブースでレッスンするので周りが気になってイマイチ
- シェーン英会話 の雰囲気とコスパはよかったけど,子供塾みたいな感じで,人生に余裕のあるご家庭が教養としてやるっぽさあった.あと短期集中カスタマイズとか無理そうだった
- 英会話教室もいくつか比較した結果,Berlitz に決めた.マンツーマンでじっくり喋ることが出来るのと,"Berlitz Method" と呼ばれる,雰囲気で会話を進めずに文法的に正しい文を作るまでしつこく繰り返させられるというスパルタ方式が気に入った
人生廃課金厨なので月収を墓場に直送, 英会話短期集中コースを召喚するなどのプレイを行う
— 俺のミームを受けてみろ (@T_Hash) October 31, 2017
英会話のフィードバック, 自己認識よりも良い評価だったけど「雑談のための話題を用意する」って英語関係なくない?話題性のない人生よ… pic.twitter.com/1AjqOgxi0n
— 俺のミームを受けてみろ (@T_Hash) November 25, 2017
- 『どんどん話すための瞬間英作文トレーニング』で中学生レベルの英語が瞬間的に出るように鍛える
- 様々な英語学習法を比べてみて,現在自分に最も適当と思われたので購入
- 日本語の単文から,英文を瞬間的に作る反復トレーニング.読み書きはそれなりに複雑な事ができるのに喋りに自身がない.. という人に良さそうだったので
どんどん話すための瞬間英作文トレーニング (CD BOOK)
- 作者: 森沢洋介
- 出版社/メーカー: ベレ出版
- 発売日: 2006/10/25
- メディア: 単行本
- 購入: 80人 クリック: 383回
- この商品を含むブログ (174件) を見る
インドで腹を壊さないためのたった N ツの冴えた方法
先にインドを経験した先人がお腹を壊しているのを見て (僕自身も 6 年前にインドを訪れた際はやられたので),彼らの意見を取り入れた結果,最後まで体調を崩さず元気に過ごすことができた.ポイントは以下:
- 整腸剤 (ヤクルト BL 整腸剤, 下記リンクあり) を毎食後飲む
- 水はペットボトルの水だけを摂取すること
- ジュースやカクテルに入ってる氷に気をつける
- 歯磨きもペットボトルの水でやる…という話も聞いたが僕はそこまではやらなかった.普通に水道水で歯を磨いたあと,気持ちペットボトル水でうがいするくらい
- 屋台の食事は食わない
- 6 年前は喜んで屋台のチャパティとかラッシーとか食いまくってた
- 単なる栄養摂取で事足りる食事は COMP で済ます
- 同僚とのランチやディナーは普通に食うが,一人の食事では胃をいたわる,という方針
- インド,基本的にスパイシーな食事しかないので,三食現地食を食ってると日本人の胃は回復時間が足りないんだと思う
- 出版社/メーカー: ヤクルト本社
- メディア: ヘルスケア&ケア用品
- 購入: 4人 クリック: 64回
- この商品を含むブログを見る
COMP飲も? https://t.co/7j4zipbQwI pic.twitter.com/m3XSUeoCza
— COMP.JP (@comp_official) March 11, 2017
いじょ
最後に,そのままでは味気ない食事になりがちな COMP を飲む際のコツですが,上記 COMP ちゃんに飲ませてもらってると思うとバブみがあり満足感が上がります.
なぜ何もないのではなく何かがあるのか
書きたいことがない.書きたいことはあるが,それをテキスト化のフローに載せていくことが出来ていない.というわけでこれは無理矢理に文章を出力する試みである.軽く 2000 字を目処に.
一つの思想の真の生命は,思想がまさに言葉になろうとする地点に達するまで持続するにすぎない.その地点で思想は石と化し,その後は生命を失う.だが化石化した太古の動植物のようにその思想は荒廃を免れる.我々は思想のつかのまの生命を,まさに結晶せんとする瞬間の結晶体の生命に比することができる.
仕事ではめっきりコードを書く機会が減り,自然言語を書いている.技術職ではあるが,それを道具としながら日本語と英語を読み書きして人間と社会をやっている.英語はもっと喋れるようになりたいな.論理と自然言語を装備して正論と事実で殴ることのできる今の仕事は案外性に合ってるとは思うが,コードを書いてものを作り続けている人はほんとうに尊敬に値する.
仕事が楽しいのでわりと社畜っぽい働き方をしているが,IT 業界の闇みたいな辛い話がない職場で待遇的にもかなり恵まれているとは思う.現世に楽園は存在しないんで嫌なこともあるが総じてな.成果測定の難しいことをやっているので,給料分はチームの役に立っていることを願う.
学生時代から得意とする絨毯爆撃的な学習を日常業務に活用する方法が自分の中でようやく確立できてきた気がする.この方法を人間的やっていきの瞬発的飛び道具と組み合わせ,いくつかそりっどな成果を得ることが出来た.1000 ページあるドキュメントを片っ端から読んでいったりする人間は少なく,知識獲得そのものにモチベーションを感じて継続できるというのは社会全体から見るとレアな方の気質であるらしい (エンジニア職はそういう気質わりと多いような気もするけど).ともあれ,そういう偏執的な姿勢は使いようによっては社会でも役に立つらしい.院を出て就職したばかりの僕に聞かせてやりたい希望のある話である.しかし油断はできない.この世に慈悲はなく僕自体に価値はなく油断をした瞬間に死ぬことは確定的に明らかである.
朝と夜に100回ずつ「この世に慈悲はない」「俺に価値はない」「みんな殺す」と唱えることで人生がよくなる
— 俺のミームを受けてみろ (@T_Hash) 19 December 2014
もちろん偏執的なだけでは社会をやっていくことが出来ないため,人間とのコミュニケーションにおいては基本的な論理を使って丁寧に事実を整理してみる.整理した論理を言葉に落とし込んで理解のポイントをすり合わせようと試みる.そもそも人間の頭脳は基礎的な論理 (とくに因果関係) プラットフォームに乗りやすいようには思う.
つい先日 「異世界からきた」論文を巡って: 望月新一による「ABC予想」の証明と、数学界の戦い « WIRED.jp なんて記事を読んだりもして,"人間の頭脳の程度がだいたい全部同じくらい" とはとても主張できないのだけど.僕もできることなら天才になりたかった.元々ものごとの理解力が格別優れているわけでもなく,むしろとんでもなく基礎的で誰も見向きしないような場所の石ころに躓いていつまでもそこで途方にくれているような頭脳でしかない.
閑話休題.
『生命・エネルギー・進化』
突然ですが最近読んでよかった本の紹介です.
ニック・レーンによる『生命・エネルギー・進化』という本で,原題は “THE VITAL QUESTION – Why Is Life the Way It Is?".著者がかつて『ミトコンドリアが進化を決めた』で展開した論をさらに先へ進めたもの,っぽく,より汎用性の高い語り口として "進化には実は,生命のきわめて根本的な特性の一部を第一原理から予測できるようにする強い制約 – エネルギーの制約 – があるということ” を主張する.
とくにこの制約が顕著に感じられるのは我々真核生物という “異端” においてである.細菌も古細菌も,その化学・遺伝的特性の吹っ飛び方からは考えられないほど,見た目にはほとんど同じである. “まるでそこに内在する物理的制約が原核生物を複雑な形態に進化できなくしているかのようで,その制約はなぜか真核生物の進化では解かれたのである” (p.57) … こうした謎を提起しながら論をすすめる本書はある種推理小説的というか,謎解きの楽しさを与えてくれる.まぁ謎解きというか著者に導かれて"謎の解かれていき" を見ることになるわけだが,そこらへんの事情は推理小説でもさほど変わるまい.
『生命、エネルギー、進化』読みはじめたけどこれは久々に熱い本だな… 生きていることとは究極的にはプロトン反応である的な視点から, 生命の起源やら古細菌と細菌の分岐やらを語るっぽい pic.twitter.com/e2TOFms5dS
— 俺のミームを受けてみろ (@T_Hash) 24 December 2016
先月忙しくて止まってた『生命、エネルギー、進化』ようやく読了. 真核生物の特異さや「複雑な」生物機能の進化的意義を生化学・エネルギー観点から説明しようとしていてめっちゃ面白い. 昔生化学から入ってタンパク質構造変化 ΔG とかやってたので特に楽しめる
— 俺のミームを受けてみろ (@T_Hash) 4 February 2017
生命 (life) とは何か,ではなく,生きている (living) とは何なのか.生化学畑の学生であったこともあり実に楽しい読書体験であったと言える.
ひそむもの
仕事と生活にある程度の安定感が出てくると,この先どうするという思考が仄暗い水の底から浮かび上がってくる.いまググって知ったけど仄暗い水の底からってパチンコになってんのか頭おかしい.人生でやりたいことはいろいろあるが,僕は本当に人生の進捗が遅く遠回りばかりで,ぐるぐると何かの周りを歩き続ける以外にこれといった存在の使い方もしていないように思われる.
そうしてぐるぐると歩いているうちにある程度は身の程を知ってしまい,自身の無能と現実の現実たる現実に打ちひしがれているうちにぐるぐる回っているときは見えていたはずのものがいつのまにかひどくぼんやりとして来て,視界不良と幸福の定義に悩みながら新しい火薬を手札に加えるべく周回ルートを外れてあてどなく生きていく以外に何も
それはぼくの思いすごし 悪いことばかり思いつく いつもの日曜日の午後
– スガシカオ「日曜日の午後」
Spark on EMR で 1000 Genomes Project のデータを触る
なんやかんやで年始から記事書いてないですが, 外部要因がないとブログを書かないようになっている. "Distributed computing (Apache Hadoop, Spark, ...) Advent Calendar 2016" の 12/11 担当として何かを書きます.
1000 Genomes Project public data set on S3
AWS S3 には無造作にいろんなびっぐなでーたが置いてあり,
その中に 1000 Genomes Project のデータがあります: 1000 Genomes Project and AWS.
1000 Genomes Project については公式ページ 1000 Genomes | A Deep Catalog of Human Genetic Variation などを参照して頂ければと思いますが, 複数民族をソースとして匿名の 1000 人以上のゲノム配列を決定しよう, というプロジェクトで既に完了しています. なんで 1000 人も要るかって言うとヒトのみならず生物ってのは同じ種でも個体ごとに微妙にゲノム配列に差があってその差がいわゆる "体質" とか "病気のかかりやすさ" 的な差を生み出しているからで, ヒトの標準的なゲノムは何なのか, ということを知る意味でもある程度の N が必要だからです. ともあれ, そのデータは匿名化された上で公開されており, S3 上にも上がっているというわけです.
aws cli が使えるなら以下のコマンドですぐアクセスできます.
$ aws s3 ls s3://1000genomes PRE alignment_indices/ PRE changelog_details/ PRE complete_genomics_indices/ PRE data/ PRE hgsv_sv_discovery/ PRE phase1/ PRE phase3/ PRE pilot_data/ PRE release/ PRE sequence_indices/ PRE technical/ 2015-09-09 06:16:09 1663 20131219.populations.tsv 2015-09-09 06:17:01 97 20131219.superpopulations.tsv 2015-09-09 00:01:44 257098 CHANGELOG 2014-09-03 00:39:53 15977 README.alignment_data 2014-01-30 20:13:29 5289 README.analysis_history 2014-01-31 12:44:08 5967 README.complete_genomics_data 2014-08-29 09:22:38 563 README.crams 2013-08-07 01:11:58 935 README.ebi_aspera_info 2013-08-07 01:11:58 8408 README.ftp_structure 2014-09-03 06:19:43 2082 README.pilot_data 2014-09-03 21:33:15 1938 README.populations 2013-08-07 01:11:58 7857 README.sequence_data 2015-06-19 03:28:31 672 README_missing_files_20150612 2015-06-04 04:43:32 136 README_phase3_alignments_sequence_20150526 2015-06-19 01:34:45 273 README_phase3_data_move_20150612 2014-09-03 21:34:30 3579471 alignment.index 2014-09-03 21:32:59 54743580 analysis.sequence.index 2014-09-03 21:34:57 3549051 exome.alignment.index 2014-09-03 21:35:15 67069489 sequence.index
Adam
今回は s3://1000genomes/release/
配下の多型データを使いますが, これは Variant Call Format (VCF) という形式で保存されておりこのままでは Spark で扱うことが出来ません. そこでマエショリが必要になってくるのですが, Spark でゲノムデータ解析をする際は Adam というパッケージがデファクトスタンダードっぽくなっているので, 以下それを使って Parquet ファイル (相当) に変換します.
EMR クラスタ (Release label: emr-5.1.0 *1 ) を立て, Adam をビルドします. maven, sbt は適当に入れているものとします. EMR 5.1.0 は Spark 2.0.1 がバンドルされており Scala のバージョンは 2.11.8 なので, ./scripts/
配下の適切なスクリプトを流す.
$ git clone https://github.com/bigdatagenomics/adam.git ~/adam $ cd !$ $ ./scripts/move_to_scala_2.11.sh $ ./scripts/move_to_spark_2.sh $ MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m" mvn clean package -DskipTests
モノがビルドされると, spark-shell, spark-submit を拡張した adam-shell, adam-submit なるコマンドが使えるようになります.
[hadoop@ip-172-31-12-100 adam]$ ./bin/adam-shell Using SPARK_SHELL=/usr/bin/spark-shell Setting default log level to "WARN". To adjust logging level use sc.setLogLevel(newLevel). 16/12/11 13:57:32 WARN Client: Neither spark.yarn.jars nor spark.yarn.archive is set, falling back to uploading libraries under SPARK_HOME. 16/12/11 13:57:41 WARN SparkContext: Use an existing SparkContext, some configuration may not take effect. Spark context Web UI available at http://172.31.12.100:4040 Spark context available as 'sc' (master = yarn, app id = application_1479477268424_0036). Spark session available as 'spark'. Welcome to ____ __ / __/__ ___ _____/ /__ _\ \/ _ \/ _ `/ __/ '_/ /___/ .__/\_,_/_/ /_/\_\ version 2.0.1 /_/ Using Scala version 2.11.8 (OpenJDK 64-Bit Server VM, Java 1.8.0_111) Type in expressions to have them evaluated. Type :help for more information. scala>
VCF to "Adam Format" (Parquet に毛の生えたフォーマット)
adam-submit のサブコマンドに vcf2adam
というものがあり, これを噛ませることで VCF ファイルを ADAM Format に変換します. 変換元に先述の S3 1000genomes バケットを指定し, 出力先として自分の手持ちのバケットを指定する感じです. ゲノムデータは染色体単位で分かれて記録されているので, とりあえずトリソミーになるとダウン症を発症する 21 番染色体を選んで変換してみようと思いますが, まずは手元に落として, 少し VCF ファイルを覗いてみます.
$ aws s3 cp s3://1000genomes/release/20130502/ALL.chr21.phase3_shapeit2_mvncall_integrated_v5a.20130502.genotypes.vcf.gz /home/hadoop/ // 冒頭部分 $ zcat ~/ALL.chr21.phase3_shapeit2_mvncall_integrated_v5a.20130502.genotypes.vcf.gz | head -n 6 ##fileformat=VCFv4.1 ##FILTER=<ID=PASS,Description="All filters passed"> ##fileDate=20150218 ##reference=ftp://ftp.1000genomes.ebi.ac.uk//vol1/ftp/technical/reference/phase2_reference_assembly_sequence/hs37d5.fa.gz ##source=1000GenomesPhase3Pipeline ##contig=<ID=1,assembly=b37,length=249250621> // 行き来しながら探したところ 253 行目がヘッダーの模様. $ zcat ~/ALL.chr21.phase3_shapeit2_mvncall_integrated_v5a.20130502.genotypes.vcf.gz | head -n 253 | tail -n 1 #CHROM POS ID REF ALT QUAL FILTER INFO FORMAT HG00096 HG00097 HG00099 HG00100 HG00101 HG00102 HG00103 HG00105 HG00106 HG00107 HG00108 HG00109 HG00110 HG00111 HG00112 HG00113 HG00114 HG00115 HG00116 HG00117 HG00118 ... (省略 // データの中身. $ zcat ~/ALL.chr21.phase3_shapeit2_mvncall_integrated_v5a.20130502.genotypes.vcf.gz | head -n 1000 | tail -n 1 | head -c 200 21 9440889 rs574517239 A C 100 PASS AC=1;AF=0.000199681;AN=5008;NS=2504;DP=13457;EAS_AF=0;AMR_AF=0;AFR_AF=0.0008;EUR_AF=0;SAS_AF=0;AA=.|||;VT=SNP GT 0|0 0|0 0|0 0|0 0|0 0|0 0|0 0|0 0|0 0|0 0|0 0|0 0|0 ... (省略
では, adam-submit 叩きます. adam-submit コマンドは spark 用のオプションと adam オプションの間に --
を入れる必要があります. ちょっとハマりました.
$ ./bin/adam-submit \ --deploy-mode cluster \ --master yarn \ --num-executors 10 \ --executor-memory 20G \ -- \ vcf2adam \ s3://1000genomes/release/20130502/ALL.chr21.phase3_shapeit2_mvncall_integrated_v5a.20130502.genotypes.vcf.gz \ s3://hash-genome-work/chr21.v5/
行けましたね. コアノード 2 台でやっていましたが, この辺で処理が遅いことに気が付き金で殴るべく m3.2xlarge x 10 台に増設しています. そのスペックで 21 番染色体の VCF の Adam フォーマット変換処理に要する時間が 1.5hr 程度でした.
結果.
$ aws s3 ls --recursive s3://hash-genome-work/chr21.v5 2016-12-12 02:39:55 0 chr21.v5/_SUCCESS 2016-12-12 02:39:56 11895 chr21.v5/_common_metadata 2016-12-12 02:39:56 5548 chr21.v5/_header 2016-12-12 02:39:56 29687 chr21.v5/_metadata 2016-12-12 02:39:56 28835 chr21.v5/_samples.avro 2016-12-12 02:39:56 3466 chr21.v5/_seqdict.avro 2016-12-12 02:39:47 119661147 chr21.v5/part-r-00000.gz.parquet 2016-12-12 02:39:50 117920329 chr21.v5/part-r-00001.gz.parquet 2016-12-12 02:39:52 117938957 chr21.v5/part-r-00002.gz.parquet 2016-12-12 02:39:54 24165424 chr21.v5/part-r-00003.gz.parquet 2016-12-12 01:08:37 0 chr21.v5_$folder$
Parquet 化された多型データを adam-shell から参照
adam-shell で色々見てみたいのですが, AdamContext だと S3 からは直接ロードが出来ず HDFS に置く必要があるっぽいので先に HDFS に書き出します.
$ aws s3 cp --recursive s3://hash-genome-work/chr21.v5 /home/hadoop/chr21.parquet $ hadoop fs -mkdir /user/hadoop/ch21 $ hadoop fs -put /home/hadoop/chr21.parquet/*.parquet /user/hadoop/ch21/ $ hadoop fs -ls /user/hadoop/ Found 2 items drwxr-xr-x - hadoop hadoop 0 2016-12-11 19:18 /user/hadoop/.sparkStaging drwxr-xr-x - hadoop hadoop 0 2016-12-11 19:35 /user/hadoop/ch21 $ hadoop fs -ls /user/hadoop/ch21/ Found 4 items -rw-r--r-- 3 hadoop hadoop 119661147 2016-12-11 19:35 /user/hadoop/ch21/part-r-00000.gz.parquet -rw-r--r-- 3 hadoop hadoop 117920329 2016-12-11 19:35 /user/hadoop/ch21/part-r-00001.gz.parquet -rw-r--r-- 3 hadoop hadoop 117938957 2016-12-11 19:35 /user/hadoop/ch21/part-r-00002.gz.parquet -rw-r--r-- 3 hadoop hadoop 24165424 2016-12-11 19:35 /user/hadoop/ch21/part-r-00003.gz.parquet
さて, ようやく Adam Shell からデータを読めそうです.
$ ./bin/adam-shell scala> val data = spark.read.parquet("/user/hadoop/ch21/*.parquet") data: org.apache.spark.sql.DataFrame = [variant: struct<contigName: string, start: bigint ... 8 more fields>, contigName: string ... 20 more fields] scala> data.count res13: Long = 2785018912
約 27.8 億レコードと, 21 番染色体の variant だけでけっこうなデータ量. スキーマ見てみます.
scala> data.first res14: org.apache.spark.sql.Row = [[null,null,null,WrappedArray(rs559462325),G,A,true,true,WrappedArray(),false],21,9411238,9411239,[true,true,WrappedArray(),null,null,null,null,null,null,null,WrappedArray(),WrappedArray(),null,null,Map()],HG00096,null,null,WrappedArray(REF, REF),null,null,null,null,null,null,WrappedArray(),WrappedArray(),WrappedArray(),false,true,null,null] scala> data.printSchema root |-- variant: struct (nullable = true) | |-- contigName: string (nullable = true) | |-- start: long (nullable = true) | |-- end: long (nullable = true) | |-- names: array (nullable = true) | | |-- element: string (containsNull = true) | |-- referenceAllele: string (nullable = true) | |-- alternateAllele: string (nullable = true) | |-- filtersApplied: boolean (nullable = true) | |-- filtersPassed: boolean (nullable = true) | |-- filtersFailed: array (nullable = true) | | |-- element: string (containsNull = true) | |-- somatic: boolean (nullable = true) |-- contigName: string (nullable = true) |-- start: long (nullable = true) |-- end: long (nullable = true) |-- variantCallingAnnotations: struct (nullable = true) | |-- filtersApplied: boolean (nullable = true) | |-- filtersPassed: boolean (nullable = true) | |-- filtersFailed: array (nullable = true) | | |-- element: string (containsNull = true) | |-- downsampled: boolean (nullable = true) | |-- baseQRankSum: float (nullable = true) | |-- fisherStrandBiasPValue: float (nullable = true) | |-- rmsMapQ: float (nullable = true) | |-- mapq0Reads: integer (nullable = true) | |-- mqRankSum: float (nullable = true) | |-- readPositionRankSum: float (nullable = true) | |-- genotypePriors: array (nullable = true) | | |-- element: float (containsNull = true) | |-- genotypePosteriors: array (nullable = true) | | |-- element: float (containsNull = true) | |-- vqslod: float (nullable = true) | |-- culprit: string (nullable = true) | |-- attributes: map (nullable = true) | | |-- key: string | | |-- value: string (valueContainsNull = true) |-- sampleId: string (nullable = true) |-- sampleDescription: string (nullable = true) |-- processingDescription: string (nullable = true) |-- alleles: array (nullable = true) | |-- element: string (containsNull = true) |-- expectedAlleleDosage: float (nullable = true) |-- referenceReadDepth: integer (nullable = true) |-- alternateReadDepth: integer (nullable = true) |-- readDepth: integer (nullable = true) |-- minReadDepth: integer (nullable = true) |-- genotypeQuality: integer (nullable = true) |-- genotypeLikelihoods: array (nullable = true) | |-- element: float (containsNull = true) |-- nonReferenceLikelihoods: array (nullable = true) | |-- element: float (containsNull = true) |-- strandBiasComponents: array (nullable = true) | |-- element: integer (containsNull = true) |-- splitFromMultiAllelic: boolean (nullable = true) |-- phased: boolean (nullable = true) |-- phaseSetId: integer (nullable = true) |-- phaseQuality: integer (nullable = true)
わお. いくつか取ってみます.
scala> data.first.get(1) // contigName. このデータでは染色体番号が入ってるので 21 res15: Any = 21 scala> data.first.get(0) res17: Any = [null,null,null,WrappedArray(rs559462325),G,A,true,true,WrappedArray(),false] // variant の情報. referenceAllele = G で alternateAllele = A なので, // リファレンス配列では G のところが A になってる変異のレコード, ということになる scala> data.first.get(5) res21: Any = HG00096 // sampleId.
sampleId HG00096 について少し調べてみると, 年齢不詳イギリス人男性の B 細胞からサンプリングされたものであることがわかる.
なにか面白い解析が出来るとよかったのだけどぱっと思いつかなかったので一旦ここまでで公開. 何か思いついたら追記するかも.
とりあえず一発でアクセス可能な場所 (S3) に世界的な科学研究の成果が転がっていて, データを使ってあれこれ弄れる形まで持って行くのは案外簡単ですよ, というあたりが伝われば幸いです.
*1:最新版は EMR 5.2.0 ですが手元に立ってたのが 5.1.0 だったので...
カレーはハウスの箱裏レシピが至高
カレーを作った.
玉ねぎを炒めます
かぼちゃも切って一緒に
親の仇のように飴色になるまで炒めます.いい具合になったら野菜たちを取り出してざっと鍋を洗ったらおもむろに
肉を
強火で焼き
野菜たちを戻してアスパラとかと一緒に 1L の水で煮込みます.沸騰してから 15 分,
- 作者: ミシェル・フーコー,Michel Foucault,渡辺一民,佐々木明
- 出版社/メーカー: 新潮社
- 発売日: 1974/06/07
- メディア: 単行本
- 購入: 5人 クリック: 55回
- この商品を含むブログ (158件) を見る
フーコー『言葉と物』を読みながら灰汁を掬い,待ちます.
具材がちょっととろっとして来たらルーを投入.個人的なベストはハウスの「こくまろ 中辛」と「ジャワカレー 辛口」の組み合わせです.
- 出版社/メーカー: ハウス食品
- メディア: 食品&飲料
- この商品を含むブログを見る
- 出版社/メーカー: ハウス食品
- メディア: その他
- この商品を含むブログを見る
弱火で10分コトコトしたら,炊きたてご飯にかけます
以上,ブログらしいブログを書いてみたかった.オチはない.
取り寄せた本が届いたら DynamoDB Stream を元に Lambda 経由で Twitter に通知させる
人間は図書館を活用することで住民税の元を取ることが出来る.取り寄せ依頼した本が届いたり借りてる本の返却期限が近付いたりすると Twitter bot が通知してくれるようにしている.が,単純に cron で回していたので人間側の対応が遅れると何度も同じことを言われてしまう.
今回「今年もやるよ!AWS Lambda縛り Advent Calendar 2015 - Qiita」の 12/05 分担当として,上記の仕組みをDynamoDB の更新 Stream をイベントソースとして Lambda で一回だけ通知してくれるように改良する話を書く.
前提
図書館の web をスクレイピングして DynamoDB に格納する... ところは Lambda Advent Calendar としては本題ではないので省略*1.
うちの地区はこんな感じのページ.とりあえず人間が
- 本の取り寄せ依頼(予約)をしたとき
- 取り寄せた本が届いた時
- 本を借りた時
- 本を返した時
といった行動をした際に,日付,書籍 ISBN,行動タイプが以下の様な形式で DynamoDB に格納される機能が実装済とお考えください.
{ "date": "2015-12-02", "hashed_title": "7f550dd47d40ea31eb84c092dbcdd2f66ff960db", "isbn": "9784000236218", "type": "borrow", "uuid": "b946c18b-0aa9-45fc-938e-be5b52d1fc33" }
なお書籍データ本体は別の場所に ISBN をハッシュキーとして格納している. ...で,該当テーブルに対して DynamoDB Stream を有効化する.DynamoDB Stream はリリース当初 Preview 版で利用申請が必要だったが今は誰でも使える.
DynamoDB ストリームは、Amazon DynamoDB テーブル内の項目に加えられた変更に関する情報の順序付けされた情報です。テーブルでストリームを有効にすると、DynamoDB はテーブル内のデータ項目に加えられた各変更に関する情報をキャプチャします。 ...
DynamoDB ストリーム は、ストリームレコードをほぼリアルタイムで書き込むため、これらのストリームを使用し、内容に基づいてアクションを実行するアプリケーションを構築できます。
引用元: DynamoDB ストリーム を使用したテーブルアクティビティのキャプチャ - Amazon DynamoDB
DynamoDB マネジメントコンソールからテーブルを選択してぽちぽちやると有効化される.
すると,こんな感じの Stream ARN が得られる.
arn:aws:dynamodb:ap-northeast-1:<MY_AWS_ID>:table/<MY_TABLE_NAME>/stream/<Stream を有効化した時刻>
ARN = Amazon Resource Names は AWS サービスにおいて何らかのリソース (EC2 インスタンス, IAM ユーザ,DynamoDB のテーブル etc) を URI よろしく一意に識別する ID のようなもので,AWS サービス間で何か連携しようとするとよく出てくる.
Lambda で DynamoDB Stream を受け取る
ようやく Lambda.先の操作で得られた Stream ARN をイベントソースとして受け取り発火させる設定を行う. ゼロから書き始めてもいいんだけど,マネジメントコンソールで "Create a Lambda Function" を選択,"dynamodb-process-stream" という blueprint を元にすると話が早い.
console.log('Loading function'); exports.handler = function(event, context) { //console.log('Received event:', JSON.stringify(event, null, 2)); event.Records.forEach(function(record) { console.log(record.eventID); console.log(record.eventName); console.log('DynamoDB Record: %j', record.dynamodb); }); context.succeed("Successfully processed " + event.Records.length + " records."); };
event.Records
の中に Stream の更新情報がどばっと入ってきている.たとえば以下の様な形.
{ "Records": [ { "eventID": "1", "eventVersion": "1.0", "dynamodb": { // 更新レコード情報 "Keys": { "uuid": { "S": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } }, "NewImage": { "date": "2015-12-03", "hashed_title": "7f550dd47d40ea31eb84c092dbcdd2f66ff960db", "isbn": "9784000236218", "type": "return", "uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }, "StreamViewType": "NEW_AND_OLD_IMAGES", "SequenceNumber": "111", "SizeBytes": 26 }, "awsRegion": "ap-northeast-1", "eventName": "INSERT", // レコードの新規作成 "eventSourceARN": "arn:aws:dynamodb:ap-northeast-1:<MY_AWS_ID>:table/<MY_TABLE_NAME>/stream/<Stream を有効化した時刻>", "eventSource": "aws:dynamodb" } ] }
先の Stream 有効化時に "New and old Images" を選択していると,更新前/更新後のレコードがフルフルで入ってくる.単に「なんか更新合ったよ」という事実を知りたいだけなら "Keys Only" でいいと思う. Stream から取得できる "Records > dynamodb > NewImage ..." あたりの内容を使えば Twitter 通知に必要な情報は取れそう.
Twitter API
今回やりたいことのためには Twitter API を Lambda から叩く必要があるが,その名も twittter という npm package で簡単に実現できる.
var client = new Twitter({ consumer_key: <consumer_key>, consumer_secret: <consumer_secret>, access_token_key: <access_token_key>, access_token_secret: <access_token_secret> }); var msg = "予約していた『" + event.Records[0].dynamodb.NewImage.title + "』が図書館で確保済です. (" + event.Records[0].dynamodb.NewImage.date + "迄)"; client.post('/statuses/update.json', {status: msg}, function(err, ...) { // ... });
こんなイメージのコードを書けば,Lambda から bot を喋らせることが出来る.
ところでちょっと余談気味だけど,consumer_key
とか access_token_secret
とかを zip に含めて Lambda にアップロード,ってのはちょっとせきゅりてぃ的な意味でやりたくない.そこで AWS Key Management Service (KMS) を使うようにした.KMS のコンセプトについてはクラメソさんの 10分でわかる!Key Management Serviceの仕組み #cmdevio | Developers.IO などを参照にするとわかりやすい気がする.
流れとしては,あらかじめ KMS の Master Key を作っておいた後,その key-id を指定して Twitter API の各種 API credentials 情報をカンマで接続したテキストを暗号化.
$ aws kms encrypt --profile my-profile --key-id 9xxxxxx-9999-aaaa-a996-ffffffffffff --plaintext "consumer_key:<my_consumer_key>,consumer_secret:<my_consumer_secret>,access_token:<my_access_token>,access_token_secret:<my_access_token_secret>" --query CiphertextBlob --output text | base64 -D > ./encrypted-credentials
この encrypted-credentials
を zip に含め,ソースコードと一緒に Lambda にあげてやる.
Lambda 上では毎回実行時に KMS へのリクエストを行い,credentials を複合する.Lambda Function に紐付けた Role からのみ復号可能となるように IAM 的に制限することができる,という仕組み.Lambda 上で動作する KMS decrypt 部分のコードは以下.
var encrypted = fs.readFileSync('./encrypted-credentials'); // コードと一緒に上げた暗号化済 credentials 情報 kms.decrypt({CiphertextBlob: encrypted}, function(err, data) { if (err) { console.log(err, err.stack); context.fail('failed'); } else { var decrypted = data['Plaintext'].toString(); // console.log(decrypted); obj = {}; decrypted.split(',').forEach(function(item) { var pair = item.split(':'); obj[pair[0]] = pair[1]; }); // Lambda 上で復号した credentials 情報を使って Twitter API を叩く var client = new Twitter({ consumer_key: obj.consumer_key, consumer_secret: obj.consumer_secret, access_token_key: obj.access_token_key, access_token_secret: obj.access_token_secret }); // ...
いじょ
もう少し DynamoDB Stream + Lambda の汎用的な話にするつもりだったんだけど,ざっくり用例を一つ紹介する記事になってしまった.がんばります.
バイオインフォ(略)技術者試験に不本意な成績で合格
したっぽい.
Japanese Society for Bioinformatics - JSBi :: バイオインフォマティクス技術者認定試験
昨年11月に試験で12月には合格わかってたんだけど, 成績がようやく今月に入って届く.
誕生日記載する必要なくね?
答え合わせして900(1000点満点)は超えてるだろと思ってたら, フタを開けると800点しか取れておらず愕然とする.
特に点数の低いバイオインフォマティクス分野(後半)は, タンパク質立体構造系の問題が多くてよっしゃと思ってたのに何があったのか... 悲しい. 解答の公開が待たれる.
平成26年度試験問題&解答
平成27年1月中の公開を予定しています。
はよはよ
試験について
試験は4パートに分かれており
- Bio
- Info
- BioInfo(1)
- BioInfo(2)
がそれぞれ250点ずつというイメージ. 過去問が公式Webサイトから落とせるのでそれを解いて, わからない問題については教科書などを調べるという勉強法で出社前とか昼休みとかに1.5ヶ月ほど.
バイオインフォな実務で使う知識がどれくらい含まれてるのかわからんのが正直なところだけど, 久しぶりに専門分野の記憶を引っ張りだして来れてわくわくしたのと, やはり自分は学習が大好きなことを再確認し, それなのに就労以降明らかに低下し続けている知性を顧み, さらに正月に弟と4,5年ぶりに顔を合わせたらヤツは理系修士からの技術職就職してて, 大学で学んだ知識ばりばり使ってとても楽しそうでいいなぁ, 人生とは... みたいな気分に. 話が逸れた. 人生よ.
使った教科書的なものをアフィリエイト
何はともあれこれ. 今まで買おう買おうと思いつつ値段から踏ん切りがつかなかったけど試験挑戦がきっかけとなり購入. 良い本です
ヴォートはまぁ, たまたま僕が学生時代に使ってた生化学の教科書がコレというだけなんだけど, 生体分子の働きを化学レベルから考察するレイヤーがとても心地よい. バイオ分野の学習に.
おなじみザ・セルこと細胞の分子生物学. わりとマクロなバイオ分野知識が抜けてたりしたのでこいつで補う. 実際僕が持ってるのは銀色表紙の第4版なんだけど, 昨年末に青表紙の第6板が出たので記念してそっちを貼っとく. 買いたいが金欠で辛い... Kindle版だと1万円台で買えるっぽくて揺れてる.
で, インフォ分野の問題は
- 実務で馴染んでて「はいはい」で回答できる問題
- ググれば十分な情報が得られる問題
が多くてそんな教科書の出番を感じず特に紹介するものなし. 数学(統計・確率)がらみの問題がやや手こずったけど, 前回の記事でも触れた↓の本が概念をつかむ助けになった.
いじょ
人生よ