こんにちは、Habr。 これは、無人車両に関するレポート後のチュートリアルです。機器のコストをかけずに物事を行う(開始する)方法です。 すべてのコードはgithubで利用できます。とりわけ、このようなクールな画像を簡単に生成する方法を学習します。
簡単に
トピックに精通している人向けの要約:従来、機械学習に基づいて自動操縦のトレーニングサンプルを設定するには、 かなり情報量の多いCANバスとそのインターフェイスを備えた特別装備の車が必要でしたが、これは高価です。 私たちはそれをより簡単に無料で行います-フロントガラス上のスマートフォンから同じ基本的なデータを収集します。 どんな車にも適し、装備の変更はありません。 このシリーズでは、ビデオを使用して、各瞬間のステアリングターンを計算します。 この段落ですべてが明確な場合は、 すぐに導入部を飛び越えてアプローチの本質に移ることができます。
なぜ、なぜ、より詳細に
したがって、数年前、大企業の深刻なリソースがなければ、自動操縦のトピックに入る理由はありませんでした-LIDARセンサーだけで数万ドルかかりましたが、ニューラルネットワークの最近の革命はすべてを変えました。 一対のウェブカメラからの最も単純なセンサーのセットを持ついくつかの人々のスタートアップは、有名ブランドと結果の品質と同等の条件で競います。 特に、非常に多くの高品質のコンポーネントがすでにパブリック ドメインにあるので、ぜひ試してみてください。
オートパイロットは、センサーデータを制御アクション(ステアリングホイールの回転と必要な加速/減速)に変換します。 Googleのようなレーザー距離計システムでは、次のようになります。
センサーの最もシンプルなバージョンは、フロントガラスを通して「見える」ビデオカメラです。 誰もがすでに携帯電話にカメラを持っているので、私たちは彼と協力します。
畳み込みニューラルネットワークは 、生のビデオから制御信号を計算するのにうまく機能しますが、他の機械学習アプローチと同様に、正しい結果を予測する方法を教える必要があります。 トレーニングを行うには、(a)モデルアーキテクチャを選択し、(b)モデルのさまざまな入力状況と、各モデルの「正解」(ステアリング角度やアクセルポジションなど)を示すトレーニングサンプルを作成する必要があります。 トレーニングサンプルのデータは、通常、マシンが人によって制御されるレースから記録されます。 つまり、ドライバーはロボットに機械の操作方法をデモンストレーションします。
パブリックドメインには多くの優れたニューラルネットワークアーキテクチャがありますが、データの状況はもっと悲しくなります。1つ目はデータが十分ではないことです。2つ目は、ほとんどすべてのサンプルが米国のものであり、これらの場所とは多くの違いがあります。
オープンデータの不足は簡単に説明できます。 まず、データはアルゴリズムとモデルの専門知識と同じくらい貴重な資産であるため、誰も急いで共有することはありません。
ロケットエンジンはモデルであり、燃料はデータです。
アンドリュー・ン
第二に、データを収集するプロセスは、特に「額」で行動する場合、安価ではありません。 その好例がUdacityです。 彼らは、ステアリングとガス/ブレーキがデジタルバスに接続されている車モデルを特別に選択し、バスへのインターフェイスを作成し、そこからデータを直接読み取りました。 アプローチのプラスは、高品質のデータです。 マイナスは深刻なコストであり、大部分の非専門家を遮断します。 実際、 CANであっても、すべての現代車が必要なすべての情報を書き込むわけではなく、インターフェイスをいじる必要があります。
簡単に行います。 「生の」データ(現在は単なるビデオ)をDVRとしてフロントガラス上のスマートフォンで記録し、そこから必要な情報(オートパイロットをトレーニングできる速度とターン)を「絞り込み」ます。 その結果、ほとんど無料のソリューションが得られます。フロントガラスに電話の所有者がいる場合は、ボタンを押すだけで作業中のトレーニングデータを収集できます。
このシリーズでは、ビデオから回転角度を「絞り」ます。 すべての手順は、 githubコードを使用して自分で簡単に繰り返すことができます。
挑戦する
問題を解決します。
- 車に固定されたカメラからのビデオがあります(つまり、カメラがハングアップしません)。
- 各フレームで現在のステアリング角度を見つけることが必要です。
期待される結果:
すぐに少し単純化します-ステアリング角度の代わりに、水平面内の角速度を計算します。 これは、次のシリーズで行う並進速度を知っている場合、ほぼ同等の情報です。
解決策
このソリューションは、公開されているコンポーネントから組み立てて、少し仕上げます。
カメラの軌道を復元する
最初のステップは、ビデオ用のSLAMライブラリ (同時ローカリゼーションとマッピング、同時ローカリゼーションとマップ構築)を使用して、 3次元空間でカメラパスを復元することです。 出力では、各(ほぼニュアンスを参照)フレームに対して、6つの位置パラメーターを取得します:3Dオフセットと3 方向角 。
optical_trajectories
モジュールは、 optical_trajectories
この部分を担当しoptical_trajectories
ニュアンス:
- ビデオを録画するとき、最大解像度を追いかけないでください-特定のしきい値を超えて、それは痛いだけです。 720x480付近の設定は、私にとってはうまく機能します。
- カメラは、レースのビデオが記録されたのと同じ設定でキャリブレーションする必要があります( 指示 、 理論パート1と2が関連します )。
- SLAMシステムには、参照ポイントとして「フック」できる「良好な」フレームシーケンスが必要です。そのため、システムが「フック」するまで、ビデオの最初の部分には注釈が付けられません。 動画のローカライズがまったく機能しない場合は、キャリブレーションの問題(数回キャリブレーションして結果のばらつきを確認してください)またはビデオの品質の問題(高すぎる、圧縮が大きすぎるなど)のいずれかです。
- 隣接するフレーム間で失われるキーポイントが多すぎると、システムによるSLAMトラッキングの中断が発生する可能性があります。たとえば、ガラスが水たまりからのスプラッシュで瞬間的にあふれる場合があります。 この場合、システムは初期のローカライズされていない状態にリセットされ、再びローカライズされます。 したがって、1つのビデオから、いくつかの軌跡を取得できます(時間的に重複しません)。 これらの軌跡の座標系は完全に異なります。
- 私が使用した特定のORB_SLAM2ライブラリは、並進変位に関する信頼性の高い結果を提供しません。したがって、現時点ではそれらを無視しますが、回転を適切に決定し、そのままにします。
道路の平面を決定する
3次元空間でのカメラの軌跡は良好ですが、それでも最終的な質問に直接答えることはできません。左または右に回転し、どのくらいの速さです。 結局のところ、SLAMシステムには「ロードプレーン」、「トップボトム」などの概念がありません。 この情報は、「生の」3Dパスから抽出する必要もあります。
ここでは簡単な観察が役立ちます。 通常、道路は垂直よりも水平方向にはるかに長く延びています。 もちろん例外もありますが 、無視する必要があります。 その場合、軌道の水平面として最も近い平面(つまり、最小の再構成誤差を与える平面)を取得できます。
軌跡のすべての3D点に沿った主成分の優れた方法で水平面を選択します。最小の固有値を持つ方向を削除すると、残りの2つが最適な平面を提供します。
optical_trajectories
モジュールは、プレーン割り当てロジックも担当しoptical_trajectories
ニュアンス:
主なコンポーネントの本質から、山道に加えて、車が常に直線で走行している場合、メイン平面を強調表示することはうまく機能しないことは明らかです。匹敵する。
そのような軌道からの大きなエラーでデータを汚染しないようにするために、最後の主要なコンポーネントに沿った散布が最後から2番目のコンポーネントに比べて大幅に(100倍)小さいことを確認します。 軌道を通過しないものは、単に捨てられます。
回転角を計算する
水平面v 1およびv 2の基本ベクトル(前の部分から最大の固有値を持つ2つの主要コンポーネント)がわかっているので、カメラの光軸を水平面に投影します。
したがって、カメラの3次元の向きから、車の方位角を取得します(一般的なケースではカメラ軸と車軸が一致しないため、未知の定数に正確です)。 回転の強さ(つまり、角速度)にのみ関心があるため、この定数は必要ありません。
隣接するフレーム間の回転角度は、学校の三角法によって与えられます(最初の要因は回転の絶対値で、2番目は左右の方向を決定する記号です)。 ここで、a tは、時間tでの射影ベクトルaを意味します。
計算のこの部分は、 optical_trajectories
モジュールによっても実行されます。 出力は、次の形式のJSONファイルです。
{ "plane": [ [ 0.35, 0.20, 0.91], [ 0.94, -0.11, -0.33] ], "trajectory": [ ..., { "frame_id": 6710, "planar_direction": [ 0.91, -0.33 ], "pose": { "rotation": { "w": 0.99, "x": -0.001, "y": 0.001, "z": 0.002 }, "translation": [ -0.005, 0.009, 0.046 ] }, "time_usec": 223623466, "turn_angle": 0.0017 }, ..... }
コンポーネント値:
-
plane
水平面の基本ベクトル。 -
trajectory
-要素のリスト。SLAMシステムによって正常に追跡された各フレームに1つ。
-
frame_id
ソースビデオのフレーム番号(0から開始)。 -
planar_direction
水平面への耳軸の投影 -
pose
空間でのカメラの位置
-
rotation
- 単一の四元数の形式での光軸の方向。 -
translation
-オフセット。
-
-
time_use
ビデオの開始からの時間(マイクロ秒) -
turn_angle
前のフレームを基準にしたラジアン単位の水平回転。
-
ノイズを取り除く
私たちはほとんどそこにいますが、問題は残っています。 結果の(これまでの)角速度グラフを見てみましょう:
ビデオで視覚化する:
一般に、回転の方向は正しく決定されていますが、多くの高周波ノイズがあることがわかります。 ローパスフィルターであるガウスぼかしでこれを削除します。
コードのスムージングは、 smooth_heading_directions
モジュールによって実行されます
フィルター後の結果:
トレーニング済みのモデルを「フィード」して、適切な結果に依存することはすでに可能です。
可視化
明確にするために、JSON軌跡ファイルのデータに従って、上記のデモのように、ソースビデオに仮想ステアリングホイールを重ね合わせて、正しく回転しているかどうかを確認できます。 render_turning
モジュールrender_turning
ます。
フレームごとのグラフも簡単に作成できます。 たとえば、matplotlibがインストールされたIPythonラップトップの場合:
import matplotlib %matplotlib inline import matplotlib.pyplot as plt import json json_raw = json.load(open('path/to/trajectory.json')) rotations = [x['turn_angle'] for x in json_raw['trajectory']] plt.plot(rotations, label='Rotations') plt.show()
今のところすべてです。 次のシリーズでは、速度を制御する方法も学習するために翻訳速度を決定しますが、今のところプルリクエストを歓迎します。