レッツ&ゴー! 音声認識を使ってミニ四駆を「声」で操作!(MKZ4“魔改造” iOS編)

こんにちは。アプリケーションエンジニアの中村です。

MKZ4も発売してからしばらく経ちまして、買ってしばらく遊んだものの、ホコリを被っているお宅もあるのではないでしょうか。かくいう私もそうだったのですが、子供のころ見ていたミニ四駆のアニメで、声でミニ四駆を操っていたのを思い出しましたので、MKZ4とスマホアプリを使って再現を試みてみました。

まずはiPhoneアプリを作成してみましたので、ご紹介したいと思います。追ってAndroidでの作成方法も記事にしようと考えています。

また、「INTERNET Watch」、「AKIBA PC Hotline!」にてリレー形式で掲載している – 触れてみよう電子工作×IoT 第2回 IoTで「パンツァー・フォー!」 1/48重戦車タイガーIを改造してスマホで遠隔操作してみた<前編><後編> – でご紹介した、1/48戦車をコントロールするカスタマイズ方法も後日ご紹介します。

必要な機材、ソフトウェア

  • Mac (macOS 10.11.5以降が動作しているもの)
  • MKZ4を取り付けたミニ四駆
  • iPhone (iPhone 6 Plus, iPhone 7で動作確認。どちらもiOSバージョンは10.2.1。)
  • Xcode Version 8.2.1 (App Storeからインストール)
  • サンプルアプリのソースコード (GitHubからダウンロード)

iPhoneアプリはMacでXcodeを使って作成できます。最新のXcodeのバージョンは8.2.1で、macOS 10.11.5以降で動作しますので、このOSが動作するMacを使用します。(名前で言うと、OS X El Capitan v10.11.5以降や、macOS Sierraですね。)

iPhoneはモバイルデータ通信が有効になっている必要があります。音声認識には、インターネット接続が必要なのですが、iPhoneをMKZ4のWi-Fiに接続すると、Wi-Fiではインターネット接続できなくなってしまうためです。

もちろんMKZ4も必要です。まだお持ちでない場合は、作例を 文系新卒でもIoTに入門できるのか?! 改造ミニ四駆製作キット「MKZ4」に挑戦 でご紹介していますので、ご参照ください。

事前準備

Xcodeのインストール

  • XcodeをMacのApp Storeアプリからインストールします。App Storeを開いて、Xcodeを検索し、「入手」ボタンを押してインストールしてください。
    mkz4-mac-appstore_01
  • インストールが終わったら、「開く」ボタンをクリックして、Xcodeを起動します。Xcode and iOS SDK Licence Agreementに同意して次に進みます。
  • 「Welcome to Xcode」と表示されたら、Xcodeは準備完了です。

サンプルアプリのソースコードをダウンロード

  • https://github.com/cerevo/MKZ4_VoiceCommander_iOS を開きます。
  • 「Clone or download」ボタンを押し、開いたダイアログのDownload ZIPを選択してダウンロードします。
  • ダウンロードしたファイルをクリックして、展開します。これでソースコードの準備は完了です。

実行してみる

設定を修正する

  • ソースコードのフォルダ内にある VoiceCommander.xcodeproj をXcodeで開きます。
  • 下図を参考に設定を修正します。
  • (1)をクリックしてプロジェクトの設定を開きます。
  • (2)をクリックして、「PROJECT」と「TARGETS」を表示します。
  • (3)をクリックして、「VoiceCommander」を選択します。
  • (4)の「Add account」ボタンをクリック します。(表示されていない場合はスキップして6へ)
  • Apple IDを入力し、(5)の「Sign in」ボタンをクリックします。
    mkz4-ios-xcode_11
  • (6)のTeamのプルダウンメニューをクリックします。
  • (5)で入力したApple IDのPersonal Teamを選択します。(7)
    mkz4-ios-xcode_12
  • (8)のようになることを確認します。
  • iPhoneをMacにLightning – USBケーブルを使って接続します。(9)のようなステータスが表示されていたら、接続することで、解消すると思います。
    mkz4-ios-xcode_13
  • iPhoneの画面をみると、「このコンピュータを信頼しますか?」と確認されるので、信頼をタップします。
  • (10)のインストール先をクリックし、接続したiPhoneを選択します。(11)
    mkz4-ios-xcode_14
  • (9)の部分が更新され、(12)のようになれば完了です。
    mkz4-ios-xcode_15

iPhoneにインストールする

  • (1)のRunボタンをクリックします。
    mkz4-ios-xcode_16
  • 開発者モードを有効にしますか?と聞かれたら、Enableをクリックします。
    mkz4-ios-xcode_17
  • Processing symbol files というメッセージが表示されたら、OKをクリックし、しばらく(5分程度)待ってから、(1)のRunボタンを再度押して下さい。
    mkz4-ios-xcode_18
  • アプリを起動できないというメッセージが表示されたら、メッセージに従ってiPhoneを操作します。
    • iPhoneの設定アプリを開きます。
    • 一般をタップし、下の方にスクロールして、プロファイルとデバイス管理をタップします。
    • デベロッパAppセクションにご自分のApple IDが表示されていると思いますので、そちらをタップします。
    • 表示されたダイアログ内の「信頼」をタップします。
    • 再度、XcodeのRunボタンをクリックします。
      mkz4-ios-xcode_05

アプリを使ってみる

  • アプリが起動すると、音声認識へのアクセス許可を求められますので、同意します。
  • 音声認識を開始するボタンを押すと、今度はマイクへのアクセス許可を求められますので、こちらにも同意します。
  • 再度、音声認識を開始するボタンを押しながら、iPhoneに向かって何か言ってみます。言い終わったところでボタンを離すと、認識結果を表示します。下図では「スタート」と言ったのを認識しています。
    mkz4-ios-app_01_smkz4-ios-app_02_smkz4-ios-app_03_smkz4-ios-app_04_s

MKZ4の電源を入れる

  • 本アプリが想定しているMKZ4本体のプログラムは、製品の説明書の手順通り書き込むサンプルプログラムのままで、特にカスタマイズの必要はありません。もし、何かカスタマイズしていた場合は、以下のサンプルプログラムを書き込んでください。
  • すでにサンプルプログラムを書き込み済みの場合は、電源を入れて下さい。

iPhoneをMKZ4に接続して操作してみる

  • iPhoneのWi-Fi設定を開き、「MKZ4」のネットワークに接続します。
  • 下記のコマンド表を参考に、キーワードを言って、操作します。
コマンド キーワード
前進 進め、行け、いけ、スタート
後退 後退、下がれ
左折
右折
停止 止まれ、ストップ

実装のポイント

以上で、動かしてみることはできるようになったかと思います。以下では、実装について説明したいと思います。

  • ソースコードの構成
  • App Transport Security (ATS) の設定
  • マイクと音声認識を有効にする

ソースコードの構成

Xcodeのナビゲーターエリアを見ながら、主要なファイルについて説明します。
mkz4-ios-xcode_06

  • ViewController.swift
    • アプリ起動後に表示される、メインの画面の動作が書かれています。
    • 画面の初期化、音声認識の初期化、音声認識したキーワードの処理などを行っています。
    • 音声認識のキーワードとコマンドの対応を編集したい場合は、こちらを修正してみて下さい。
  • Mkz4ApiCaller.swift
    • MKZ4へコマンドを送信する処理が書かれています。
    • ViewController.swiftから使っています。
  • Main.storyboard
    • メイン画面の要素の配置を行っています。
    • ViewController.swiftから、このファイル内のボタンやテキストを参照しています。
  • Info.plist
    • アプリの設定が書かれています。
    • 後述の、App Transport Security (ATS) の設定や、マイクと音声認識を有効にする設定については、このファイルに保存します。
  • Localizable.strings
    • 多言語対応の辞書です。
    • ViewController.swiftから、後述のLocalizedStringという関数を使って利用しています。
  • LocalizedString.swift
    • NSLocalizedStringという、多言語化辞書から文言を取得するための関数を、簡単に使えるようにするLocalizedStringという関数が書かれています。

App Transport Security (ATS) の設定

iPhoneからMKZ4にコマンドを送信するときに、HTTPで通信を行っています。ATSの設定がデフォルトのままだと、HTTPで通信できないため、設定を変更しています。Info.plist を開くと、「App Transport Security Settings」に「NSAllowsLocalNetworking = YES」という設定を行っているのが確認できると思います。こうすることで、同じネットワークにつながっているiPhoneとMKZ4でHTTPでの通信ができるようになります。
mkz4-ios-xcode_07

参考: NSAppTransportSecurity

マイクと音声認識を有効にする

iPhoneのマイクや音声認識をアプリから利用するには、ユーザーに許可を求める必要があります。許可を求めるダイアログを表示する際に、理由を表示する必要があり、理由を設定していない場合は、許可を求めようとしたタイミングでアプリが終了してしまいます。Info.plist を開くと、マイクは「Privacy – Microphone Usage Description」、音声認識は「Privacy – Speech Recognition Usage Description」に設定していることが確認できると思います。
mkz4-ios-xcode_08

参考: NSMicrophoneUsageDescription, NSSpeechRecognitionUsageDescription

終わりに

いかがでしたでしょうか。MKZ4、iPhone、Mac、Xcodeと、準備が少し大変かもしれませんが、一度準備が整えば、自分好みに修正していろいろと遊べると思います。今回の構成では、音声認識の結果を得るまでと、MKZ4にコマンドを送信終わるまでの時間が必要なため、どうしても応答性が悪く、残念に思うこともあるかもしれません。そのあたりは、今後の技術の発展が解決してくれることを祈りつつ、まずは音声で操作することを楽しんでいただければ幸いです。

バックパッカー的な海外旅行の持ち物

お久しぶりです。ツバサです。
以前トルクメニスタンに関するエントリを書いて以来です。
その節は多くの方にエントリを見ていただいたようで誠にありがとうございました。

僕の最近の仕事といえば『旅行用デバイスを開発すれば会社のお金で海外旅行に行けるのでは!?』と夢想することですが、
これが思ったよりも大変です。

何も思いつきません。

まずいですね。
このままではただの妄想にふけっている人になってしまいます。

やはりアイディアというものはオフィスに座っているだけでは出てこないようです。
こうなったら実際に旅行に行ってみるしかないでしょう。

そういったわけで最近は単に妄想を膨らませているだけでなく、旅行の予定を立てることにも余念がありません。全く忙しい限りです。

さて、行先をはじめ旅行に関する情報を得る手段は色々ありますが、他の旅行者さんのブログ等web上の情報も大事な情報源の一つです。
僕も日々参考にさせてもらっています。
しかしこれまでは情報を受け取る一方でこちらからは何も発信してこなかったなぁ、という事に思い至り、これからは自分でも海外旅行情報を発信していこうかなと思っています。

今回は準備編ということで、まずはどんなものを持って行っているかということを紹介したいと思います。

旅のスタイル

旅行と一口にいっても色んなタイプがあります。
気ままに安宿を渡り歩く旅行もあれば、ツアーに参加する旅行もあるかと思います。

たとえば、しっかりとしたホテルに宿泊したり、格式のあるレストランで食事をするのであればスーツや革靴など相応の装いが必要になるかもしれません。
そうなってくるとバックパックよりもスーツケースのほうが荷物を入れるのに向いています。
このように旅のスタイルによって持っていく荷物も全く変わってしまいます。

そこで、このエントリ(とその続編)ではいわゆるバックパッカー的な旅行を前提としてお話しすることにします。
バックパッカー的と言っても特に定義があるわけではないのですが、
大まかなイメージとしては

  • 旅行会社のツアー参加ではない、個人旅行である
  • スーツケースは使わず、バックパックに荷物を入れる。
  • “Cheaper is better”が合言葉
  • ドミトリー(相部屋)宿泊が基本
    IMG_0613

といったところでしょうか。

持ち物

海外旅行に際し『何を持っていけばいいのだろう』と悩む人は少なからずいらっしゃるかと思います。
あれもいるかも、これもいるかもと、どんどん荷物が増えていきますが、往々にしてそのうちの大半は結局使わなかったりするものです。
僕も初めて一人旅をしたころには役に立たないものをたくさん持っていきました。

何度も旅行をしながら、以前使わなかったものを置いていき、一方で新しく出会った便利そうなグッズを新たに持っていくという、終わりのない最適化を繰り返しています。
下記では現時点ではこんなものを持って行っているよ、というのをご紹介します。

パスポートケース

僕が使っているパスポートケースはこちらのスライド式のもの。
ズボンと下着の間に隠すタイプです。パスポートのほかにも盗まれたくないものを入れておくのもよいでしょう。
こういった身に着けるタイプのパスポートケースは首下げや腹巻タイプなど数多くありますが、首から下げるものは外見からわかりやすく、腹巻タイプのものは取り出すのが一苦労なのが欠点です。
このタイプなら外見からも身に着けているのがわかりにくく、さっと取り出しやすいのでおすすめです。

パスポートケースにいれてるもの

  • パスポート
    多くの国ではパスポートの有効期限が半年以上残っていることを要求しています。出発前に有効期限を確認し、必要なら更新を行いましょう。
  • パスポートのコピー
    特に役に立ったことはないです。が、捨てる理由もないので入ったままになっています。
  • 予防接種記録
    アフリカ諸国と南米諸国では黄熱病の予防接種証明書の提出を入国の条件としているところがあります。そのため、渡航地がそういった国である場合には予防接種を事前に受け、その証明書を携行している必要があるのです。
    僕の場合、日本でA型肝炎、B型肝炎、破傷風etc の予防接種を受けた他にもトルコで黄熱病の予防接種を受けたので2つの接種記録をパスポートケースに入れっぱなしにしています。どこか他のところに入れておくと確実に紛失する気がしています。
    ちなみにこれが役に立ったことは一度もありません。
    黄熱病の予防接種記録の提出を条件にしている国にもいくつか行きましたが、提出を求められたことはなかったです。
    tb-2
  • 予備のクレジットカード
    メインのクレジットカードはお財布に入れて置き、普段はそちらを使いますが、財布を無くしたり盗まれたりした時に備えて予備のクレジットカードをパスポートケースに入れておきます。

財布

財布はこちらのものを使用しています。
以前はもっとアウトドア丸出しのものを使用していたのですが、流石にもうちょっとオシャレなものを使おうかなと思い購入。
条件として①見た目がいい、②チェーンやベルトがつけられる、の2つを基準に選定しました。
②はスリ防止の観点からです。こちらの財布にはベルトは付属していないのでベルトは別途アマゾンで購入しました。

財布の中身

  • 現金
    渡航地にもよりますが、現地通貨はまず現地についてすぐに困らないように1万~2万円程度を空港で替えておきます。足りなくなったら後はクレジットカードでのキャッシングで現地通貨を調達しています。
    行きと帰り用に適当な額の日本円もいれておきます。
  • クレジットカード
    クレジットカードは複数枚持ち、できれば別々の場所に保管しておきましょう。
    ブランド的にはVISAとMaterは世界中どこでも使えて、American Express はアメリカ方面では強く、アジアではJCBもそこそこ、という感じでしょうか。
    僕はマイルを稼ぐことにもラウンジを利用することにもあまり熱心でないので、海外旅行保険さえついていればその他の旅行関連の特典は気にしてません。
  • その他諸々
    レシートとか切符の半券とか特に意味もなく入れたままになってたりします。

サブバッグ(街歩き用バッグ)

サブバッグにはこちらで紹介されているpacsafe のCS200(大きいほう)を使っています。
生地のなかにワイヤーメッシュが入っておりバッグがナイフ等で切られても中身が取り出せないようになっているのをはじめ、スリ防止、置き引き防止の機能が盛り込まれています。
見た目はシンプルな肩掛けのバッグなのでいかにも『観光客です』という雰囲気を出さずに済みます。
飛行機内に持ち込む荷物もこのバッグとなります。

サブバッグにいれてるもの

  • BOSE QuietComfort 35
    機内をどう快適に過ごすか、というのは海外旅行する人にとっては一つのテーマです。
    これもその一環で、BOSE に限りませんがノイズキャンセリング機能を持ったヘッドホン等は機内の独特な重低音を忘れ去るのに向いているように思います。
    飛行機内で使われている端子への変換器を使い機内上映の映画や音楽もこのヘッドホンをつけて聞いています。
    イヤホンなどに比べると大きい荷物ですし、盗まれると割とショックですが快適さを優先して持って行っています。
  • Kindle paperwihte
    主に飛行機内での暇つぶし用です。スマートフォンやタブレットでも電子書籍を読むことはできますがバッテリの持ちの観点から別途Kindle をもっていっています。
  • Fireタブレット
    情報端末は何個か持っていると役立ちます。FireタブレットはiPad 等と比べるとお安いので万一なくしたり盗まれたりしても惜しくないので旅行用に購入しました。
  • マスク
    飛行機内でつけて喉の乾燥を防止します。あとつけていると涎を垂らしても気付かれません。
  • サングラス
    普段は屋内に引きこもっているので使用しませんが、旅行中は太陽の下を歩かないといけないので装着します。
  • 文房具
    何かと便利です。
    飛行機内で入国カードを書く時にも役立ちます。
  • 常備薬
    頭痛薬や酔い止めなど必要に応じてもっていきましょう。

メインバッグ

メインのバッグとしては Karrimor flyer 50-75 を使っています。
どうも75Lまでのモデルはなくなってしまったようで、現在では65Lまでのモデルになるようです。
特にこだわりがあるわけではないのですが、
①フロントアクセスを採用しているためそうでないものに比べてパッキングが楽
②最大75Lと荷物が沢山はいる。
等の点が気に入っています。

通常の観光旅行であれば50Lあればまず十分かと思いますが、
現地でキャンプや登山をする予定があると荷物が増えるので上限75Lはとても心強いです。
元々登山用だからか丈夫にできており壊れる気配もないので、しばらくはこれを使っていきそうです。

メインバッグに入れるもの

電子機器関係

  • 無印良品の変換プラグアダプター
    これ1セットでほぼ全ての国のプラグに対応できる優れものです。
    デザインもよいので非常に気に入っています。
  • 電源タップ
    ドミトリーに宿泊していると充電したいのに電源口が足りないということがあります。
    また、電源口の位置が微妙で自分のベットから使いにくいということも。
    そういう時のために普段家庭でつかっている電源タップを持っていきます。ケーブルの長さは1mもあれば十分かと思います。
  • 各種ACアダプターとケーブル類
    スマホをはじめとする電子機器の充電用に適当な数入れておきます。
  • 電動髭剃り
    普段家で使っているものをそのまま持っていきます。
    旅行用に小さめのものを用意したりはしていません。
    僕はアトピーを患っているため、普段と違うものをつかって皮膚にダメージを与えてしまうより、少し大きくともいつもと同じものを使い、旅行中に症状が悪化しないようにという判断です。

服関係

服や下着などは旅行日数と旅行先の気候に合わせて適当にもっていくという感じです。
この点については特にこだわりはないです。毎回適当に決めています。
ただ、荷物で一番大きな容積を占めるのがこの服関係になるので、あまり多めには持って行かず『足りないかな?』と思うぐらいにしています。
もし不足があったら現地で買えばそれでOKです。

その他

  • 洗面具入れ&シャンプーとか
    無印良品の洗面具入れにシャンプー、ボディーソープ、化粧水等々を入れています。
    これらもアトピーが現地で悪化しないように、普段使っているものを小分けの容器(これとか)に入れて持って行ってます。
  • 歯磨き用セット
  • 速乾性のタオル
    ミズノのものを使っていますが、最近手ぬぐいのほうがいいのではないか?と思い始めています。
    ここはもっと良いのないかなーと探しているところです。
  • Pacsafe のバックパック用プロテクター
    観光する際には大きなメインバッグは宿において身軽に歩きたいものですが、ドミトリーに宿泊していると他人が自由に入ることができる部屋にバッグを置いておくことになります。そうなると盗難が心配ですが、このプロテクターでバッグ全体を覆ってしまえば安心です。最終的に南京錠やダイヤル式錠でのロックになるので実際の堅牢さはともかく、泥棒に心理的に与える影響は大きいでしょう。
  • バックパックカバー
    正確にはメインバッグに”いれてる”ものではなく、メインバッグに”かぶせる”ものです。
    本来は雨でバッグが濡れないようにすることが目的ですが、メインバッグを空港で預ける際にバッグから垂れるベルト部分が嫌われることがあるので、カバーをかけてカバーの中にできるだけベルトを収納しておくのに役立ちます。
    tb-1
  • ロープ
    登山用のものを使っています。
    荷物を自転車に括り付けたり、洗濯干しにつかったりと何かと役に立ちます。
    IMG_5660
    1
    a (782)
    ↑の写真には洗濯バサミやハンガーが写っていますが、自分で洗濯する必要があるほど長期の旅行を最近しないので、これらを持っていく機会はあまりないです。
  • フォーク&スプーン
    現地のスーパーなどで食料を買っても箸もフォークもない、ということがあります。
    最悪手で食べればよいのですが、アウトドアショップなどでよく見かけるフォークとスプーンが一体になったものを持っていると便利です。
    IMG_5664

当日身に着けているもの

  • 財布
  • スマートフォン
    スマートフォンは普段自分が使っているものをポケットに入れていきます。ちなみに現時点ではiPhone 6 plus です。
    盗まれたりするとダメージ大きいですがやはり普段から使っているものが便利なのでここは快適さ優先です。もし盗まれても情報端末が全くないという状態にならないように予備として Fireタブレットを配備してあります。

  • 僕の場合、トレッキングなどを現地で行うこともあるのでそれ用にも使える靴として、
    アディダスのテレックス FAST R MID Gore-Tex を使っています。
    この靴の一番気に入っているところは自分の足によく合っており、長時間歩いても辛くならないところです。
    一応トレッキング用ではありますが、登山する際にもこの靴を使っています。
    たくさん歩いてもストレスがなく、しかも街歩きから登山までカバーできる便利な靴として非常に気に入っています。

終わりに

さて、持ち物はこんなところでしょうか。
本当は持って行っているけど書き忘れているというものがあるかもしれませんが、それは後々追記させていただきます。
しかし、絶対必要というものはパスポート、現金やカードぐらいで、大体のものは現地で買えます。なのでそんなに心配しなくても大丈夫です。
ヨーロッパやアメリカへの旅行ぐらいなら、東京から京都に行くのとそれほど変わりありません。
海外旅行だからといって気負うことなく気楽にいきましょう。

次回予告

次回のエントリの内容は全く決まっていません。
気長にお待ちいただければ幸いです。

ではまた。
a (78)

RIDE-1のオープンソース版ステー、外観データを公開

RIDE-1製品マネージャの佐藤(@key3)です。

いろいろな自転車でRIDE-1が使えるように、Hack RIDE-1プロジェクトの第1弾としてオープンソース版のステー、外観データの3Dモデルデータを公開しました。

スクリーンショット 2017-02-06 23.10.09

ファイルはSTL形式となっており、3D CADを用いて変更することができるため、3Dプリンタで出力することで、これまでRIDE-1が取り付けられなかったユニークな形状の自転車や、その他の乗り物で利用することができるかも知れません。

40a67a4668ae7244137ec13c1b4ea9ee-825x510

ステーのモデルデータは製品と同じく自転車のボトルケージに取り付けるタイプのもので、S / M / Lの3種類をMITライセンスで公開しています。

外観のモデルデータも製品と同様のもので、うまく利用することでオリジナルのステーを作成することができるでしょう。こちらはCC BY-NC-ND 4.0に従ってご利用ください。

また、1月を少し過ぎてしまいましたが、先月実施したRIDE-1の更新内容をお届けします。

パスワードリセット機能の追加

パスワードを忘れてサービスにログインできなくなったことはありませんか?RIDE-1ウェブサービスにパスワードリセット機能を追加しました。これでパスワードを忘れても安心(?)ですね。

アクティビティ表示画面のユーザビリティ改善

地図を操作しているときに自動的にスケールが変わってしまったり、スクロールしてしまうことがありました。少々使いにくい印象でしたので、先月の更新で動作を変更しています。

※ユーザビリティにまつわる改善は不定期に実施されます。

また、今後数週間で以下の対応を進めていく予定です。

  • アクティビティ同期速度の改善
  • アクティビティ表示の改善
  • ANT+ Bicycle Powerプロフィールの対応

近日中にRIDE-1本体のBluetooth Low Energyのキャラクタリスティック定義とWebAPIの公開を予定しています。面白いアイデアがあればぜひ info@cerevo.com までご連絡ください。

文系新卒でもIoTに入門できるのか?! 改造ミニ四駆製作キット「MKZ4」に挑戦

こんにちは。中村です。

私はCerevoの中でも数少ない新卒のうちの、さらに希少種である文系出身です。「IoT」というバズワードが発する芳ばしい香りに誘われて2016年4月から事務系職として働いています。

さて、文系出身者である私は「IoT」のInternetもThingsもこれまで対して勉強したことがありません。しかし徐々に社外の方と交流する機会も増え、IoTについて質問をされることがよくあります。最初はそれっぽい適当な返答でごまかしたり、抽象度を引き上げて煙に巻いたりと、のらりくらりかわし続けてきたのですが、それにも限界があると感じ、いよいよ本格的にIoTについて勉強しなければならないと思い始めました。

しかしながら、世に出回っている「IoTがわかる!」系の本を何冊か手に取ってみたものの読んでもよくわかりません。人工知能とか自動運転とかが書いてあっても、それをどう実現するのかは書いてありません。

「これは困ったぞ……理系でなければIoTを扱うことはできないのではないか……」と思っていたそんな折、なんと弊社からIoTが学べる学習キットが発売されました。

今回のエントリでは、私が実際にこのIoT学習キットである『MKZ4』の組み立て行い、その体験を通じてIoTって実際に何をするものなのかをお伝えできればと考えています。

まだIoTとはなんじゃらほいという方も、是非このブログを参考に、『MKZ4』にトライしてみてください。

それでは早速行ってみましょう!

準備編

s-P_20161216_154057

  • MKZ4
  • MKZ4WK
  • ワイルドミニ四駆
  • MKZ4ガイドブック(Kindle版)

準備するものはこの四つです。

今ならなんと「MKZ4」「MKZ4WK」「ワイルドミニ四駆」をセットにしてギフトラッピングに対応した数量限定スペシャルモデルがCerevo official storeで販売されています。

また、MKZ4ガイドブックは腕に自信がある方はなくても構いませんが、電子工作が初心者という方は無いと厳しいです。付属のマニュアルだけでは落とし穴があります。Kindle Unlimitedでも読むことが可能ですので、ぜひ用意して臨むことをお勧めします。

電気編

準備が整ったらいよいよ開始しましょう。

s-P_20161216_154648

MKZ4を開封するとこんな感じです。

s-P_20161216_154812

こんなに小さなパーツもあるのでなくさないように注意しましょう(※これは振りです)。

s-P_20161216_154944

まずは電子部品から基板の上に載せていきます。ちなみに「基盤」ではなく「基板」です。これは入社してから何度も注意されますので、覚えておくとCerevoに入社しても困りません。

s-P_20161216_155211

はじめの一歩は抵抗です。3種類あります。パーツの色をよく見て間違えないように乗せていきます。s-P_20161216_155333

根元からガッツリ曲げないとこんな感じで浮きます。浮いても大丈夫ですが不格好なのでなるべく基板にぺったりくっつくようにします。

s-P_20161216_155444

こんな感じ。

s-P_20161216_155451

裏側です。

さて、ここまでできましたら、いよいよ半田付けを行っていきます。半田付けを行ったのは、中学3年生の時に手回しラジオキットを授業で作成たのが最初ですが、Cerevoに来てちょくちょく触らせてもらっていたためちょっと自信があります。

s-P_20161216_155635

機材はこちら。製品の基板は顕微鏡ないと厳しいですが、今回はなくて問題ありません。

s-P_20161216_155910

こんな感じでランドを熱し、左サイドから半田を付けます。

s-P_20161216_160029

1個付きました。

s-P_20161216_160131

反対側も半田付けします。

s-P_20161216_160449

基板を覗いてみて、穴が開いていなければしっかり半田が盛られています。穴が開いていると取れやすく、不具合の原因になるのでたっぷり盛っておきましょう。

s-P_20161216_160527

余った線はニッパーで切り取ります。

s-P_20161216_160146

最初の1つが乗りました!

s-P_20161216_161521

次々に抵抗を乗せていきます。

s-P_20161216_161817

セラミックコンデンサも乗せていきます。

s-P_20161216_161851

裏側です。半田付けするところがたくさんありますが、まだ序の口です。

s-P_20161216_162209

サイドから見て半田の盛り方がおかしいと感じたらその都度直します。

s-P_20161216_162556

お次にピンヘッダ。

s-P_20161216_162635

手で割ります。

s-P_20161216_162724

基板に置いて、

s-P_20161216_163142

マスキングテープで固定します。

s-P_20161216_163337

こうしておくことで裏返してもぐらつきません。

s-P_20161216_163709

半田付けし、おかしそうな箇所は修正します。

s-P_20161216_164001

ついでにL字ピンヘッダもつけてしまいました。

s-P_20161216_164124

LEDライトと電解コンデンサー。これらは取り付けの向きが決まっているので注意が必要です。

s-P_20161216_165315

さて、最初の難関のチップ抵抗まで来ました。すこぶる小さいです。鼻息で飛んでいくので扱いに注意が必要です。

s-P_20161216_165553

もはやフィルムをはがすところから苦戦。

やっと剥せたところで撮影するための席から半田席へもどろうとしたところ……

s-P_20161216_165731

落としました……。

あれだけ気をつけたのに、ピンセットで持ち運びをした結果がこれです。完全に迷彩になっています……。(ちなみに右上の白い点がそれで、探すのにとても苦労しました)
みなさんも本当に気をつけてください。

s-P_20161216_165842

気を取り直していきます。基板のランドの片側に「予備半田」とよばれる半田をあらかじめおいておきます。

s-P_20161216_170029

ピンセットで位置を合わせ、半田を溶かして乗せます。

s-P_20161216_170143

右側にも半田を置いて完成です。右側は上から半田を置いただけなので、若干不安です。しかしここは後々エラーが出たらその時に考えることにして先に進みます。

s-P_20161216_170326

電気編のクライマックスが早くも登場です。

s-P_20161216_171815

動かないよう台座に固定して、フラックスという秘密兵器を使います。これを塗ると半田付けが非常にしやすくなります。

s-P_20161216_172409

まず、最端の一個内側の4ヶ所を半田付けし、ESP8266を基板に固定します。端のピンはGNDにつながっているので半田付けに時間がかかり難易度が上がります。そのために内側のピンから半田付けを行います。

s-P_20161216_173444

非常に苦労しましたが、押切先生のご指導の下、すべてのピンの半田付けが完了しました。このESP8266の半田付けの解説はこちらの記事を参照してください。動画付きで非常にわかりやすく解説されています。

s-P_20161216_174124

山を越えたので後は一直線です。残りのコンデンサーをおいていきます。一つは後でモーターにつけるのでこの時点で余っていても問題ありません。

s-P_20161216_174911

モータードライバモジュールもつけ、終わりが近づいてきました。

s-P_20161216_175128

ワイルドミニ四駆の方からモーターを取り出し、余っているコンデンサーを付けます。

s-P_20161216_175439

この時忘れないようにピニオンギアをつけましょう。後から付けようとすると基板ごと動かす必要があり面倒です。

s-P_20161216_175507

電池ボックスを用意し、赤と黒の線をモーター接続用に切り離します。60mmです! ここで私は適当に半分くらいに切ったために後で電池ボックスの収納に苦労しました……。

s-P_20161216_175825

モーター。

s-P_20161216_175903

基板取り付けのため導線の被覆を向きます。オフィスにはそれ用のこんな便利なペンチがあります。

s-P_20161216_175918

つるん。

s-P_20161216_180551

途中、ブリッジしてしまったところの半田を吸い取ります。

s-P_20161216_181734

取り付けが完了しました。

これでMKZ4基板は完成です!

MKZ4WKを準備する

さて、お次はMKZ4WKです。
MKZ4WKとは、MKZ4にプログラムを書き込むのに必要なものです。

s-P_20161216_182017

これを、

s-P_20161216_182826

こうして、

s-P_20161216_185553

完成です!(特に難しい点はないので三分クッキングスタイルで行きます)

PCと接続して青いLEDが光ればOKです。

MKZ4基板にプログラムを書き込む

次からはMKZ4にMKZ4WKを通してプログラムを書き込んでいきます。

s-P_20161216_190126

MKZ4のジャンパーがD側になっていることを確認してMKZ4WKをMKZ4に接続します。

s-P_20161216_190115

MKZ4WKの接続する向きに注意しましょう。

ソフトウェア編

ソフト側の準備として、Arduinoのインストールが必要です。ここからダウンロードしておきましょう。以降ではインストールが済んでいることを前提に進みます。

arduino01

まず環境設定から、

arduino02

ボードマネージャーのURLを追加します。

arduino03

ボードマネージャーから、

arduino04

esp8266 by ESP8266 Communityのバージョン2.2.0をインストールします。

次にMKZ4のスケッチ(ソースコード)をここからダウンロードし展開しておきます。

arduino05

展開したファイルをダブルクリック。

arduino06

ボードマネージャーから該当のものを選択。

arduino07

コンパイルし、

arduino09

完了したらOKです。

arduino10

シリアルポートが表示されていればそれを選択します。

arduino11

マイコンボード(MKZ4)に書き込みます。

arduino12

書き込みが完了しました!

s-P_20161216_190531

ジャンパーをB側に移して、電池ボックスの電源をONし、赤色LEDが点灯すれば書き込みが無事できています。

ここまでで電気とソフトウェアは終了です。

メカ編

さて次に、ワイルドミニ四駆の組み立てに移ります。

s-P_20161216_193656

右上がMKZ4付属の特製パーツで、他はワイルドミニ四駆となります。

s-P_20161216_194628

前輪のパーツを切っていきます。

s-P_20161216_194733

二か所に切り込みを入れると、あとは力技で割れます。

s-P_20161216_194940

本体シャーシも前輪部分を切っていきます。

s-P_20161216_195332

ザクザク切ります。

s-P_20161216_195825

前輪部分がカットできればOKです。手が疲れるので適宜休憩しながら行いましょう。

s-P_20161216_200124

ギアをはめます。前輪部分は一つ使いません。

s-P_20161216_200607

カバーをパチリ。

s-P_20161216_200854

シャーシに穴をあける準備として、シールを張ります。

s-P_20161216_201752

穴をあけるには3mmのピンバイスが必要ですが、あいにく壊れたドライバーしかオフィスにありませんでした。良いのです。ドライバーが必要なのではなく穴が必要なのですから。

s-P_20161216_202303

こんな感じで二か所に穴をあければシャーシ側はOKです。

s-P_20161216_202650

ホイールの加工をします。ギア部分をカット。

s-P_20161216_203117

ドリルで六角の穴を丸くします。前輪について二つのホイールを加工します。ここで前輪に該当するホイールを誤ってしまうと完成しません。穴をあける前によく確認してください。なお、私はギアがついているホイールを両方とも前輪と勘違いしたため、とんでもないことになりました(後述)。

s-P_20161216_203635

タイヤを装着。

s-P_20161216_204110

黒いパーツを埋め込みます。

s-P_20161216_204434

この辺りは説明書通りなのでサクサク進みます。(※実はここで映っているタイヤは本来前輪ではありません。その事実にこの時点で私はまだ気づいていません……)

s-P_20161216_204545

今回のキモのサーボです。サーボホーンの先端を合わせます。

s-P_20161216_205013

ネジを留めていきますが、複数あるので注意が必要です。

s-P_20161216_204833

まずはサーボ本体を取り付けます。

s-P_20161216_210131

このように、先にサーボホーンに穴をあけましょう。

s-P_20161216_210727

s-P_20161216_210938

どんどんねじ留め。

s-P_20161216_211144

前輪部分を本体シャーシに合体します。

s-P_20161216_212059

前輪を取り付け……と行きたいところですがここで問題発生です。

なんと、前輪と後輪の組み合わせを間違えていました。

s-P_20161216_212113

正しくはこちらです。

左後輪はギアがないとモーターの動力を受けることができません。わたしはこのギアがあるタイヤを前輪と勘違いしていたため、誤ってホイールの穴をドリル丸くしてしまいました。押切先生に懇願してタイヤを譲ってもらったのですが、交換用がない場合は新しいワイルドミニ四駆を準備する必要があります。大出費になってしまうので、くれぐれもドリルで穴をあける前にはどれが前輪でどれが後輪か注意して行ってください。

s-P_20161216_213119

タイヤを取り付けます。

s-P_20161216_213536

モーターをパチリ。

s-P_20161216_213932

MKZ4基板を両面テープでくっつけます。

s-P_20161216_213937

上から見た図。

s-P_20161216_215519

電池ボックスも本体シャーシに両面テープで固定します。私は導線を測らずに切ってしまったため短くなりすぎ、中央に固定することができませんでした。中央に固定しないと重心が左右に偏ってしまいますので、導線はきちんと測って切りましょう。

s-P_20161216_214626

s-P_20161216_214705

s-P_20161216_215019

後輪も組み立て、取り付けます。

s-P_20161216_215947

上側を留め具で留めます。

s-P_20161216_220129

最後に後輪の軸の保護パーツを取り付けます。

メカはこれで完了です。

個人的に鬼門だったのはサーボホーンの穴あけです。ネジがうまく回らずに時間がかかりました。タッピングネジには先が尖っているものと丸いものがありますが、尖っているものを用いることで穴を拡大しながらネジ締めを行うことができます。サーボホーンの穴あけには先が尖っているものを用いましょう。

動作確認編

電気とメカが完成しました!

いよいよお待ちかねの動作確認です。テスタでの測定もかっ飛ばして先に進めることだけに注力していたので、内心ビクビクです。

s-P_20161216_220141

電池ボックスに単四電池を三本入れ、電源をONにします。すると中の基板の赤LEDが点灯します。

mkz401

スマートフォンのWi-Fiから、「MKZ4」を選びます。パスワードを入力する必要はありません。

接続が完了したら192.168.4.1にブラウザからアクセスします。

Screenshot_2016-12-16-22-02-56

接続が確立すると、「CONNECTED」と表示されます。うまくいっているようです。

Screenshot_2016-12-16-22-05-04

フリックで操作します。

s-P_20161216_220455

おお! サーボが反応して前輪を左に傾けています!

s-P_20161216_220518

勢い余って段ボールに激突してしまいました。

かなり急ぎ足で組み立てたため、まっすぐ走らせようとしても左に曲がっていきます。逆に右に曲がりません。これはサーボの中心位置がずれているため起こっていそうです。修正が必要なところですが、ひとまず動いたことに満足して、今回はここまでにしたいと思います。

終わりに

いかがだったでしょうか。

初めての『MKZ4』組み立てでしたが、私は4, 5時間はかかったと思います。これは日ごろ半田付けを訓練していたので、チップ抵抗やESP8266も問題なく、また基板の半田付けでエラーが生じずにストレートに動作確認までこぎつけられたからでしょう(ある意味エラーがなかったため起伏の乏しい記事になってしまったことは反省です)。半田付けも初めてという方は、この2倍程度の時間を想定されると良いかもしれません。

丸1日、日ごろの雑事を忘れて童心に帰るというのは楽しいですよ!

是非みなさんもトライしてみてください。

[24日目] NAT Traversalって知ってますか

Cerevoアドベントカレンダー2016、最終日です。といっても、どうやら大トリは弊社代表が年末までに昨年のネタの更新版を出すようなので、私はトリらしい何かとかでもなく、テックブログらしく技術ネタを書きたいと思います。
まつけんです。CTOをしています。今日はハードはほぼ関係ない、ソフトというかUDP/IP、TCP/IPな世界の話です。IPレイヤーより上でのお話です。

まず、NATと言われて動作を想像できる方どれくらいいるでしょうか。今や、ルータという名でNATが動作する機器は各家庭にほぼ設置されているのではないかと思いますし、携帯向けネットワークも昨今はLarge Scale NATもしくはCarrier Grade NATの導入という形でちょっと話題になったようにNATが導入されています。そんな世界では、グローバルIPが直接振られるのではなく、ルータやキャリア側でローカルIPからグローバルIPへのアドレス変換が行われるのが一般的です。NATと言いますが、今挙げた例は、正確に言えば、IP masquarade(マスカレード)です。NATはNetwork address translationなのでアドレス変換をするというもう少し広い意味で、グローバルIPとローカルIPが1:1で変換されるようなケースも包含されますし、サブネットを分けたローカルネットワークでの1:1や1:Nでの変換などにも利用されることもあります。

今回は、最も一般的な多:1でローカルIPからグローバルIPへとソースアドレスを書き換えて通信を行う(グローバルIPアドレスを共有する典型例)、ほぼどんな環境でも導入されていると言っても過言でなくなった、いわゆるIPマスカレードされている環境でのP2Pでの通信を実現する、NAT Traversalのお話です。

NAT Traversalとは

まず、NAT Traversal、日本語で言うと、NAT越え(NAT超えかもしれません。ここでは越えで統一します。)はなぜ必要なのか。IPv4アドレスが世に溢れ、すべてのデバイスにグローバルIPが割り当てられている状態であればそもそもNAT越えは必要ないわけですが、現実にはIPv4アドレスは足りないし、ルータ下にいない直接グローバルIPをデバイスがもつというのはセキュリティ的にも推奨されない(デバイス自身がbindしているポートへの通信をローカルネットワーク内のパケットに限定でき、脅威となるデバイスやサーバが容易に直接のアクセスがしづらい)のが現実です。そんなNATがあふれる世界でもP2P通信をしたい、という需要を満たすためにNAT Traversalという技術(というか、手法でしょうか)が考えだされるわけです。これはもうかなり昔からあるもので、様々な資料がネット上にもありますが、一度、自分の整理のためにこの記事を書いてみている次第です。

ルータの下にいるデバイス同士が直接するのはなぜそんなに難しいのでしょうか。まず簡単な通信の流れを下記図を見ながら説明します。もっとも一般的な構成として、ルータ下のネットワークをローカルネットワーク、ルータの外に存在するネットワークをグローバルネットワークとして考えます。そのときに、ルータ下のデバイスがグローバルネットワークと通信とパケットをルータの外のネットワークにあるアドレスに送ります。そのローカルネットワークの内からNATを通して外へという形で出て行く分にはルータがアドレス変換をよしなに行います。これがIPマスカレードです。やることはそんなに難しくなく、TCPの場合、ソースになったIPアドレスとポート(つまり、デバイスのIPとポート)と、宛先になるIPアドレスとポート、ソースアドレス書換後のルータ自身のグローバルネットワーク側のアドレスとポートを、最初にデバイスからパケットが来た時に組み合わせで覚えます。その上で、ソースアドレスをNATが動作しているルータ自身のIPとポートに書き換え、宛先にパケットを投げる、宛先からパケットが返ってくれば覚えてるマップにしたがって、その返信パケットの宛先をローカルネットワークにいる通信元のデバイスに書き換えてパケットを送信します。こうすることで、デバイスはソースアドレス変換が行われたことを知ることなく、外のインターネットな世界と通信ができるわけです。

oneNAT
ここでNAT越えの話に戻ります。でも、宛先はあくまでグローバルIPでなければ(ルータを通してNATを通じて書き換えが行われなければ)、ルータは宛先を書き換える機能を発揮できません(通信双方のアドレスやポートを覚えて変換と転送ができません)。では、相手もNAT下にいる場合、そのローカルアドレスを指定するわけにもいきませんし(ローカルネットワークのアドレスなわけで別ネットワークにいるので届くわけはありません)、グローバルIPはルータがもっているわけなので、ルータ下に居るデバイスと通信するというのは極めて困難なわけです。

NATを越えてデバイス同士が直接通信するには

これの安直な解決方法のひとつがサーバーリレーです。要はデバイス同士が同じサーバに接続をして(この場合、サーバがグローバルIPを持っているのでどちらもルータ下のローカルネットワークから容易に接続ができる)その後の通信はサーバが介在して両者で通信を行います。ただ、この方式、TCPレベルで解決するには宛先を別途アプリレイヤーで指定する必要がありますし、なによりも、トラフィックがすべてサーバを経由するので、サーバの通信量が2倍、そして、サーバを経由するため経路としてはあきらかに冗長に長くなりレイテンシが悪化します。これは、P2Pでの典型的な例である、ビデオチャットやゲーム対戦の特性に対してあまりマッチしません。

ここまで前提を並べてきて、やっと本題にたどり着きました。それぞれのデバイスがルータの下にいたとしても直接パケットをどうにかして届かせたいという必要性が出てきます、それがNAT越えです。ここからはNAT越えを単純な手法から順に説明していきましょう。まずは極めて単純なUDP hole punchingです。これはあとから紹介しますが、特定の動作をするNATでしか通用しません。ここから、NATの種別ごとにどういう形で対応を増やしていくか、というのがこのNAT越えの議論のおもしろいところです。

fullconeNAT
UDP hole punchingは上記の図の通り、cone NATが対象である限り、NATのグローバル側のIPとポートさえわかれば、そして、それをサーバで観測すれば、その後はサーバからIPとポートをそれぞれが教えてもらい、そこに対して、UDPパケットを投げ込めば到達できるわけです。
ここで、唐突にでてきたcone NATを含め、NATの動作によって分類をまず行いましょう。

  • full cone NAT
  • address restricted cone NAT
  • port restricted cone NAT
  • sequential port smmetric NAT
  • random port symmetric NAT

というふうに考えると、NAT越えの方法と特性が分類されます。まずはconeとsymmetricの差を考えてみましょう。

cone NATはシンプルな動作です。ルータ下のデバイスがグローバルアドレスに対して通信を開始し、パケットを送信したとき、ルータはソースになるIPとポートを覚えるのみです。それにしたがって、ルータのグローバル側のIPとポートをマップします。つまり、グローバル側のIPとポートに対して、グローバル側にいるデバイスであれば誰でもパケットを送信し、それはルータ下のデバイスにパケットはアドレスを書き換え転送されます。したがって、下記のような流れを経れば、UDPによるP2P通信が可能になるわけです。

  • ローカルネットワークAに存在するデバイスをαとし、ローカルネットワークBにいるデバイスをβとします。αとβが互いにUDPパケットを直接送受信できるのが最終目標です。
  • αとβを仲介するサーバをSとします。まず、P2P通信を開始したいという意思をαとβで共有します。そして、通信を開始するぞとなるタイミングをSから受け取ります。
  • すると、αとβはまず、サーバSに対して、UDPパケットを送信します。すると、パケットはαとβそれぞれのNATされたグローバルのIPとポートを観測することができます(ソースアドレスはNATされたルータのものが見えます)
  • それをαの情報はβへ、βの情報はαへと、サーバから通知を行います。(たとえば、P2P通信開始処理の間は、サーバとの間にTCPがはられていてここでやりとりするとかです。)
  • αはβのルータのグローバルのIPとポートに対して、UDPパケットを投げます。βも同様です。
  • cone NATであるため、ソースアドレスやポートに制限はありませんから、それぞれのUDPパケットは無事、それぞれのデバイスに届くことになります。

さて、cone NATの場合、こういったサーバが介在してアドレスとポートを伝えるという処理はあるものの、まだまだ単純な処理で終わります。次はaddress restricted cone NATとport restricted cone NATを攻略していきましょう。

address restricted cone NAT, port restricted cone NATはそれぞれfull cone NATに比べて制限が増えます。マッピングするときに、ソースのIP、ポート以外に、宛先のアドレスを覚えるのがaddress restricted cone NAT、ポート番号までも覚えるのがport restricted cone NATです。それぞれ攻略をしていきましょう。

address restricted cone NATは割りと簡単です。要は送った宛先のIPからのパケットでないと受け取りません。でも、サーバに送ってしまえば宛先はサーバになるわけでサーバからのパケットしか受けないことになります。では、αからβに送ったパケットはどうしてもNATに阻まれるはずです。でも、ここでcone NATのもう一つの特徴が際立ってきます。
cone NATの定義のひとつに、NATの外側のポートはソースアドレスになるデバイスのポートに従います。すなわち、αがport 30000をbindしてパケットを送ったとしたら、基本的には、NATもport 30000を利用するのがcone NATです。ここからが実際のNAT越えの方法をまた流れに沿って説明しましょう。

α、βそれぞれがサーバに向かってUDPパケットを投げ、サーバはそのIPアドレスとポートを観測します。そして、それをαとβに伝える。
すると、βはαのIPアドレスとポートに対して、UDPパケットを投げます。ここで重要なのは、βはbindするポートをサーバにUDPパケットを送ったときと同じポートを使います。このとき、NATはfull cone NATであれば通りますが、address restricted portやport restricted portでは、サーバとIPアドレスもポートも違う訳ですから通りません。が、β側のNATにはαのIPアドレスとポートへのパケットが記録され、βのIPアドレスとポートは維持されるわけです。ここで、αがβのIPドレスとポートに対してパケットを投げたとします、そうすると、βはさきほどのパケットはNATに阻まれたとはいえ、NATのマッピングができあがっているため、αからのパケットはβに到達します。また、このパケットでも、αはサーバへのUDPパケットを送ったポートをbindしていればNATには同様のマッピングがβのIPアドレスとポートに対してできあがるわけです。これで、αとβはそれぞれのNATを越えて通信ができるようになりました。

ここまで、cone NATの場合のNAT越えを説明してきました。実はこれはすでによくある方式として、STUNという形で標準化されています。実際はユーザ認証なども含みますが、P2Pの経路開通を行う方式はまさにいままでの説明通りです。いくつかのアプリケーションではSTUNが実装されているので参考にしてみてください。

ここからが、難しく解決できない問題に対して考えて行く形になります。
Symmetric NATです。これはcone NATと違い、ポート番号が再利用されません。つまり、ソースとなるデバイスでポートを固定したとしても宛先によって、別ポートが割り当てられることになります。そのため、サーバで観測したとしても、そのポートを再利用する方法がないのです。

さて、この場合、どういった方法でSymmetric NATを攻略するかを考えます。Symmetricの何がツライかというと、NATの外側のポートを観測してもそれを利用できないことです。いや、それは本当でしょうか。観測できることではなく、NATでどのポートが利用されるかと言い換えられるわけで、つまるところ、NATのポートは当たるなら予測でも良いわけです。では、予測可能か、が重要になります。Symmetric NATと言われるのもを更に分類しましょう。といっても、NATの内(ソースとなるデバイスのポートは固定)から外への通信が起こった際に、ポートがシーケンシャルに変化するか、ランダムに変化するか、です。

つまるところ、方法としては、例えばこうです。ソースとなるデバイスからサーバに対して、複数のパケットを打ちます。その際、bindするポートを固定します。そして、サーバの受けるポートはパケット数分変化させます。ここでは3発のパケットをサーバに対して投げてみます。そして、サーバからポートを観測します。このとき、ポートがどう変化するかです。例えば、+1されていく、+2されていく、-3されていく、ランダムに変化する、様々なパターンが見えるはずです。ランダムに変化する場合を除いて、NATの特性を予測するわけです。+1される場合はどうするか、もう分かりますね。NAT下のデバイスが1つしかなく、ほかの通信がないと仮定すれば、αからβへの通信を同じポートをbindした上でパケットを投げます。サーバで観測されたポート番号+1されたβはパケットを投げ込みます。そうすることで、NATのグローバル側のポートは予測され、そこにパケットを投げ込めば、おそらくは疎通するはずです。

ここからが考えどころです。実際にはNAT下のデバイスは複数あって他のUDPパケットが送られてポート番号はずれるかもしれません。もしくは、同じbindしたポートから通信しても、宛先のアドレスが違えば外側のポートが変わるかもしれません。前者はリトライでカバーするか、複数のポートをつかってα、βから送ってどれかが疎通したらその後はそれを使うかなどいくつかの方法が考えられますね。宛先のアドレスが違えば届かないことを考えると、サーバも複数のIPアドレスを持ち、それぞれに対しておくったときのポート変化も観測する必要があります。このあたりから観測の方法がどんどんと複雑化していくため、これに対して、どう対処していくかがNAT越えの最大の難所です。いままで、cone NAT, SymmetricNATという類型で説明してきましたが、昨今、この類型に意味がないと言われる所以がこのあたりから始まります。(とはいえ、NAT Traversalという技術を理解するにはこの類型も私は有用だと思っています。)

とはいえ、越えられないひとつに、ランダムにポートが割り当てられるSymmetric NATは対処のしようがなさそうです。この場合のみサーバーリレーを行うというのが疎通する確率を100%にする一つの方法です。そうではなく、ランダムにポートを開けるNATに対するポート予測方法をなんらか確立できる手法もあるかもしれません。もし、すでに公知だよという場合はぜひ情報へのポインタを教えてください。

ここまでで、UDP hole punchingのなんとなくの基本を学んできました。cone NATへの対処はSTUNの仕様、規格を読んでみてください。たぶん、この文章よりはよっぽど分かりやすいです、ここまで読んでいただく方にそう言うのもアレですが。また、実際、Symmetric NATの場合の方法としては、 https://tools.ietf.org/html/draft-takeda-symmetric-nat-traversal-00 を読むのがおそらく一番詳しく丁寧です。そういった意味でも、ここでの解説は初歩の初歩です、じゃあどう実装するんだというのから、サーバで何を観測するのか、そもそもNATが複数段あったらこの手法は果たして通用するのか。観測を複雑にしていけばいくほど、開通までの時間がかかります、それは本当にターゲットのアプリケーションにとって許容できるのか。Large Scale NATではこの穴開けが通用するのか、キープアライブはどうするか、セキュリティ的に必要な相手のパケットだけを見分けられるのか、認証はどうするか等、現実世界では、この抜け穴のような手法に対しての課題は山盛りです。そんなところの実地調査として、UDP hole punching自体はあまり表に見えない手法ではありますが意外なところで結構導入されていたりしますので、とあるゲーム機なんかで、P2Pで対戦通信しているな?とおもったときに、パケットキャプチャしてみるのもオススメです。

さて、最後に、いままでは、UDP hole punchingをベースに説明をしてきましたが、ではTCPの場合はどうなるでしょうか。まず、TCPにはcone NATという概念はまずあり得ません。それは、宛先との通信はステートをもって(つまりシーケンス番号を使って)通信されるため、宛先を限定しないポートマッピングというのはほぼ意味がありませんし、ほかのパケットを転送するというのはただのセキュリティホールです。なので、通常は、上記類型で言えば、Symmetric NATしか存在しません。また、さらに面倒くさいところが、TCPの通信シーケンスの始め方です。接続元はSYNパケットを送り、接続先はSYN、ACKをセットにして送ります、そして、接続元からACKが送られて、はじめてTCPでの通信は始まります。つまり、UDP hole punchingのように最初の一発のパケットはなかったことにするわけにはいきません。あくまで、SYNパケットは両者に届かないわけにはいかないのです。この通信開始の流れを3-handshakeと呼びます。

でも、それでもTCPでNATを越たい、という人にはヒントはあります。まず、TCPでの仕様では、3-handshakeだけでなく、4-handshakeでも問題なく通信は開始できます。つまり、αからSYN、βからもSYN、その後、双方からACKが送信されるようなケースです。ここから見えてくるのは、要はSYNをUDP hole punchingの最初のパケットとしてつかって複数送ったとしてもひとつだけ届けば通信は確立されるということです。これは、どこかで少し話題になりましたが、TCPの仕様に実際、TCP Simultaneous Openという名前で記載があります。ここまでくればあとの考えは分かりますね。これで、Symmetric NATと同じ手法でNAT越えはおそらく可能になります(これは私は実際に実装したことはないので、たぶん)

さて、というわけで……。ざっと、通常言われるNAT Traversalの手法の入り口を紹介してきました。STUN以上のもので簡単な実装を見られるものはいくつかあります。また、Symmetric NATへの対処の実装も、いくつかGitHubを漁ればでてきます。 https://github.com/zerotier/ZeroTierOne など。

とはいえ、NATの越方は様々、この上でどういう通信をするかも様々です。UDPでしか通信できない経路ではアプリケーションでは使い勝手が悪い場合もあります。そういう場合には、TCP over UDPを実装してあげてVPN的に使うのもよいかもしれません(OpenVPNが使っているようなTUN/TAPデバイスという仮想トンネルデバイスはLinuxではよく使われますね)また、NATに関わらず通信するためのサーバリレーの方法としてはメジャーなのがTURNを使った実装でしょうか。越えられなかったらTURNをつかってサーバリレーをするというところまでフローを実装するとより使いやすくなります。また、今回は出てきませんでしたが、ルータに実装されているUPnP実装では静的なポートマッピングが可能なものが多く存在します。UDP hole punchingとは別の方法で、NATの外→内の通信を可能にできる方法なので、こちらも組み合わせるとより多くのNATに対応できるはずです。でも、この場合も複数段NATがある場合はじゃあどうする、みたいなことがなかなかに難しいポイントです。まずは、ローカルネットワークとグローバルネットワークを見分けるような方法を実装しなければなりません。方法はいくつかありそうですが、ICMPでTTLを変化させて使うとできそうな気もします。要はtracerouteですね。

昨今、IPv6も徐々に普及し始め、NAT Traversalは不要になるかと思っていましたが、NATはIPマスカレードではなく、1:1のアドレス変換として生き残る雰囲気も少しだけ感じます。そんな中で、意外にNAT Traversalは必要とされる技術なのかもしれません。WebRTCは特性上、P2P通信ができれば好ましく、NAT TraversalもSTUNなどを使って行われるケースもあるようです、同時にTURNも使われる例の最たるものとしても。

今後どうなっていくか

さて、最後に、将来はというところを少し。これまでは、UDP/TCPの話をしてきました、それ以外もあります。SCTPが一番最有力なのではないかと思います。現状、メジャーではないものの、すでに実装はLinuxではしっかり入っていますし、途中経路のルータさえしっかり対応すれば(これが一番大変なんですが)、様々な課題を解決しています。ここから、SCTPを解説しはじめると、この記事はいつになっても終わらない形になってしまいますので、NAT Traversalにまつわるアレコレはここまでで一度締めさせていただきます。年末年始の空いた時間でSCTPを調べていただくときっと楽しいのではと思ったりしています。

最後に

この記事が果たしてアドベントカレンダーの最後に相応しかったかどうかはよくわかりませんが、NAT Traversalという古くからあるけれどマイナーな、でも、結構有用だと思っている技術の導入となる方がいたりすると幸いです。
途中から図がなくなったのは気力が間に合わなかったので、後で追加できたらしたいなと思っています。