前回の記事では,KXR_HAC_SoftwareにおけるHierarchical Task Network(階層型タスクネットワーク)を用いたロボットの行動計画について解説しました.記事の最後に述べたように,正しく行動計画を行うためには,外界の状態を正確にセンシングして計画に反映しなければいけません.本記事では,KXR_HAC_Softwareにおける画像認識モジュールについて解説します.
HAC-KXRに搭載されたセンサ
以前の記事に搭載したシステム構成図のとおり,HAC-KXRには2つのセンサ,Buffalo製120°広角Webカメラと9軸IMUが搭載されています.KXR_HAC_Softwareでは,この2つのセンサを用いて外界の状態を認識しており,カメラを用いた実装は画像認識モジュールのvision_library.py
内で行われています.
画像認識の実装
では,vision_library.py
における画像認識の実装について,各要素ごとに解説を行っています.実装全体に共通する部分として,基本的にはOpenCVを用いた色検出で各オブジェクトの抽出を行っています.これは,HACフィールド内のボールやゴールライン,フィールド端ラインなどの認識すべきオブジェクトは,全て異なる色分けをされているためです.こちらについては,以前の記事中の,「カメラ画像からの物体検出」の項目にも記載しています.大まかな流れとしては,
- 取得したカメラ画像をキャリブレーション(歪み補正・鳥瞰図変換)
- 検出対象のオブジェクトの色の領域を抽出
- その領域の大きさから,オブジェクトの存在判定を行う.場合によっては形状なども存在判定に用いる.
- 対象のオブジェクトが存在していれば,オブジェクト領域の重心位置からXY座標,ハフ変換で直線オブジェクトの方向,などを取得する.
となっています.HSV色空間の閾値や,オブジェクトの存在判定のピクセル数の閾値などは,parameterfile.py
にまとめて記述しています.
class VisionLibrary
本クラスに,画像認識に関わる関数がフィールドオブジェクトごとに実装されています.前回記事で解説したタスク実行モジュールから各関数を呼び出し,ロボット周囲の情報を行動計画に反映しています.
init(self)
クラスの初期化関数です.カメラ画像取得のフォーマットやFPSを指定するとともに,各フィールドオブジェクトの検出状態を初期化します.
calibrate_img(self)
カメラ画像を取得し,画像のキャリブレーションを行う関数です.通常,カメラで撮影した画像は,そのカメラのレンズ特性により一定の歪みが含まれています.特に,今回HAC-KXRで用いているような広角カメラでは,画角の外側ほど景色が引き延ばされて歪む性質があるため,正確な画像認識を行う為にはこの歪みを補正しなければいけません.そこで,チェッカーボードを用いて歪み補正パラメータを取得し,これをOpenCVのundistort(歪み補正)関数に渡すことで補正しています.歪み補正パラメータは,tmp
ディレクトリ内の2つのcsvファイルです.より細かい解説は,外部のこちらの記事が詳しいです.
さらに,HAC競技では,各フィールドオブジェクトとロボットの位置関係を正確に把握することが非常に重要です.そこで,歪み補正を行った画像にさらに鳥瞰図変換(Bird Eye View Transformation)を行うことで,画角内を真上から見下ろしたような画像に変換しています.鳥瞰図変換についてのより詳しい解説は,外部のこちらの記事が詳しいです.今回の実装では,画角内に収まる最大限の長方形の紙を置き,この四隅に合わせて画像を変形しています.なお,今後の処理の簡単のために,床面の1mmがおおよそ1ピクセルとなるように変換しています.
ここまでの変換については,KXR_HAC_Hardwareの通りに製作されていれば,パラメータの変更は必要ありません.
detect_ball(self detect_ball_wide(self)
ボールを検出し,ロボットから見たXY座標を返す関数です.ボールのオレンジ色を示すピクセルが画像内に閾値以上含まれていれば,ボールが存在するとみなして,その重心座標を返します.def detect_ball_wide(self)
は,より広い範囲でボールを検出したい際に,キャリブレーション前の広角画像を使って大まかなボールの位置を返します.
detect_corner(self) detect_corner_wide(self)
フィールドのコーナー赤線をテンプレートマッチングによって検出し,コーナー種別とXY座標を返す関数です.
左右のコーナーの白黒テンプレート画像を用いて,キャリブレーション後のカメラ画像との類似度を算出します.ここで閾値を超えた類似度の箇所があった際には,コーナーが存在しているとみなされ,その種別(左/右)と座標を返します.
detect_corner_wide(self)
は,より広い範囲でコーナーを検出したい際に,キャリブレーション前の広角画像を使ってコーナーを探します.
detect_edge_using_numpy_calc(self)
フィールドのエッジ赤線を検出し,その角度(°),傾き,切片を返す関数です.
エッジの赤色を示すピクセルが画像内に閾値以上を含まれていれば,エッジが存在するとみなします.このとき,ハフ変換で直線を検出し,検出された直線のパラメータの平均をとることでエッジの位置や角度がわかります.ハフ変換については,外部のこちらの記事が詳しいです.ここで得られたエッジの傾き,切片をもとに,タスク実行モジュールtask_execute_library.py
中のcalculate_distance_from_the_edge_mm(self, slope, intercept)
関数で三角関数を用いてロボットからエッジまでの距離を算出しています.
def detect_goal(self)
ゴールの白線を検出し,その角度(°),傾き,切片を返す関数です.ゴールラインの白線を示すピクセルが画像内に閾値以上を含まれていれば,ゴールラインが存在するとみなしてハフ変換で直線検出を行います.このとき,フィールドに反射した明かりが白飛びすることで誤検出が発生するときがあるため,白色が検出された部分のうち,ゴールに相当する細長い直線以外はマスクする処理も行っています.
detect_ball_line(self)
ボールが置かれている青線を検出し,その角度(°),傾き,切片を返す関数です.
display_resultimg(self)
カメラ画像に検出結果を重ねた結果画像を表示する関数です.
おわりに
ここまで解説したように,KXR_HAC_Softwareの画像認識モジュールでは,各フィールドオブジェクトの色が異なることを利用して,ロボットとオブジェクトの位置関係を取得しています.ここでの認識結果に基づいて前回記事のタスク実行モジュールが働き,ロボットの動作目標を決定します.次回,HAC入門編ラストの記事では,動作目標に従ってロボットを動かす,制御モジュールの解説を行います.