SKY Crew Lab

RoboCup Junior(ロボカップジュニア)参加チーム、SKY Crew(スカイクルー)の技術ブログ。
技術の共有・伝達を目的に、ロボットに関するいろいろな情報やガイドを載せていきます。

2017年大会でRescue Line(レスキューライン) 世界3位、2018年大会でSoccer Lightweight(サッカーライトウェイト) 世界総合5位を獲りました!

2019年05月

日本大会の懇親会にて人気者のkossanの隣で爆睡していた、篠川です。

大会がひとまず一段落したので、またパラパラと記事を出していくつもりです。多分…

今回は私たちが日本大会で使った、全方位カメラでの相手ゴール周りの認識方法をものすごくざっくり説明します。

全方位ミラーを使う場合しか適応できない話が多くなりますが、ご容赦ください。

カメラの仕事は、相手ゴールの認識と、自陣ゴールの認識に大きく分けることができます。

この記事では前者についてです。

一応言っておくと、これはあくまで私たちが私たちのロボット向けに書いたプログラムの流れを、参考になるかもしれないと書き下しているだけのものです。

つまり、これは必ずしもベストとは限らないということです。

なので、ほかのチームにどれだけ当てはまるか、役に立つかは私もわかりませんし、もっといい方法も当然そのうち見つかるでしょう。

あくまで参考としてお読みください。

また、他にいいアイデアがあれば、ぜひコメントにお寄せください!

「この部分をもっと詳しく・よくわからない」といったご意見もどんどんお願いします。

内容

カメラが「相手ゴール周り」で認識するのは以下のことです。

  • 相手ゴールの方向
  • 相手ゴールの中でゴールキーパーがおらず空いている位置
  • 自機が前の角にいるか
  • ボールを蹴りだしてよいか

カメラは毎ループこれらを計算し、UART通信でメインマイコンに投げています。

大まかにこの順番に、どういうことをしているのかを説明していきます。

プログラムの書き方などはこの記事では扱いませんが、OpenMVに関してはIDEのメニューからFile→Examples以下のサンプルプログラムがとても参考になります。

そのまま貼り付けて使用するのはルール違反ですが、プログラムの書き方、ライブラリの使い方を理解する助けになると思います。

相手ゴールの方向

まずは相手ゴールの検知をするところから始まります。

全体的な方針として、角度を知りたいので、視野の中心、ミラーのてっぺんに当たる位置からぐるっと放射状に見ていきます。

close2

※画像左がロボットの正面

OpenMVで色を認識するといったら普通はimage.find_blobs(thresholds)を使うと思いますが、ここではそこまでする必要はありません

カメラをロボットに乗せた状態で、PCで視野を見ながらゴールのすぐ前→遠くまでだんだん離していくと、(当たり前ですが)ゴールが遠ざかっていくように見えますね。

close

far

ここで、下の写真の白い2円上をぐるっと見ていくと、その角度にゴールがあるなら、必ずその円上のどちらかの点がその色になっているはずです。

close with circles

far with circles

(手書きの雑な図ですみません)

ロボットが相手ゴールから背を向けるような向きになっている時はジャイロセンサーでまず正面(画像左)の向きに復帰する動きをさせるので、とりあえず前半分を回って確認していって、相手ゴールがあるかどうか調べていけばいいことになりますね。

ひとつ前のフレームとそこまでゴールの見える位置が変わることはないはずと考えると、毎ループで180度すべてを見回す必要もなく、直前にゴールがあった位置のすぐ近くを探索すれば、すぐにゴールが発見できます。

また、ある角度で黄色・青色のものを認識したとき、念のため+10度、ー10度の角度も確認し、干渉でないかどうか確認できます。周りに同じ色がなく孤立した小さなエリアはゴールではなく、ロボットのLED等の小さな部品が黄色・青色に見えているのだろうと判断できるからです。

というわけで、これによりゴールの左端の角度、右端の角度がわかりました。

相手ゴールキーパー回避

ゴールの位置が分かったところで、大抵はそこに相手ゴールキーパーがいます。

イナズマイレブンでもあるまいし、真ん中のゴールキーパーめがけてシュートしたり、ドリブルで突進していっても切ないので、ついでに相手ゴールキーパーも認識して、避けるように進ませたいところです。

というわけで、ゴールがある角度を7つくらいに分割して、それぞれの角度で「中心からどれだけ離れたところでゴールの色が出てくるか」ということを調べます。

中心から外側に向かって見ていって、最初に黄色・青色になるまでの距離を見るということです。

yellow

yellow enemy

さてここで、平均から明らかに外れて距離が遠いところがあります。上の写真で黄色く十字が打ってあるところです。

ここら辺を避けていくと、いい感じになりそうです。

というわけで、青の十字が一番長く並んでいる範囲に進めばよいですね。

補足すると、単に「平均より少しでも遠くに十字があれば、そこには相手!」とするのは良くないです。

コートの前側の角あたりにいてゴールの形が歪んでいたり、カメラの色認識に誤差が多少あったりすると、相手ロボット認識がいろんなところに出て荒ぶってしまうからです。

多少のマージンをつけましょう。

また、image.find_blobs(thresholds)はここらへんのところがごちゃごちゃにされています(よくわからん)。

相手ゴールキーパーがいるところもざっくりまとめて「ゴール」と認識されてしまったり、そもそも全方位に向いていないということで、この関数は結局一箇所も使わないことになりました。

ライブラリの関数はC言語で書かれてコンパイル済みになっており、Pythonで自力でループをまわして探して認識するより効率は良いはずなので、出来ればそちらを使いたいというのはあったのですが…

シンプルにゴールの位置を探すだけなら、きっとimage.find_blobsが一番楽なのでしょう。

ちなみに、去年の世界大会からは前向きにPSDセンサーを付けていて、正面の相手ロボットを認識してよけていくので、ドリブル時の敵よけはそちらにより頼るようになりました。

ただし、斜め前などの相手ロボットを避ける、相手ロボットの背丈が低い・肉抜きがすごくてあまり距離センサーが反応しない、もともと距離が遠いなどであれば、カメラの働きは大きいです。

コート前角検知

こちらは簡単なのでさらっと。

ゴールがめちゃくちゃ右・左に見えたら、それは自機がゴールに対して左・右にいるということです。

ゴールが真横近くにあるなら、自機がコートの前の方の角にいるとわかりますね。

相手ゴールの真横まで言ってボールを追いかけてもシュートにはつながらないし、角でアウトオブバウンズしたりゴールに引っ掛かったりすると、角は嫌なことづくしです。

というわけで、深追いせずに中立点の手前くらいまで後退し、アウトオブリーチやラックオブプログレスがかかるのを待つことにします。

ゴールの端っこが相手キーパーで隠されたりすると多少ずれてしまいますが、そこはあまり深く気にしないことにしました。

メインマイコンからジャイロセンサーの数値を送ってもらって、それを使って視界に映るゴールの角度を補正すると、割といい感じになります。

ボールを蹴りだしてよいか

ここら辺はキーパー検知と絡んできます。ロボットの正面と、左右数度を見渡して、ゴールがあり、相手ロボットがいなければ、キッカーを発動していいよ、となります。

メインマイコンは、ボールをキャッチした時、この情報をもとにキックするかを決めます。

これが「キッカーNG」という判定であれば、まだシュートせずに、相手ロボットの回避をがんばりつつドリブルしていくことになります。

その他の補足・小ネタ

カメラ(OpenMV)を使っているうちに気が付いた、本記事に関連するいくつかのことを、何かのために書いておきます。

FPS

PCにつないでカメラを走らせているときは、FPS(メインループが1秒で何周するか)を見ることができます。OpenMV IDEであれば右下にFPSというものが映っているはずです。

IDEに付属しているサンプルプログラムも、FPSを表示させるものがほとんどですね。

実は過去、後先考えずに相手ロボットの認識プログラムを書いていた時、気がつくとこれが5とか6になりました。つまりループごとに間隔が0.2秒くらい空いてしまうことであり、さすがに…という話になりました。

どうやって処理をショートカットして楽にするか、またどこの精度を落として手を抜けるか考えるのも大切そうです。

また、これもOpenMVの話ですが、PCにつないでFrame Bufferを表示させるだけで、通信でなかなかの時間がかかります。

IDE右上の「Disable」というボタンを押すと、Frame Buffer用の通信をしなくなるので、よりPCにつないでいないときに近いFPSを確認できます。

ただし「Disable」するとOpenMV IDE側でFPSを表示してくれなくなるので、サンプルプログラム通りにclock.fps()をprintしてみるのがよいと思います。

ちなみに現在の最新プログラムのFPSは30弱です。実はこれでも割と頑張った。

Stack overflow / Memory allocation error

OpenMVのメモリは実をいうとけっこう貧弱なようです。listやdictを大量生産していたとき、StackOverflowや"Cannot allocate memory"系のエラーが出て途方に暮れました。

気を付けましょう。

それから衝撃的なことに、静的変数を多く使用する、400行くらいになったプログラムをOpenMVに書き込み、実行させたとき、プログラムが長すぎてメモリに載りきらずに、エラーが出たり、ビジー状態になって手に負えなくなったりしました。

どうやらPCにつないで給電された際に、書き込んだプログラムをメモリに読み込み、さらにPCから「このプログラムを実行してくれ」と流し込んだことにより、400行プログラム2個分を読み込むことになっていたようです。

そこで、長めのプログラムが書き込まれた状態でPCと接続して、何かしらプログラムを走らせる時には、Hello Worldレベルの短いプログラムを一回だけ実行させてからだと、いったんメモリの中身がリセットされるのか、その直後長いプログラムをPCから実行してもエラーを吐かないことを発見しました。

Pythonプログラム中の半角スペースをTabで置き換えるのも地味に大きいです。

でもこれ1000行とか行ったら実行できなくなりそうだな…

通信

ほとんどの場合、メインマイコンに比べるとカメラのFPSはずっと低いです。通信でデータを投げるペースが全く違うので、メインマイコンでループ毎にカメラからの情報が来るまで待ち受けていると、それにメインマイコンの処理全体がものすごく足を引っ張られることになります。

メインマイコン側では「データが来るまで待つ」ではなく、「データが来ていたらなくなるまで受け取る」とするのがよさそうです。

干渉

相手ロボットが黄色や青の部品を積んでいたり、LEDが青色に見えたりすることがあります(私たちもあまりほかのチームのことを言えないところはあります)。

多少の小さなもので影響を受けないような仕組みにすることも大切です。なにかとびとびで小さく変な黄色・青色(・オレンジ色)のものを検知したら、ゴール(・ボール)と区別し、弾いてしまいましょう。

試合が始まる前に相手ロボットをしっかり見ておくのも大切です。後悔先に立たず。

まとめ

ここまで、実際にどうプログラムを書くのかという部分は全ておいておいて、「なにを行っているか」ということを書いてきました。

おそらくかなり雑でわかりにくいところもあると思うので、ご指摘、ご質問はどんどんお願いします。

また、かなりザーッと流れを書いたので、「この部分をもっと詳しく知りたい」というような意見は大歓迎です。

自陣ゴール周りの仕事やキーパーの動きについても、そのうち記事を書きたいと思っているので、よろしくお願いします。

こんにちは。SYです。

GitHubに現在のプログラムをすべて公開していますので、良かったら参考にしてください。

プログラム ライブラリ

自分用のバックアップのためコミットメッセージ等は適当なので、そこは気にしないでください。

時間があれば(たぶんない)解説記事も書こうかと考えています。

質問等あればコメント欄やTwitterでお願いします。参考にしていただけた場合、コメント欄等で教えていただけると嬉しいです。

また、プログラム関係の記事で書いてほしいことについてアンケートのご協力をお願いします。

アンケート


アンケートのご協力ありがとうございました!m(_ _ )m

また次回の記事をお待ちください!(´∀`)ノシ

kossanです。

4/27〜29に行われたロボカップジュニアジャパンオープンの試合動画をYouTubeにアップしました。以下のリンクからご覧いただけます。(日付と大会名を修正しました。2019.5.4)

再生リスト - SKY Crew RoboCupJunior 2019 JapanOpen

また、得点シーンの一部(ちょっとかっこいいやつ)を集めたダイジェスト動画、失点シーンを集めた失点集も作成しました。どうぞご覧ください。

試合の解説などなど記事を書きたいですが、如何せん世界大会まで時間がないのです。またしばらく記事を書けなくなるかもしれませんが、ご了承ください。

ではっ、また次回まで!

皆さん、お久しぶりです!SYです。

長らく更新が止まっており、申し訳ありません。

4/27(土)~29(月)に、ロボカップジュニア・ジャパンオープン2019和歌山に出場しました。

本日はその結果報告だけ先にすることにします。

各試合の解説記事も後日出しますので、お楽しみに。

プレゼンテーションシート

プレゼンシート

※フルの動画は後ほど公開します

最終結果―1位通過!

参加部門 : RoboCupJunior Soccer Lightweight

順位 : 1位

総得点 : 80-7 (11試合全勝)

アウトオブバウンズ : 2回

故障 : 2回

オウンゴール : 3回

オウンゴールが少し多かったことと、マカオシュートを何回か決められてしまったことが反省点のひとつです。

アウトオブバウンズが何回か起きてしまったのはラインの角でラインセンサがうまく反応しなかったことが原因だと思われます。

故障はカメラの不具合やジャイロのリセット忘れが原因です。ジャイロのリセット忘れのような人為的ミスは防げるものなので、試合前には焦らないことが大切です。

大会の様子

64チームが参加。去年と参加チーム数は同じのようです。

調整用コートはビギナー用1台とライトウェイト、オープン用3台がありましたが、1台はコートの色が少し違ったせいか少し空いていました。

用意された試合用コートはA~Kの11台です。

技術委員会のブログに書いてあるスケジュール通りに試合等は進行していきました。

予選

全6回のスイス式トーナメント。

試合時間 : 前半4分、ハーフタイム3分、後半4分

試合間隔 : 4分弱

決勝進出枠 : 6チーム(9.4%)

チーム数も多く、ライトウェイトとオープンを交互に行うため、試合と試合の間には1時間ほどの調整時間がありました。

昼休み

もちろんそんなものはない

決勝リーグ

6チームで総当たり戦

試合時間 : 前半5分、ハーフタイム5分、後半5分

試合間隔 : 10分弱

5試合連続で試合を行うため調整時間がほとんどなく、かなり大変でした。

決勝リーグタイムテーブル

決勝リーグタイムテーブル

前回からの変更点


  • 審判によるボールの持ち上げ検知 → ホームポジションに戻る
  • キーパーの精度向上
  • 回り込みの精度向上

RCJ2019Wakayama

忙しくて更新が止まることもありますが、どうか長い目で見守ってください。

それではまた! (´∀`)ノシ

↑このページのトップヘ