センサーログを設計する — iPhone-G-Sensor の二層アーキテクチャ
iPhone は加速度計・ジャイロスコープ・磁力計・GPS という、観測機器として十分な精度を持つセンサー群を内蔵しています。これらを生のサンプリング・レートで記録し、後から可視化・分析する装置として使うと、自転車の振動特性、徒歩の歩調、車の運転傾向 ── あらゆる移動体の動的特性が定量データとして得られます。
iPhone-G-Sensor2 は、この観測装置を iPhone (記録) + デスクトップ (閲覧) という二層構成で実装した個人用ツールです。本サイトの既掲記事 ツールとオーケストレーターを分ける で論じた 責務分離 の原則を、計測と可視化という分野で実装した事例研究として、設計判断をはっきり言葉にしてみます。
設計判断 1 ── 記録と閲覧を別レイヤーに分離する
最初の判断は、ロガーと閲覧器を 同じデバイスで動かさない ことでした。
iPhone を観測装置として使い、その場で時系列グラフや軌跡を表示する設計も技術的には可能です。けれども、
- iPhone の画面は小さく、複数センサーの時系列を同時に俯瞰しにくい
- 観測中の電源消費は最小化したい (記録に集中)
- 過去ログの比較・検索・分析は大画面のほうが圧倒的に速い
という三つの摩擦が、記録と閲覧を統合する設計には潜在しています。
iPhone-G-Sensor は二層アーキテクチャを採用しました ──
- 記録層 (iPhone, Pythonista3): センサー API からの取得 + JSON 形式での書き出し
- 閲覧層 (デスクトップ, PySide6): JSON 読み込み + 時系列グラフ + 地図軌跡
両層の 境界はファイル形式 (JSON) が引いています。iPhone 側は記録に専念、デスクトップ側は閲覧・分析に専念、と責務が明確に分かれます。本サイトの既掲記事 記録を標準化する で論じた 段階間の取り決めとしての標準形式 が、二層を接続する役割を担っています。
設計判断 2 ── iPhone 側の実装基盤に Pythonista3 を選ぶ
iPhone で自前のコードを動かす選択肢は限られています:
- Swift / Objective-C によるネイティブアプリ ── App Store 配布、開発者登録、Xcode 必須、変更の度に再ビルド・再配布
- Pythonista 3 ── 他社製の iOS 向け Python 開発環境。標準モジュール + iOS 専用 API (motion / location など) が利用可能3
- PWA (Progressive Web App) ── Web ブラウザ経由、センサー API へのアクセスは制限的
- React Native / Flutter ── 多言語、依存が重い
個人観測ツールのように、頻繁に修正してすぐ走らせる 用途では、Pythonista3 が圧倒的に低摩擦です。Python スクリプト 1 本を iPhone に転送して実行するだけで、新しいセンシング処理が試せます。Xcode のビルド・サイン・転送サイクルを回す必要はありません。
これは本サイトの既掲記事 思考と作業を分離する で論じた 思考の連続性を妨げない道具設計 の判断です。観測コードを書きながら頻繁に試行錯誤する場面で、ネイティブ開発のサイクル時間 (5–10 分) は思考を中断させますが、Pythonista の即時実行 (秒) は思考の流れに沿って動かせる、という違いがあります。
設計判断 3 ── センサーデータは 10 Hz で JSON にサンプリング
サンプリングレートと出力形式の選択は、保存可能な情報量と処理しやすさのトレードオフです。
| 候補 | サンプリング | 形式 | 評価 |
|---|---|---|---|
| 10 Hz JSON (採用) | 100 ms 周期 | テキスト、可読 | 自転車・徒歩・車の動的特性は十分捕捉、ログサイズ管理可能 |
| 100 Hz バイナリ | 10 ms 周期 | protobuf 等のバイナリ形式 | 高速変動に対応するが、解析・可読性が下がる |
| 1 Hz JSON | 1 秒周期 | テキスト | 軌跡記録には十分だが、振動・姿勢の動的特性は失われる |
10 Hz JSON は、人間の動作・乗り物の動的特性を捉える解像度として十分でありながら、テキストベースで grep / diff / 可視化の標準ツールがそのまま使える、という両立点です。本サイトの既掲記事 腐らない資産を設計する で論じた テキスト最優先・開放形式 の原則が、観測データにもそのまま適用されています。
JSON ログは数年後に開いても、構造が変わっても、grep / jq / Python / 各種言語ライブラリで読めます。バイナリ形式に閉じ込めれば、観測時点では効率的でも、10 年後の再分析時に「読み出す道具」が必要になり、耐久性が低下します。
設計判断 4 ── デッドレコニングを記録層で実装する
GPS は屋外では精度十分ですが、トンネル内、地下、ビル谷、屋内では取得できません。連続的な軌跡記録には、GPS 喪失時の 推測航法 (dead reckoning) が必要になります。
実装方針には三つの選択肢があります:
- 記録時に DR を計算してログに含める (採用)
- 記録時は生センサーデータのみ、可視化時に DR を計算
- 専用のカルマンフィルタ・ライブラリ (例: KalmanFilter, FilterPy) に依存
採用したのは方針 1 です ── IMU (加速度計 + ジャイロ) の積分による位置推定を、記録層で実時間計算してログに書き出す。理由は:
- 遅延ゼロで現在位置をその場で確認できる (iPhone 画面で OpenStreetMap 上にリアルタイム表示)
- ログには GPS 軌跡と DR 軌跡が 両方記録される ── 後から GPS 精度の検証や、DR の精度評価ができる
- デスクトップ閲覧器は計算済みの軌跡を読むだけ、責務が記録層と閲覧層で明確に分かれる
トレードオフとして、DR ロジックを記録層に組み込むと、後でアルゴリズムを差し替えるのが面倒になります ── 既存ログには古い DR 結果が固定で記録されるため。けれども生センサーデータも併記されているので、必要なら閲覧層で再計算もできる、という退避路は確保されています。
設計判断 5 ── 国土地理院地図に Leaflet.js で乗る
デスクトップ閲覧器の地図表示は次のように構成されています:
- 地図エンジン: Leaflet.js (オープンソースの軽量な地図ライブラリ)
- タイル: 国土地理院の標準地図タイル (無料、商用利用可、API キー不要)4
- PySide6 への組込み: QWebEngineView 経由で Leaflet ベースの HTML を表示
この組合せは「自分で書かない設計」を最大化したものです:
- 地図描画 → Leaflet.js (10 年以上の保守実績、業界標準)
- タイル供給 → 国土地理院 (囲い込みに加担しない、無料、長期安定)
- Web ↔ Qt の橋渡し → QWebEngineView (Qt の標準部品)
各要素が他者の長期保守に乗ることで、本ツール固有のコードは「PyQtGraph と Leaflet を時間軸で連動させる」接続部だけになります。 route-planner でも同じ判断構造が現れていて、地理情報の領域では国土地理院 + Leaflet + OpenStreetMap という三点の組合せがほぼ枯れた選択になっている、と整理することができるでしょう。
設計判断 6 ── 時系列グラフと地図を時間軸でリンクする
センサーデータの可視化は、時系列グラフと地図の 両方を同時に見せる ことが設計の中核です。
- 加速度・ジャイロ・速度の時系列 (PyQtGraph)
- GPS 軌跡 + DR 軌跡 (Leaflet on QWebEngineView)
- 時間軸ドラッグで両方の表示が連動
グラフ上で「ここの加速度ピークは何だったのか」と見つけたとき、地図上の対応位置に瞬時にジャンプできる。逆に地図上で「この交差点で何が起きたか」と問うとき、グラフ上の同時刻に瞬時にアクセスできる。時間軸を共通の基準軸として、空間情報と動的情報を相互参照する 画面が、観測データ分析の解像度を一段上げる、という判断です。
これは perspective-corrector の「拡大鏡」、route-planner の「区間選択」と同じ系列の 精密参照を可能にする画面上の仕掛け だといえるでしょう。本サイトの既掲記事 思考と作業を分離する で論じた、思考の側に注意を集中させる道具設計 が、データ可視化の分野で具体化されています。
設計判断 7 ── ログのファイル転送はファイルベースで割り切る
iPhone からデスクトップへのログ転送方法には複数の選択肢があります:
- ファイル転送 (Pythonista の書き出し → AirDrop / メール / iCloud Drive) ── 採用
- HTTP サーバをデスクトップ側に立てて Pythonista から POST
- iCloud Drive 同期で自動転送
- 独自の同期サーバ
iPhone-G-Sensor は最も低摩擦な ファイル転送 を採用しました。Pythonista が JSON ファイルを書き出し、AirDrop / iCloud Drive / メール添付など好きな経路でデスクトップに持っていく。デスクトップ側は そのファイルを開く だけ、サーバ起動も同期も不要。
「転送経路を発明しない」という判断が、両層の独立性を担保しています。Pythonista がいつでもファイルを書き出せ、デスクトップがいつでもファイルを読める ── 両者は AirDrop / iCloud などの第三者基盤を介して、間接的かつ非同期に繋がる、という疎結合設計です。
ここから少し、iPhone-G-Sensor を本サイトの既掲記事との接続で読み直してみたいと思います。
本ツールの設計判断は、いくつかの記事で論じた原則の 計測と可視化の分野への適用例 だといえるでしょう。
- 決定論的にではなく、相対的に で宣言した態度が、計測と可視化という分野で現れる。GPS 軌跡と DR (デッドレコニング) 軌跡を両方記録しておく 判断は、「どちらが正しいか」を事前に決め打ちにせず、後から精度を相対的に確かめなおせる余地を残すこと ── 決定論的にではなく相対的に意味を確認しなおす、という態度の即物的な現れである。記録時に DR を計算してログに含めるのも、生センサーデータを併記して「必要なら閲覧層で再計算もできる」退避路を確保しているのも、目的 (どの軌跡を信じるか) が事後的に言葉になる前提に立った設計である1
- ツールとオーケストレーターを分ける の二層分離が、記録層 (iPhone, 単一責務 = 観測) + 閲覧層 (デスクトップ, 単一責務 = 可視化) として実装される。両層の境界は JSON ファイル という段階間の取り決め ── これが二層を相対的に差し替え可能にし、同時に「記録の出力と閲覧の入力が一致しているか」の検証を機械的に成立させる
- 偉大なプログラマは優秀なプログラマのコードを利用する の再利用の原則が、Pythonista3、Leaflet.js、PyQtGraph、Qt、国土地理院のすべてを「他者の優秀なコード」として束ねる形で実装される
- 腐らない資産を設計する の テキスト最優先 + 開放形式 が、10 Hz JSON ログという形で観測データに適用される。バイナリ専用形式ではなく、10 年後も grep / jq / 各種言語で読める形式を選ぶ判断
- 思考と作業を分離する の 思考の連続性を保つ道具設計 が、Pythonista3 の即時実行サイクルと、時間軸で連動する可視化の画面に現れる
perspective-corrector と route-planner と並べると、ご自身が同じ方法論を 会議スライド × 自転車ルート × センサー観測 という三つの異なる分野で実装し続けてきたことが浮かび上がります。各分野の固有性は当然ありますが、判断の構造 ── 単一責務 / 標準形式 / 書かない設計 / 道具を媒介物として思考の延長に整える / 決定論的にではなく相対的に意味を確認しなおす ── は分野を貫いて反復しています。
オンライン講座の文脈では、この 同型の判断が分野を横断すること が教材的価値の核心です。「方法論はあらゆる分野に適用できる」と主張するだけでは説得力がありませんが、三つの分野での実装結果を事例研究として並べれば、方法論が分野を選ばないこと を読者は具体的に確認できる、と整理することができるでしょう。
参考文献
-
媒介性 / 差延 / 検証と妥当性確認 (V&V) の非対称性 の操作的な定義と理論的な扱いについては、本サイトの基礎記事 決定論的にではなく、相対的に の脚注、および著者の Zenodo プレプリント・シリーズ (レター版 DOI: 10.5281/zenodo.20096463) を参照。 ↩
-
mashi727/iPhone-G-Sensor, GitHub. https://github.com/mashi727/iPhone-G-Sensor ── 本稿で扱った Pythonista3 + PySide6 ベースのセンサーロガー + 閲覧器。 ↩
-
omz-software. Pythonista 3 — Python IDE for iOS. http://omz-software.com/pythonista/ ── iOS で動作する Python 3 の開発環境。標準モジュールに加え、
motion/location/uiといった iOS 専用 API を Python から呼び出せる。個人開発者がアプリストアの審査を経ずに iPhone でコードを動かす最も低摩擦な選択肢の一つ。 ↩ -
国土地理院. 標準地図タイル. https://maps.gsi.go.jp/development/ichiran.html ── 国土地理院が無料で公開している地図タイル。商用利用可、API キー不要、長期安定。 ↩