バイクジムカーナのためのシステム開発:かわっちタイマーのプロジェクトオーバービュー

Posted on Posted in 趣味的なIT・ネットの話題

 

 かわっちタイマーはバイクジムカーナ向け、タイム計測システムです。この文書はかわっちタイマーの開発プロジェクトの概要のノートです。

 

  1. 開発動機
     全国各地でバイクジムカーナの練習会が開催されています。ジムカーナはタイムアタック競技であり、その練習でも、コースのクリアタイムを計測して、自己の実力を把握したり、様々な走り方によって同タイムが変化するかをモニタリングする必要があります。
     多くの練習会では主催者等がお手製のタイム計測システムを使用して、計測を行っています。ただこれらの計測システムには様々な課題があります。
  • 簡易なシステムでは、ストップウォッチのリセット等、計測状態を維持するために、計測器に人が張り付いていないといけない。
  • 計測結果の紙への転記など、結果を記録するために、計測器に人が付いていないといけない。
  • 計測結果が紙で残っている場合には、ネットにアップしたり、メール配布したりするのに向いていない。
  • 計測結果を紙に記録している場合には、記録の確認のためにはその紙を見に行かないといけない。
  • 計測器の側に人が付くとゴール時の転倒事故等でその人に危害が及ぶ可能性がある。
  • 簡易なシステムでは、同時に1人分の計測しかできないので、コースで複数名が時間差でスタートすることができない。
  • 簡易なシステムでは音や光によるシグナルスタート機能が無い。シグナルスタート機能を備えている場合でも、シグナルスタート機能をスタートさせるために、計測器に人が付いていないといけない。
  • 簡易なシステムではシグナルスタート時のレスポンスタイム(グリーン点灯時から光電センサ通過までの時間)が計測できない。
  • PCのモニターをシグナル表示に使用しているシステムでは、直射日光下では視認性が悪い。
  • PCを使用したシステムでは、終日使用するためには発電機等で電源を準備する必要がある。
  • PCを使用したシステムでは、マルチタスクOSの特性上、100ms以上の誤差が出ることがある。
  • 多くのシステムでは光電スイッチにリレー式を使用しているが、消費電力が大きいのと、100ms近い遅延が生じることがある。
  • 簡易なシステムやPCを使用したシステムでは、防水性がないので、雨天では使用できないか、テント等を用意する必要がある。

 これらの課題を一挙解決すべく、以下の機能仕様を備えたシステムを構築します(しました)。

  1. 実装仕様
    1. ハードウェア
  • 組込用コンピュータボードであるRaspberryPiを使用する。
  • トランジスタ接点型の光電スイッチを使用する。
  • ハイパワーLEDをLEDドライバモジュールを介して駆動する。
  • 表示器として反射型モノクロ液晶を使用する。
  • 圧電スピーカーを昇圧駆動する。
  • 筐体・スイッチ・コネクタ全てにIP57・59以上の防護等級のあるものを使用する。
  • スマートフォン無しでも最低限のコントロールができるように、プッシュスイッチを2個設置する。
  • 光電スイッチは遅延防止のため有線接続とする。屋外での使用に備えて耐久性の高いキャプタイヤケーブルを延長用に用意する。
  • 電源はUSBモバイルバッテリーを使用する。
  • LED・スピーカー・光電スイッチの電源のため、昇圧回路にて12Vを用意する。
  • RaspberryPiはUSB無線LANモジュールによりWifiアクセスポイント化する。
  • 情報の閲覧用端末はWifiアクセスポイントにアクセスできるスマートフォン、タブレット、PCを使用する。
  • USBホスト機能を有する端末にRFIDリーダーを接続する。
  • RFIDカードをライダーの人数分用意する。RFIDカードはマグネットシールを貼っておくか、ネックストラップを付けておく。
  • RTCは使用せず、タブレットから時間をもらう。
    1. ソフトウェア
  • リアルタイムLinux上で構築する。
  • IO周りはC言語で実装する。
  • 計測システムとしての機能周りはNode.jsでJavaScriptで実装する。
  • クライアントサーバー型のWebシステムとする。
  • クライアント・サーバー間の通信はSocket.ioを使って、サーバー側主導によるクライアントの画面更新を可能にする。
  • クライアント・サーバー間の通信のデータフォーマットはJSONを使う。
  • IO周りのロジックとNodeとはUNIXドメインソケットで行う。
  • クライアントサイドもJavaScript+JQueryで一定の処理を行う。
  • RFIDカードの読み取りはクライアント側のJavaScriptで行う。
  • クライアントの表示はJQueryMobileで行う。
  • Socket.io最新版への対応、マルチOS対応の必要上、クライアント側のブラウザはChromeに縛る。
  • データの永続化はDBではなく、ファイル保存で行う。
  • 開発環境はサーバーサイドで構築する。
  1. 仕様策定の理由
    1. 開発方針
       多機能化により開発工数の増加が見込まれますが、仕事も遊びも忙しいので、それほど時間は取れないことから、目的を達成しうる範囲で、プロセスを極力合理化することにします。また目的達成に必要以上の高性能は、技術的興味があっても、割愛します。
    2. 回路の試作
      回路の設計は以下の手順で行っています。Eagleで図面を引いたら格好いいなとは思いましたが、自分以外が再製作する可能性はまず無いので余計な見栄は捨てることにしました。
    • 文献やGoogleでの情報収集
    • 紙に手で回路図を書く
    • ブレッドボードで試作して動作検証、定数を工夫する
    • 最終版を紙に手で回路図を書く
    1. リアルタイム性の確保
       時間計測のためのシステムなので、システムのリアルタイム性は非常に重要です。ただあくまでも人間が認識できる範囲のリアルタイム性があればよく、マイクロ秒オーダでのリアルタイム性は不要と判断しました。
       JAGE(http://jage.jpn.org)のここ数年来のリザルトを見ると、A級のトップタイム争いでも、10分の1秒以上のタイム差で順位が分かれていることが通常です。10分の1秒単位で順位が分かれるのが「時々」、100分の1秒単位で順位が分かれるのが「稀に」といった程度です。「ごく稀」に1000分の1秒単位で順位が分かれているケースも見受けられますが、PC型の計測システムでは、誤差の範囲と言わざるを得ないと思います。使用する光電スイッチ(オムロン E3JKシリーズhttp://www.fa.omron.co.jp/products/family/3179/specification.html)の応答時間の保証値が「1ms以下」です。オムロンに電話して、応答時間のばらつきについてデータがあるかどうか聞いたのですが「答えられない」とのことでした(データはもってそうな感じでしたが)。浜松ホトニクスの素子のデータシートでは、応答周波数の10分の1程度がパルスの周期ですので、仮に同様の検出ロジックを採っているとすると、パルスの取り出しのタイミングずれにより、応答周波数の10分の1までのずれが生じる可能性があると推定しました。
       そこで「100us単位ではズレがあるけれども、1ms単位では正確である」ことを最大目標としてシステム設計を考えることとしました。なおリレー接点の場合は20ms以下ですから、仮に10分の1のばらつきがでると、2msのばらつきが出ます。リレー接点の光電スイッチを採用したシステムで1ms違いで勝負が決まったとしても、その結果が真実である可能性はおよそ半分しかありません。
       時間を測定するCPUは、マイコンを使用して外部割り込みでタイマの値を採るのが一番正確です。これをやると恐らく1usとか100ns単位での正確性で、時間を計測できると思います。ただシステムの目的からすると、そこまでの正確性は必要無いと判断しました。ただRaspbianでコンテクストスイッチのテストプログラムを書いて試してみると、10msぐらいは平気でばらつきが出ます。Raspbianはconfig_preemptがもともと有効化済みです。これではconfig_preemptの意味が無いように見えたのですが、CPUに可負荷をかけた状態では、優先度の高いタスクが遅延しにくかったので、それなりに意味はあるのでしょう。
       config_preempt_rtを適用したカーネルのテストリポート(http://www.emlid.com/raspberry-pi-real-time-kernel-available-for-download/)を見ると、config_preemptでは最大遅延が1200us、config_preempt_rtで最大遅延が77usとありました。自分で実験してみるべきなのですが、カーネル環境の準備に時間がかかりそうなので、見合わせました。実際に77usなのであれば、最大遅延と遅延ゼロとの間でぶれたとしても、1ms単位での数値がぶれるの確率が10分の1以下になります。
       このカーネルレベルでの誤差(+-0.05ms)と光電スイッチの誤差(+-0.05ms)が混合すると最大で0.2msの誤差があることになりますが、これだと1ms単位の計測値が正しい可能性は95%ぐらいしかありません(10.0009秒は半分ぐらいの確率で10.000秒と計測され、半分ぐらいの確率で10.001秒と計測される)。ただ10ms単位での数値が正しい確率は99.5%ぐらいあるわけですので、ジムカーナでの計測目的であれば、実用性があるといえるでしょう。1ms単位で勝負が分かれた場合には、2ms以上の時間差があればそれによって勝負を判定、1ms違いの場合には、誤差を考慮して同着と扱う必要があります。
       これ以上の正確性を求めると、光電スイッチにレーザーファイバセンサを使用する必要がありますが、高価ですし、会場での設置も困難です。またマイコンを使用すると実装工数、開発工数が増えます。マイコンだけで開発が完結するのであればいいのですが、Wifiアクセスポイント化をマイコンだけでやるのは現実的ではありません。 Linuxであればミドルウェア、開発情報が豊富なので、開発時間を大幅に短縮できます。そのためRaspberryPi+Linuxで達成可能な範囲のリアルタイム性にて満足することとします。
    2. マイコンボード
       Linuxが動く組込ボードはいくらでもあるのですが、開発を楽に進めるという観点からは、開発情報が多いボードを使うのが楽です。piは流通量が圧倒的に多く、開発情報にあふれています。Beagle Boardも選択肢に上がったのですが、音声出力がHDMIからしか取れないので、今回のプロジェクトには向いていないと判断しました(結局音声出力は使いませんでしたが)。
    3. 光電スイッチ
       リレー式の光電スイッチはディレイタイマが設定できるなど高機能ですが、消費電力が大きく、遅延も大きいです。また開閉の耐久上限回数も少ないです。ストップウォッチのスイッチを代替するだけであればリレー式しか使いようがないですが、組込で利用するのであればメリットがありません。トランジスタ接点方式のものを利用します。部品点数がスイッチ一つあたり3点増えますが(3つとも抵抗)、それほど面倒では無いので、受容可能なデメリットとします。
       なおジムカーナ界では通過検出用センサーを「光電管」と呼んでいますが、実際に使用されているセンサーは「光電管」では無く、産業上も「光電管」と呼ぶことはほとんど無いようですから、本項では本来の名称通り、「光電スイッチ」と呼ぶことにします。
       光電スイッチをXBee等でワイヤレス化すると非常に便利なのですが、遅延が100ms単位で発生するので計測システムには向かないと判断し、有線で使用する事とします。将来的には、センサユニットにGPSを搭載するとか、NTPで連携するなどして、時刻を高精度で同期させ、センサをユニット化してワイヤレス化することを目指したいです。
    4. 電源
       バッテリー駆動とするわけですが、乾電池はランニングコストが高く、シールドバッテリーは扱いに難があります。最近、スマートフォン向けのUSBモバイルバッテリーが低価格化しており、5V/2A程度の出力が取れます。バッテリー残量メータも付いており、開発するシステムのシンプル化にも貢献できることから、これを採用します。
       もっともLEDドライバや光電スイッチの電源には12V以上が必要です。そこで昇圧回路を使用して、12Vを用意します。ここでもストリナさんのモジュールを使用して、設計・実装を簡素化し、システム自体の信頼性を上げます。
    5. 音声出力について
       最初はタイムの音声読み上げを行わせたり、リアルなシグナル音を鳴らしたり、レコード更新でファンファーレを鳴らしたりするために、RaspberryPiの音声出力を使用する予定でした。しかし以下の理由により頓挫しました。

      1. RaspberryPiの音声出力はPWM出力をRCフィルタに通したものなのでそのままでは出力インピーダンスが高すぎて使えない。
      2. オペアンプを使用してインピーダンスを改善することもできるが、部品の実装点数が増える。
      3. 音声出力で実用性のある音量を出すためにはパワーアンプが必要だが、5Vで10WクラスのD級アンプモジュールが無い。仮にあったとしても、USBモバイルバッテリーの出力可能電流が2Aまでなので、最大出力ではリミッターがかかってシステム全体が落ちる。
      4. 防水性のあるスピーカーの入手が難しい。典型的なのはトランペット型だがシステム全体がかさばるようになる。

 そこで防犯ブザーの回路を応用して、昇圧回路と圧電ブザーを組み合わせて、音量を出す方法を採用しました。これであれば回路構成がシンプルで済み、音量の割には省電力です。音声パルスはWiringPiの矩形波生成機能を使いました。

 

    1. ライト
       スタートシグナル用のライトは電球を使うのが簡易ですが、バッテリー駆動のため、電力を無駄遣いできません。そこでハイパワーLEDを使用します。ハイパワーLEDは発光点が小さいので、本来はシグナルランプに向いていませんが、LED用のマーカーレンズが販売されていますので、これを利用して、視認性を挙げます。マーカーレンズとは、トラックの電飾に使用されているレンズです。
       回路のシンプル化、実装の短時間化のため、LEDドライバは既成のモジュールを使用します。
    2. 表示器
       システムの各種状態はスマートフォンから確認する予定ですが、一切表示器が付いていないと、状態確認で毎度スマホを接続しないといけなくなります。スマホが使い無いときはタイムがわかりませんし、またデバッグも大変です。そこで小さくてもLCDを付けて、各種状態を表示させるようにします。
       直射日光下で使用するものですので、モノクロの反射型液晶を使用します。RaspberryPiのバスを有効活用する観点から、ストリナのI2C接続液晶を選択しました。
    3. スイッチ
       チャタリングを防止する必要があるのでRCフィルタを介して接続します。
    4. 防水性
       屋外で使用されるものですが、ジムカーナ競技は雨でも中止しませんので、防水性が必須です。センサーはもともと防水性を備えていますので、ケース、コネクタ、スイッチに防護等級が保証されているものを使用します。
    5. アクセス端末
       ジムカーナの練習会に参加しているメンバーが何時でも情報を参照できるようにするためには、誰でも持っている端末でのアクセスを可能にする必要があります。近時はガラケーの利用率が下がってきており、また、ガラケーではサーバー側からの情報更新が困難なので、スマートフォンの利用を前提に設計します。
    6. アクセスポイント
       iPhoneはピアツーピアでのWifi通信に対応していませんので、通信にあたっては、システム側がアクセスポイントとなる必要があります。Wifiアダプタ選びがシビアですが、Linuxであれば比較的簡単にアクセスポイント化できますのでこれを実施します。
    7. RFID
       スタートするライダーを識別する方法として考えられたのは以下の方法です。

      1. ライダーがタッチパネルで入力
         スマートフォンを使用するので一番簡易な方法でしたが、手袋をしたままでは操作ができないことから採用できませんでした。昔は感圧タッチパネルの製品がありましたが、今ではほとんどが静電パネルとなり、手袋をしたままでは反応しませんでした。ただ後になって、ある程度の大きさのタッチペンを使うと、手袋をしたままでも操作できることがわかりましたので、選択肢の一つに復帰しました。
      2. iBeacon
         iPhoneと親和性が高く、また、検出範囲が広い(5mぐらいでも検知する)ので、ライダーに固有のビーコンを持たせておけば、ライダーを特定できます。しかし指向性が無いので、スタートとゴールが近くにある場合には、スタートにいるライダーとゴールにいるライダーの識別ができません。またスタートの列に並んでいるライダーとスタート直前のライダーを間違える可能性もあります。そのため「なんとなくかっこいい」技術ではあるものの、ライダーの識別には採用できませんでした。
      3. 光学ナンバー読み取り
         ショッピングモールなどでよくあるナンバー読み取りをOpenCVとOCRを使って実装することが考えられました。ただカメラの精度や読み取り精度からナンバー中の4桁の大きな数字しか読めない可能性が高いのですが、そうするとナンバーが被る場合には区別ができません。またキッズレーサーなどでそもそもナンバーが付いていない場合があり、ライダーの識別には採用できませんでした。
      4. キーボードで入力
         おしゃれな方法では無いですが、防水型のキーボードを用意しておいて、自分でIDを入力させる方法が考えられました。ただどうにもかっこわるいのと、操作誤りに対応するために画面が必要、手袋をしたままの指で押そうとすると安定したマウントが必要、大きなキーが必要ということで採用しませんでした。
      5. バーコードリーダ
         ライダーにバーコードを配布し、スタート地点に据え置いているバーコードリーダでライダーが自分でコードを読み取る方法が考えられました。ワイヤレスリーダーを使うとスタート周りの設置システムの無線化も同時に達成できます。ただし直射日光に弱く、明るい環境下では読み取り精度が低下します。ワイヤレスで且つ直射日光下で使用できるリーダーは、市販されているものの、価格が10万円以上するので、採用は見送りました。
      6. RFID
         リーダーとカードで初期投資が若干必要なものの、手袋をしているライダーが自分だけでスタート時の読み取り動作が行えるという条件を満たしました。投資金額もカードも含めて1万円以下で済むため、採用が比較的容易でした。RFIDカードは、裏にマグネットシールを貼っておけば、タンクに貼り付けておき、スタートの時だけ読み取りをさせるという使い方ができます。

 以上の検討の結果、RFIDリーダを採用しました。製品はストリナさんで扱っているOLIMEX社のものです。USB HIDとして動作するので、特別なドライバが必要ありません。

    1. RFIDリーダの接続方法
       RFIDリーターはUSB HIDなので、RaspberryPiに直接接続させられます。しかしRaspberryPiの2つしか無いUSBポートを占有させるのは痛いです。またキーボード入力をパイプでNodeに送ってみたのですが、レスポンスが今ひとつでした。またUSBの無線化は金がかかるので、有線にならざるを得ないのですが、そうするとシステムの設置場所とライダーのスタート位置を有線でつながないといけません。
       そこでUSBホスト機能のあるタブレットをスタート地点に設置して、このホストにRFIDリーダーを接続することにしました。この場合の問題はタブレットのUSBポートをRFIDリーダーが占有してしまうと、外部電源を接続したまま使用するという点です。競技でのシステム使用は、朝から夕方まで、画面輝度最大で放置する必要があるので、そのままではバッテリーが持ちません。そこでここは充電ジャックがUSBポートとは別になっている変わった仕様の中華パッドを使用して問題に対応する事にしました。
    2. RTC
       RaspberryPiはRTCを内蔵していません。そのため電源投入毎に毎回UNIXタイムが0からスタートします。一方、走行タイムの保存にあたっては、ここのタイムの測定日時を記録したいです。またmakeを実行するにあたって時計が狂っていると更新したファイルの更新時刻が無茶苦茶になるので、ソースを更新してもコンパイルが正常実行されなくなります。ネットワークに常時接続されているのであればNTPで同期すればよいのですが、野外のインターネット接続が無い場所で使用するのでNTPは使えません。RTCを実装すれば話は簡単なのですが、実装工数が増えます。そこでですが、今回のシステムはクライアントからアクセスすることが通常であることに着目して、アクセスしたクライアントから時刻をもらうことにします。具体的には、起動後初回のアクセスであるかどうかを判定し、初回アクセスであれば、クライアント側から現在時刻を送らせて、それをサーバーが取得して、システム時刻に設定します。
    3. リアルタイムOS
       Xenomaiを使用してリアルタイムOSとLinuxOSを両立させるのがレスポンス的には最適解でしたが、実装に時間がかかりそうなので見合わせました。Xenomaiは資料が少ないですし、RaspberryPiで動作するリアルタイムOSも見あたりませんでした。
       そこでconfig_preempt_rtを使用してリアルタイム化することにしましたが、カーネルコンパイルやカーネル入れ替えはやったことがありません。そこで当初ははconfig_preemptの実装を前提にしたnice値の設定のみを行い、追ってconfig_preempt_rtカーネルを実装させます。
    4. 開発言語
       単一言語でシステム全体を開発できるのが理想ですが、IO周りのレスポンス確保はC以外では難しい一方で、WebシステムをCで作るのも現実的では無いことから、C言語プラス1言語にて開発する事としました。
       Webシステムでサーバサイドからクライアントの表示を更新するためにはSocket.IOしか事実上選択肢がありません。Socket.IOのクライアントサイドはJavaScriptで実装するしか選択肢が無いです。Socket.IOのサーバサイドの実装はいくつかありますが、開発言語を増やさないという観点からいくと、Node.jsを採用するしかこれも選択肢がありません。ここからIO周りがC言語、Web周りがJavaScriptという方針が確定しました。
    5. Webシステム
       練習会の参加者のスマホのOSがバラバラなので、ネイティブソフトを開発するのは開発負荷が重いです。JQueryMobileでそれっぽい見栄えにはできるので、クラサバ型のWebシステムとして構築することにします。
       Webシステムとして構築したときの問題が、サーバー側からクライアントの表示を書き換えることができないという点です。システム的には、ライダーがゴールしたときには、特段の操作を要すること無く、ラップタイムが自動的に画面上に表示されることが必須です。レガシーが方法としては、クライアントからポーリングさせることが考えられますが、ゴール後即座に表示を更新しようとすると、更新頻度が多くなり、クライアント側の消費電力が増えますし、サーバー側のリソースも消費してしまいます。そこでWebsocketを使用して、サーバー側からクライアントにメッセージを送信することができるようにし、クライアント側のスクリプトが、サーバー側のメッセージに応じて、クライアント側の情報を書き換える方式を採ることとしました。
       開発言語がJavaScriptなので、メッセージフォーマットは一番簡単そうなJSONにしました。
    6. プロセス間通信
       C言語のプロセスとNodeが同時に起動しますので、これらが相互にメッセージを送信しあえる必要があります。
       一番簡単なのはパイプですが、一方向ですし、送り手が複数になることができません。共有メモリはポーリング的な使用が必要になりますから、休止時間が多いシステムには向いていません。シグナル、セマフォとキュー(FIFO)は送れる情報が限られるので、今回のデータには向いていませんし、Nodeでの使用はモジュール追加が必要です。そこでUNIXドメインソケットを使用する事にしました。UNIXドメインソケットであれば、Nodeはデフオルトで対応していて、特にモジュールの追加は必要ありません。将来的な拡張を想定してインターネットソケットを使用する事も考えましたが、現状ではセンサとサーバーを別のマシンでホストすることは考えにくく、オーバーヘッドを増やす必要が無いので、UNIXドメインソケットにしました。
    7. クライアントサイドの処理
       サーバーサイドのリソースが制限されているので、クライアントサイドでも可能な処理を行わせる方針とします。というかサーバーよりクライアント側の方が高性能なので、やれることはやらせないと損と考えることにしました。
    8. 画面デザイン
       本当は格好いいデザインがいいのですが、開発者の能力とリソースがそれを許しません。そこでここは妥協してJQueryMobileで提供されているデフォルトのデザインをほぼそのまま使用する事にします。
    9. クライアント要件
       Websocketに対応していて、主要端末(Windows/OSX/Android/IOS)全てに対応しているブラウザがChromeしかないので、クライアントはChrome必須とします。電波が入っていればその場でインストールしてもらうことも可能なので、会場での使用も特に問題無いと判断しました
    10. データ永続化
       走行タイムを後からでも参照したいので、データの永続化方法を検討する必要があります。またいつシステムがストールするかもわかりませんので、ストール直前までのデータを参照できる必要もあります。Nodeを使っている場合にはMongdbのようなNoSQLを使用するケースが多いと思いますが、Mongoのデータを参照するためには、アクセスするプログラムが必要ですので、SDカードからデータをサルベージするニーズには向いていません。そこでDBは使用せずに、メモリデータを随時ファイル化して保存することにしました。
    11. セキュリティ
       最初はセキュリティゼロの状態です。悪意ある参加者が全てのレコードを削除することも可能です。利用者にアクセス方法を開示する前に、一定の対応が必要になります。
  1. (続く?)

Facebooktwittergoogle_pluspinterestlinkedinmail
納得したらすぐにシェア!