RoboCupJunior(以下RCJ)サッカーリーグの公式球として使われている赤外線パルスボールのセンシング方法について紹介します。
現在サッカーオープンリーグでは、公式球がオレンジ色のパッシブボールへ変更となりましたが、それ以外のサッカーリーグでは依然このボールが使用されていますので、そちらのリーグに参加される方の参考になればと思います。

以下で紹介するセンシング方法は、私がチームとして活動していた時に当時の先輩から引き継いだ技術をベースとして、様々な改良が加えられたものです。
特にパルスの同時読み込みの技術は、当時のロボットの性能を大幅に向上させました。
この記事を書くにあたって、まずは先輩方に感謝申し上げます。

ハードウェア : Hardware

IMG_0108
今回実験に使用したハードウェア

M Robotsの赤い方、Rosso(バージョン6)のセンサをお借りしました。
Yunitシリーズのボールセンサとは、マイコンとセンサ数以外の設計が完全に同一です。
  • IR reciever Vishay TSSP58038
  • Microcontroller Atmel ATmega328P-PU
  • Firmware Arduino
なぜ合併する前なのに完全な同一構成になったのかは、色々な経緯がありますが今回は触れません。

センサの選定

ボールセンサを設計する上でまず重要である、センサ素子の選定の考え方について紹介します。
恐らく、RCJに参加するチームは既に実例があるセンサを使い回す事ががほとんどだと思います。以下で説明することは、完全に理解する必要があるとは思いませんが、選定にあたってどのような観点が存在するのかは知っておいても良いと思います。

Soccer rules (2017) - RoboCupJunior
 
の21ページ“APPENDIX I: Technical Specification for pulsed Soccer Ball”には汎用的な赤外線信号受信モジュールを使ってON/OFFの時間から距離を割り出したりすることができる」みたいな説明があります。
その汎用的な赤外線信号受信モジュールとして、以下の3つのセンサが例として紹介されています。
  1. Vishay   TSOP1140
  2. Vishay   TSOP31140
  3. SHARP GP1UX511QS
要するに、これと同じようなセンサを使う事で、パルス幅による距離検出ができるという事です。
しかし実際には、パルス幅を使った距離検出を行うためにあたって、いくつか注意すべき点があります。

ルールに書かれているセンサのうち、SHARPのGP1UX511QSについては正直よく分かりませんでしたので、今回はデータシートの情報が豊富なVishayのセンサを見てみます。

センサアプリケーション

センサアプリケーションとは、いわゆる「センサの用途」です。
ユーザーはそれを見ることで、そのセンサがどんな目的で使われるかがわかります。
VishayのIRセンサ製品の主なアプリケーションとしては、主に以下の2つがあります。
  • Remote control:遠隔操作(テレビやラジコン玩具などのリモコン受信)
  • Presence/Proximity sensors:存在/接近検出(ラインセンサのような反射検出)

アプリケーション比較
センサアプリケーションの比較

2つのアプリケーションを比較してみます。
左のRemote control application(遠隔操作アプリケーション)は (IR) transmitter(送信機:RCJでは赤外線ボールがこれに該当)と、IR reciever(センサ)を用いて送受信が行われる事がわかります。

一方、Presense sensingの図はラインセンサと同じような構成になっている事がわかります。
変調された光を壁に照射し、反射してきた光を検出する事ができます。

どちらのセンサも、変調された赤外線信号を受信するセンサである事に変わりはありませんが、検出したい対象が異なるという事がわかります。
  • 遠隔操作:出力されたパルス信号をその形のまま受け取りたい
  • 物体検出:受信したパルス信号の強さを知りたい
結論としては、RCJにおいては物体検出のセンサを用いるべきです。
つまり、ルールで紹介されているTSOP1140/TSOP31140は、受信したパルス信号の強さを知りたい場合には、使えないことは無いが最適では無いという事になります。

その理由を次のセクションで説明します。

ブロックダイアグラム

Block diagram(ブロックダイアグラム)はセンサICの内部設計を簡易的に示したものです。
だいたいのIC(集積回路)のデータシートにはこれが書かれていて、ユーザーはこの図を見る事で、細かい説明文を読まなくてもICの機能を理解することができます。

センサ比較
ブロックダイアグラムの比較

先ほどの図と同じ構成で、今度はブロックダイアグラムを比較してみます。

細部が微妙に異なることがわかりますが、主に重要なのは“AGC:Automatic Gain Control”というシステムです。上の図で比較した2つのセンサを見てみると、左のTSOPシリーズにはAGCが搭載されていますが、右のTSSPシリーズには搭載されていない事が分かります。
赤外線ボールのセンシングにおいては、このAGCの有無が大きく関わってきます。

AGC:Automatic gain controlとは
Vishayの技術資料(Choosing an Infrared Receiver Based on AGC Type)に詳細が書かれています。
AGCが何のために付いていて、どんな働きをするかがわかります。

ざっくり読むと
赤外線レシーバーには、データ信号だけを受け取って、他のノイズは受け取らないような能力が必要である。

赤外線レシーバは本気出せばめちゃくちゃ距離を読めるし値段も抑えられるけど、リモコン受信機を作りたいエンジニアにとってはそれは最適ではないだろう。もしノイズの影響を受けたら、信号が破壊されてエラーが発生するから、それに伴って信号を繰り返したりしないといけなくなって、大変でしょ?

ノイズには白熱灯、ハロゲン、ネオン、蛍光灯、コンパクト蛍光灯の放射が含まれる。あと液晶ディスプレイ及びプラズマディスプレイ、そして日光なんかもそう。

という訳で、IR受信モジュールは受信信号を受け取る利得(感度みたいなもの)をコントロールするためのフィードバック回路が組まれている。これは、ノイズが検出された時にセンサの感度を下げ、データが正常に受信されているときには利得を上げる動作をする。
みたいな事が書いてあると思います。

このAGCが搭載されたセンサは、テレビのリモコンなどから発せられる、一瞬のシグナルを正確に検出できるように調整されています。
赤外線ボールから発せられる信号は、テレビのリモコンなどと比較するとはるかに強い・かつ連続したノイズのように見えます。

従って、信号用のAGCが搭載されたセンサで赤外線ボールを見ると、ほとんどの場合でゲインを下げる方向に制御が働きます。ボールを隠してもう一度戻すと検出ができたりするのは、このAGCの働きに由来するものです。

また、当然ですがAGCが搭載されたセンサは、受信する赤外線の強さによって、ゲインの強弱が変化します。
従って、AGCが組まれたセンサを使うと、ルールブックに書かれているような「ON/OFFの時間から距離を割り出す」という事ができなくなります。ゲインが常に変化する上、その状態を知る手段が無いからです。

結論としては、RCJrの赤外線ボールのセンシングにはAGCが搭載されていないセンサを使うべきです。
ルールで紹介されているTSOP1140/TSOP31140は、AGCにより常にゲインが変更されるため、受信したパルスから距離を検出するという事ができなくなります。

遮蔽:Sensor screen

前項で、適切な赤外線センサを選ぶことでボールとの距離検出が可能だと書きました。
しかし、ボール検出をするためには距離情報だけでなく角度情報も必要です。

角度情報を得る方法として一般的なのは、複数のセンサを異なる方向に配置して、それぞれのセンサの指向性を利用してボールの方向を判別する方法です。
ここで重要になるのが、センサの指向性:Directivityです。
指向性比較
指向性の比較

指向性はセンサによって特徴が異なる事がわかると思います。
左側のTSSP58038は指向性が低いため、側面や後ろ側の光も読むことがわかります。
一方、右のPL-IRM−A2121は指向性が高いため、そのままの状態でも方向検出に十分に使用可能です。

実際にA2121を使った事がありますが、ほぼセンサーカバーなしでもボールの方向検出が可能でした(ただしAGC搭載であるため距離検出の精度は低い)。

指向性が低いセンサを使う際には、センサーカバー(最初の写真でいう黒い部分)を使って指向性を高める必要があります。

この指向性をどれ位にすれば良いかを厳密に検討するのは難しいですが、私は「ボールの最大検出距離が下がらない範囲で一番絞る」のを狙って設計しました。詳細な検討には、何種類か形状をテストする必要があると思います。

センサ形状について

IRセンサ
Hefei機のIRセンサ遮蔽

自分は、Gcraud Nanoの白いロボットで始めて円形のセンサを採用してから、以降はずっと円形センサの設計を使い回しています。

円形のセンサを使う理由は、どの方向に対しても同じ計算が適用できるからです。

この、どの方向に対しても同じ特性を示すという事は、以降紹介するベクトル計算を行うにあたっては非常に重要になります。厳密には、円形センサでなくてもベクトル計算は可能ですが、円形センサを使う事でより楽に・正確にボールのセンシングが可能です。

例えばNitroのロボットはセンサがD型配置ですが、ベクトル計算によってボールセンシングを行っていると聞きました。詳細はNitroのゆーきさんが記事を書かれていたと思います。

読み込み回路

さすがにマイコンのデジタルピンで読み込むだけなので、ここは端折ります。

冒頭でSTM32(3.3V駆動)でTSSP58038(5V信号)を読み込んでいると書きましたが、デジタルピンの5Vトレラント機能を使って直接接続の回路になってます。

もし電圧を制限する必要があるのであれば、センサの出力信号が内部プルアップされていると思うので、外部に信号GND間抵抗を入れれば出力電圧にリミットをかける事ができると思います。

逆に、間違えて付けた外部プルダウン抵抗でHレベルが取れなくなる、という不具合も見たことがあります。こちらも注意が必要ですね。

ソフトウェア:Software

センシングにあたっては、これら複数のパルスセンサの出力を読み取り、それぞれのパルス幅からボールの位置を計算する事になります。
そこで、まずデジタルパルスの読み込み方法について紹介します。

パルスセンサの読み込み

ボールセンサに搭載されるパルスセンサの数は、少ないもので数個、多いもので数十個に渡ります。
センサの数が増えると、パルスの読み込みに必要なリソースは大きくなりますが、それを精度重視で配分するか、速度重視で配分するかは、プログラム次第で自由に変更可能です。

なお、読み込みにかかるリソースをどれだけ確保できるかは、マイコンの処理速度に依存します。
Arduinoはデジタルの読み込みが比較的低速であるため、以降紹介するプログラムでは、ポート操作を使ってデジタル読み込みを高速化し、読み込みのリソースを少しでも多く確保する工夫がされています。詳しくは Arduino Reference - Port Registers または Arduino 日本語リファレンス を見て下さい。

では、パルス読み込み処理について
同じ読み込みリソースでも、速度重視・精度重視の読み込み方法があると説明しました。
以下でそれぞれ解説してみます。

figure1
方法1:複数あるパルスセンサを順番に読んでいく方法

この方法を使うと、マイコンの持つ読み込みリソースは全て精度に使われます。

図において、マイコンは1周期の間に13回のデジタル読み込みを行うものとします。
実際には数百回から数千回くらい行われるので、この図はあくまでも簡略された例です。
1パルスの間に13回読み込みを行い、そのうち何回Hが入力されたかカウントしてパルス幅を計算します。
※実際のパルスセンサとは、論理が逆(実際のセンサは赤外線を受光すると出力がLOW)なので注意して下さい。

Arduinoであれば、pulseIn()という関数を使ってパルス入力をし、それをセンサの数だけ繰り返すような実装がこれに当たります。この方法だと、パルス幅の測定は非常に正確に行えますが、(センサの数×パルス幅)の分だけ時間がかかるため、センサの数が増えるとサンプリングレートが下がっていきロボットのレスポンスが低下します。

実際どれ位のサンプリングレートになるか、目安として

pulseIn関数を使う場合、833[us]周期のパルスにトリガをかける(図で言うとSensor output signalが0→1になる瞬間を検出する)まで、1周期の間待機する必要があります。この時点で、最小0〜最大833[us]の待機時間が発生します。ここでは平均の待機時間を0.5[ms]とします。

また、トリガがかかってからの読み取りには最大で833 - 346[us] ≒ 0.5[ms]とすると、1つのセンサの読み込みにかかる時間は、平均の待機時間0.5[ms] + 読み取り時間0.5[ms] = 1[ms]となります。12個のセンサで読み込むのにかかる時間は、1[ms/1センサ] × 12[センサ] = 12[ms] となります。
Arduinoで実験した場合、読み込み以外の処理に5[ms]くらいかかる事がわかったので、これを考慮すると
(0.017[s])^-1 ≒ 59[Hz] となります。

59Hzでサンプリングするというのは、ボールが有るか無いかを、1秒間に59回判断して動いている状態です。このサンプリングレートが上がれば上がるほど、応答性は良くなります。

この、59Hzという周期はそれなりに早い周期に見えます。
人間の目がおよそ60Hz近傍で映像を見ていると言われるのですが、フィールド上を移動するロボットは人間が地面を動くよりもはるかに早いスケールで動いています。

従って、59Hzはロボットにとってはかなり遅いと言えるでしょう。
センサを増やすなどして、読み込み以外の処理が増えたりした場合には、結構目に見える形でレスポンスが落ちていきます。

もう一つの読み込み方法として速度重視のものがあります。
figure2
もう一つの方法が、1周期の間に全てのセンサを同時に読み込む方法です。
私はこれをずっと採用しています。
この方法を使うと、全てのセンサを833[us](およそ1000分の1秒)以内に読み込むことができます。
読み込み以外の処理を先ほどと同じ5msで計算すると、全体で約6ms、読み取り周期は約166[Hz]まで向上します。

その代わり、センサの読み取りの解像度は、センサの数に反比例します。
つまりセンサの数が増えるほど、パルス幅の測定精度が悪くなります。

しかし実際には、赤外線ボールから発せられるパルスの強さが4段階しか無いため、パルス幅の測定精度はそこまで上げる必要がありません。読み取り周期を上げてレスポンスを向上させたほうが、競技においては有利になると思います。

なので結論としては、同時読みしつつ精度はマイコンのスペックで稼ぐのがおすすめです。
今のSTM32マイコンとTSSP58038を16個使う構成に落ち着いているのには、そういう経緯があります。

パルス幅データからボールの位置を計算する

GitHubリポジトリにプログラムを公開しました。
y6tada/RCJr_IRball


ハードウェアに依存する部分は
定義
  • #define IR_NUM 12
    パルスセンサの数
関数
  • bool getSensorPin(uint8_t pin)
    正面を0とした時計回り11までのセンサ番号を与えると、そのセンサの状態を真偽で返す。
変数
  • const uint8_t SensorPins[IR_NUM]
    正面を0として時計回り11までのセンサ番号
  • const float     unitVectorX[IR_NUM]
  • const float     unitVectorY[IR_NUM]
    12個のセンサの向いている方向のcos, sinの単位ベクトルが格納された配列
  • const float     deltaPulseWidth
    パルス幅を加算していく際の1回読み込みあたりの加算量 必要に応じて変更
になります。
プログラムの説明はさすがに大変なので、サボります。構造体やポインタなど、少し難しい書き方もありますが、そこまで読み辛いものでは無いと思われるので、良ければ読んでください。
それか、基本的には先程のパルス読み込みのフローを理解して、それを実装すれば同じものは書けると思います。同時読みさえ理解できれば、あとは普通のベクトル計算とその計算過程の管理だけです。


実験プログラムを動作させて、実際にボールの軌道をトラッキングしてみました。

ボールの追跡方法は、以下の3つを試してみました。
  1. 一番強い反応を示したセンサの方向をボールの方向とし、
    そのセンサのパルス幅をボールとの距離とする
  2. 一番強い反応を示したセンサの方向をボールの方向とし、
    円形に並べたセンサのうち反応した個数を距離とする
  3. ベクトル計算
結果は次のようになりました。
graph3
方法1:
一番強い反応を示したセンサの方向をボールの方向とし
そのセンサのパルス幅をボールとの距離とする

変数:maxSensorNumber は、最大のパルス幅を示したセンサの番号(12方位)で、これをボールの方向として計算しています。つまり、ボールセンサの数が12個であるため、12段階でしか角度を検出できないことになります。ただ、実際12段階あれば競技には十分だったりもします。

変数:maxPulseWidth は、最大のパルス幅がいくつであったかを示しています。
この方法では、最大のパルス幅が大きければボールが近いというような計算を行っています。

しかし、グラフと動画の実際のボールの動きを照らし合わせてみてみると、角度は正しいものの距離がほとんど判別できていないことがわかります。パルス幅に含まれているノイズ成分が大きいため、このままでは実用には値しません。

この方法はほぼ使われることは無いでしょう。
この代わりとして、円形センサを活用した距離判別方法があります。
graph2
方法2:
一番強い反応を示したセンサの方向をボールの方向とし、
円形に並べたセンサのうち反応した個数を距離とする

変数:activeSneosrs は、パルス幅が0以上を示すパルスセンサの数を示しています。ボールが近づいていくに連れて数が増えていき、ロボットのすぐ近くでは、遮蔽していても反射などで真後ろまで光が届くため、12個全部のセンサが反応する事があります。

この方法は、私がHefei世界大会マシンまで実際に使っていた方法です。
円形センサの大きな特徴として、この「反応個数で距離が割り出せる」事が挙げられます。

先程のセンシング方法と比較すると、動画の挙動と同じような、遠くから近くへと回ってくる軌跡を、きちんと追跡できていることがわかります。円形配置でかつ遮蔽がきちんとできているセンサであれば、この反応個数をカウントする方法で距離を割り出すことができます。

では最後に、より高度なセンシング方法としてベクトル計算を紹介します。
本当は数学的な解説もしたいのですが、余裕が無いのでごめんなさい。
graph1
方法3:ベクトル計算

12個のセンサの反応をベクトル合成した軌跡を示したものです。
詳細はプログラムを参照して下さい。

方法1と2と比較すると、角度の検出精度が大幅に上がっていることがわかります。
距離については、かなり誤差が大きくあまり当てにならない部分がありますが、角度に関しては今まで見えていなかった細かい挙動まで正確に追跡できています。

また距離についても、ベクトルの長さがボールとの距離に対応しているのが分かります。

ロボットを関数制御したいのであれば、この方法を用いて高解像度の角度情報を取得すると、うまくいくと思います。また、オープンリーグはカメラを使う事でこれと似たような高い精度のボールの位置情報を得られるため、今後はボールの位置情報から回り込みを決定するまでの部分の技術が進歩していく可能性はありますね。

さいごに

現役引退した後もJuniorのロボットを作り続けて後輩にリファレンスとして見せる、という方もいらっしゃるようですが、私はこうしてインターネットで情報公開した方が今後の為になる気がしたので、こういう事を続けています。
モノで示すのも悪くないのですが、現役でないと色々やりづらい部分もありますね。

記事の内容に関しては
本当は数式等使ってベクトル解析の議論をしたかったのですが、あくまでロボカップ「ジュニア」なので、数式使って云々はジュニア相手にやる事では無い気がしたので踏みとどまりました。
まあ本人があまり余裕無い所為なんですけどね…残念。

という訳で
最後のほうがかなり手抜きになってしまいましたが
一応今までの赤外線ボールセンサの検出の技術は、これで全公開になりました。

質問を頂ければ、それを参考に記事をバージョンアップする事はお約束します(個別の返信もなるべくします)ので、今後もコメント等よろしくお願いします。

そういえば周りが受験生に化け始めてビビってますが私も一応受験生です。
あと1年、ロボカップの活動とも両立しながら頑張っていこうと思います。皆さま今後もどうぞよろしくm(_ _)m