ArduinoでIRセンサの処理の高速化
こんにちはbee(ビー)です
今回は16個のIRセンサ(ボールセンサ)を読み取ることにめちゃくちゃ苦労したので記事にしました。前まではダイセンさんのボールセンサを4つ使ってボールを追いかけていましたが、それをうまく使いこなせず、今年の京奈滋大会ではうまくボールを追いかけることができずになかなか良い順位が取れませんでした。そこで僕はIRセンサを16方位につけることでボールの位置を正確に把握し、きれいにボールに追従できるようにしようと考えました。一応僕はarduinoを使っているのでこの記事の実験はその言語で書いてやってます。
まずボールのパルスの周期を見てみました。オシロスコープで見てみると、、、
写真の真ん中の下を見てもらうと、"250µs"と書いてありますこれはオシロの画面に見える横軸のマス目一つ分の大きさを表しています。これによると、ボールの周期は850µsです。(ボールの情報によると正確には833µsらしいです)
(あまり周波数について知らないので、時間を比較してこれから検証していきます
<実験1>
pluseInで見る!
最初に試したことはpluseInです。これはIRをデジタルで読んだ時などにボールがない時を1とすると、0になった瞬間に計測を初めて、1に戻るまでの時間を測り、その長さを数字にするといったものでした。まず一旦これを実装してみました。コードはこんな感じです。(実際のコードは長くて見にくいのでIRセンサ一個だけのverを載せます)
すると、シリアルで見たとき、なかなか値が表示されず、使い物に一切なりませんでした。一応オシロスコープで見てみると、赤色の線がプログラムが動いている時間を表していて、HIGHになっているときがボール処理をしている時間で、LOWになっている時間は関係ないので、気にしないでください。(以降、すべてHIGHになっている時がボールの処理をしている時間です)
写真の真ん中の下を見てもらうと、"5.0ms"と書いてありますこれはオシロの画面に見える横軸のマス目一つ分の大きさを表しています。そして僕はhighのときがボールの処理をしている時間にしているので、これによると、pluseInで読むと、約22.0ms(22000µs)かかるということになります。
つまり、すごく遅い!!
これじゃぁ話にならんということで、実験2に移ります
<実験2>
digitalReadで見る!
次にpluseInは誰もやっていないと先輩が教えてくれたので、digitalReadして、出てきた値をある変数に代入し、それをfor文で何回もやることによって、その変数の値の大きさによって、IRセンサからのボールの距離は遠いのか短いのかを知ることができるようにしました。
コードはこんな感じです。(実際のコードは長くて見にくいのでIRセンサ一個だけのverを載せます)
なんとかpluseInよりも速く、処理できるようにできたのですが、まだ遅い!!
オシロスコープで見ると、
この時の時間の幅は1マス25µsだから、digitalReadで、ボールを処理する時間は約75µsかかります。pulseInのときとくらべて、結構速くなった気がしますがまだまだ速くします!
そこでたどりついたのが実験3です
<実験3>
レジスタを直接操作して見る!
arduinoのレジスタっていうものを直接操作することによって、digitalReadよりも高速で処理することができるという優れものがあることを知りました。(レジスタとかについて詳しくは知りません。自分は”arduino レジスタ”とか”arduino ポート”みたいに調べて勉強しました)やってみるとはじめはよくレジスタの書き方をよく知らなかったので値がうまく出ず、128や32などが出てきましたので、まずやっててつまずいたところを書きます。
問題はこの原因ですが、下のコードを見てください
uint8_t IR_1b = PIND & _BV(7); //前
こういう風に書いていると"IR_1"の値はどうなっているかというと、
IR_1b = 1000000
という状態になっていました。ほかのコードも同じように
uint8_t IR_14b = PINC & _BV(5); //左前左
IR_14b = 00100000
見たいな感じになっていました。
なぜこうなっているかというと、上の"IR_1"でいうと、portDの7番のピンの値は1か0かを聞いていて、僕のIRセンサはボールがないとき、1になるようになっているので、このような表示になっているのだと思いました。
また、"1000000"は10進数で表すと、"128"、ほかにも"00100000"は"32"、になります8ビットの2進数で出てきた値を10進数に勝手に変換されていたのだと考えました。
そこで、その解決策としてやったのが、"ビットシフト"というものです。あまりこれについては自信がないのであまり言えないのですが、僕なりに解釈すると、データが"10000000"や"00100000"などの時にその"1"という部分を一番右に持ってきたり、左に持ってきたりと移動させることができるというのがビットシフトなんだと思っています(間違っていたらコメントなどで教えてくださるとうれしいです)
そのビットシフトをすることで、見たいピン番号のところに焦点を当てることができるらしいので、それを早速実行してみました。
今回は16個IRを見ているのを書いてあった方が見やすいと思いすべて載せました。
bool ir_1 = IR_1b >> 7;
を入れることでビットシフトしてみたい番号に焦点を合わせました。
あまりうまく説明できませんでした。すいません!!
まぁこのコードでやっとうまくいったので、
これから本題に入ります。オシロスコープで見てみると、
今回1マス25µsだから、レジスタを直接操作すると、ボールの処理にかかる時間は約25µsになりました!
今までの中で一番処理が速い!!!
ってな感じで今回の実験は終わりました。これでボールとの距離をより正確に測れるようになりました。
この記事はあくまで自分用の記録なのでこの記事を参考にしてくださる時には自己責任でお願いします。
ここまで読んでくださってありがとうございました。
初めての記事になるのでいろいろ間違っているかもしれません。おかしな点があればコメントして教えてほしいです。
素晴らしい記事ありがとうございます。
返信削除int Ball[16]; と配列式に書けばfor文で一気に処理できてプログラム一気に短くなるんじゃないかなーって思いました。
アドバイスほんとに助かります。
削除確かに配列をすると簡単にかけますね!
そうさせていただきます!
forループ内のセンサを読んでカウントしている
返信削除uint8_t IR_1b = PIND & _BV(7); //前
bool ir_1 = IR_1b >> 7;
Ball_1 += ir_1;
は、変数を使わずに、
if(PIND & _BV(7))
Ball_1++;
とすれば(他のセンサも同じ)、もっと速くなると思います。
なるほど!
削除出てきた数値をわざわざ変換しなくてもif文ですれば、こんなに処理を書かなくてもよさそうですね!
アドバイス本当にありがとうございます!