OpenCVベースのラインフォロー

現在、自動車用の自動操縦装置の作成に関する非常に人気のあるコースです。 Udacityからのこのナノ度は、おそらく最も有名なオプションです。



多くの人々がそれから学び、決定を投稿します。 私も通り過ぎることができず、夢中になりました。



違いは、コースでは提供されたデータに基づいてアルゴリズムを開発することであり、ロボットのためすべてを行ったことです。



コースの学生がコンピュータビジョンを勉強する際に直面する最初のタスクは、道路上の線に従うことです。 このトピックに関して多くの記事が書かれていますが、ここに最も詳細なものがいくつかあります。





それらはすべて非常に単純であり、作業のスキームはいくつかのポイントに削減されます。





私は白いテープを床に接着し、仕事に取り掛かりました。







上記の作品では、あなたの仕事は黄色の線を見つけることでしたので、彼らはHLSとHSVの色で働きました。 私のラインは白だけだったので、私はこれを気にせず、白黒フィルターに自分自身を制限することにしました。



幾何学



ジオメトリの問題はすぐに始まりました。 写真の生徒の場合、矢印の付いたストライプが地平線に入ります。 それでも、多くの行が検出され、作成者が結合する必要がありました。 しかし、彼らの線はうまく方向付けられており、写真には破片はありませんでした。



私は全く異なる絵を持っています。 電気テープのストリップの形状は、まっすぐではありませんでした。 床のまぶしさはノイズを生成しました。



Cannyを適用した後、これが起こりました。







そして、ハフ線は次のようなものでした。



画像



基準を強化すると、ゴミを排除することはできましたが、ストリップ上で見つかったほとんどすべての線が消えました。 このような小さなセグメントに依存するのは愚かなことです。







一般的に、結果は非常に不安定であり、別のアプローチを試してみることができました。



線の代わりに、輪郭を探し始めました。 最大の輪郭が電気テープであると仮定して、ゴミを取り除くことができました。 (その後、大きな白いベースボードが電気テープよりもフレーム内のスペースを多く占めることが判明しました。ソファクッションでカバーする必要がありました)。

輪郭を囲む最小の長方形を使用する場合、中央の縦線はモーションベクトルの役割に非常に適しています。









2番目の問題は照明でした。 ソファの影にトラックの片側を非常にうまく配置し、同じ設定でトラック全体の写真を処理することは完全に不可能でした。 その結果、白黒フィルターに動的なカットオフを実装する必要がありました。 アルゴリズムは次のとおりです。画像にフィルターを適用した後、白が多すぎる場合(10%以上)、しきい値を上げる必要があります。 少なすぎる場合(3%未満)-省略します。 実践では、平均3〜4回の反復で最適なカットオフを見つけることができることが示されています。



マジックナンバーは個別の設定に配置され(以下を参照)、最適なものを探してプレイすることができます。



def balance_pic(image): global T ret = None direction = 0 for i in range(0, tconf.th_iterations): rc, gray = cv.threshold(image, T, 255, 0) crop = Roi.crop_roi(gray) nwh = cv.countNonZero(crop) perc = int(100 * nwh / Roi.get_area()) logging.debug(("balance attempt", i, T, perc)) if perc > tconf.white_max: if T > tconf.threshold_max: break if direction == -1: ret = crop break T += 10 direction = 1 elif perc < tconf.white_min: if T < tconf.threshold_min: break if direction == 1: ret = crop break T -= 10 direction = -1 else: ret = crop break return ret
      
      





マシンビジョンを調整すると、実際の動きに進むことができました。 アルゴリズムは次のとおりです。





コードの短縮版(フル-Githubで ):



 def check_shift_turn(angle, shift): turn_state = 0 if angle < tconf.turn_angle or angle > 180 - tconf.turn_angle: turn_state = np.sign(90 - angle) shift_state = 0 if abs(shift) > tconf.shift_max: shift_state = np.sign(shift) return turn_state, shift_state def get_turn(turn_state, shift_state): turn_dir = 0 turn_val = 0 if shift_state != 0: turn_dir = shift_state turn_val = tconf.shift_step if shift_state != turn_state else tconf.turn_step elif turn_state != 0: turn_dir = turn_state turn_val = tconf.turn_step return turn_dir, turn_val def follow(iterations): tanq.set_motors("ff") try: last_turn = 0 last_angle = 0 for i in range(0, iterations): a, shift = get_vector() if a is None: if last_turn != 0: a, shift = find_line(last_turn) if a is None: break elif last_angle != 0: logging.debug(("Looking for line by angle", last_angle)) turn(np.sign(90 - last_angle), tconf.turn_step) continue else: break turn_state, shift_state = check_shift_turn(a, shift) turn_dir, turn_val = get_turn(turn_state, shift_state) if turn_dir != 0: turn(turn_dir, turn_val) last_turn = turn_dir else: time.sleep(tconf.straight_run) last_turn = 0 last_angle = a finally: tanq.set_motors("ss")
      
      





結果



不均一ですが、自信を持って、戦車は軌道に沿ってrawっています:







デバッグ用グラフィックスのGIFは次のとおりです。







アルゴリズム設定



 ## Picture settings # initial grayscale threshold threshold = 120 # max grayscale threshold threshold_max = 180 #min grayscale threshold threshold_min = 40 # iterations to find balanced threshold th_iterations = 10 # min % of white in roi white_min=3 # max % of white in roi white_max=12 ## Driving settings # line angle to make a turn turn_angle = 45 # line shift to make an adjustment shift_max = 20 # turning time of shift adjustment shift_step = 0.125 # turning time of turn turn_step = 0.25 # time of straight run straight_run = 0.5 # attempts to find the line if lost find_turn_attempts = 5 # turn step to find the line if lost find_turn_step = 0.2 # max # of iterations of the whole tracking max_steps = 100
      
      





Githubコード。



All Articles