自動運転データの検索システムを爆速で作った話

はじめに

こんにちは、機械学習の活用を加速させるために学習インフラの開発やワークフロー自動化に取り組むチームに所属している澁井です。今回は自動運転のためのデータ検索基盤を自作した話を書きます。

なお、ティアフォーでは「自動運転の民主化」をともに実現していくエンジニアを募集しています。今回ご紹介する機械学習だけではなく、様々なバックグラウンドをお持ちの方と開発を進めていく必要があります。下記ページから募集職種のリストをご覧いただき、興味を持った方はぜひお気軽にご連絡ください!

さて、ティアフォーでは自動運転に機械学習を活用しています。主な機械学習の用途は画像や動画データを扱った認識技術への応用です。

機械学習では意味づけ(アノテーション)されたデータが大量に必要となります。アノテーションとはネコの画像には「ネコ」、イヌの画像には「イヌ」と画像に意味を付与することです。

自動運転で扱うデータは主に実証実験で各地を走ったデータになり、機械学習の学習データも実証実験で得たデータになります。しかし実証実験のデータはアノテーションされておらず、自動運転を動かしているROS(Robot Operating System)特有のrosbagというデータ形式になっているため、人間が生データを見て理解することはできません。データを分析して学習データを作るためにはrosbagを画像や動画形式に変換し、人間に読めるようにする必要があります。

f:id:cvusk:20210412082703p:plain

自動運転からROS経由でデータを得る

実証実験で収集するデータ量はビッグデータと呼ぶに相応しく、一日に数TBを超える非構造化データが集まります。実証実験の価値を機械学習にフィードバックするためには収録したデータの中から有益なデータを探索し、学習データに加えられるようにする必要がありました。

f:id:cvusk:20210412082956p:plain

自動運転のデータ規模

社内のデータ活用の状況を確認するため、社内コミュニケーションツールで下記のような質問をしてみました。

質問:rosbagのデータを活用してる方々にオープンに質問なのですが、必要なデータはどうやって検索していますか? たとえば緯度経度や地名による検索とか、年月日、時間帯とか、どういう条件で検索できると便利でしょうか?

 

回答:ほしい!

f:id:cvusk:20210412083253p:plain

社内取引はネコ画像

その結果、みんなデータを探せなくて困っているものの、データを整備する工数が取れない、ということがわかりました。課題は見つけた人が解決する精神で、私が自分で検索システムを作ることにしました。

課題

まず、ティアフォー社内のデータ活用には3つの課題がありました。

  1. データを探せない:実証実験で得たデータは社内に大量に貯まっているが、開発者が目的のデータを簡単に見つけられない。
  2. 保管先がまばら:データは社内ストレージにアップロードされている一方、「どこ」に「いつ」の「どの」データが保管されているのか、統一して整理されていない。
  3. クラウドを使わない:データがクラウドに貯まっているのにローカルの開発端末にダウンロードして使おうとする。

他にも細かい課題は多々ありますが、データ活用を加速して機械学習開発を効率化するためにはデータが探しづらい状態です。ほしいデータを得るためには社内ドキュメントに書かれた実験記録(自動運転で走った場所、日時、天気など)を元に、社内ストレージからrosbagファイルを探してダウンロードし、開発者の端末でrosbagを解凍して必要なデータを探さなければいけませんでした。rosbagは1ファイルで1GB以上のサイズの中に、自動運転中の5、6分程度を記録しています。機械学習に必要なバラエティのあるデータを得るためには5、6分程度では足りません。漏れのないデータセットを作るためには、年間通して場所や明るさ、天気、被写体の位置、大きさ、カメラの方向、自動運転の速度などを組み合わせた多様なデータパターンを用意する必要があります。rosbagファイルをいちいちダウンロードして確認するのはあまりにも非効率です。

そこで、rosbagで収集したデータを検索できるシステムをクラウド上に作ることにしました。検索システムのゴールは社内に散らばったデータを同じインターフェイスで検索し、すぐ利用できるようにすることです。

検索システムをどのように作るか

検索システム開発で立てた目標の一つは、最速で作ってすぐ使える状態にすることです。データは貯まれば貯まるほど整理し使えるようにすることが難しくなります。データベースに登録するだけでも長時間かかりますし、バラエティが増えれば属性の整理やテーブル設計が困難になります。日々数TBの非構造化データが貯まる状況で放っておくことは良策ではありません。もちろんこれまでティアフォーでは検索システムがなくても開発できていたので、自動運転に検索システムは必要ないのかもしれません。しかし、ツールを導入することで仕事が楽になるかどうかは導入してみないとわかりません。検索の有用性(または無用性)を証明するため、スピーディに作ってPoCを進めることも目標としました。

スピード優先で開発するため、検索システムのソフトウェアスタックには下記のすぐ簡単に使えるものを選びました。 

全体像は以下のようになります。

f:id:cvusk:20210412083517p:plain

検索システム(α版)

ソフトウェアスタックと全体像からわかるとおり、主な開発言語はPythonです。検索のレスポンス速度を上げるならばJavaGolangを使ったほうが性能が改善されますが、短い期間で作り切ることを優先してPythonを選びました。ディープラーニングでデータに意味づけするためにはPythonがより扱いやすいという理由もあります。

インフラは以前構築したAmazon EKSのKubernetesクラスターを使っています。個人的にKubernetesが最も楽に使えるインフラになっているからです。データベースは私の好みでAurora PostgreSQLを使っています。

バックエンドAPIはFastAPIとSQL Alchemyで作っています。SQL (Relational) Databases - FastAPIで書かれているデータインターフェイス設計をそのまま参考にしています。Python側のデータスキーマはPydanticで定義することで、関数間のデータ受け渡しで型がわからなくなって実装に迷うことを避けました。型が定まっているとコーディングの手戻りが減ります。

バックエンドAPIPython+FastAPI+SQL Alchemyで手早く済ませたコンポーネントです。機能とデータが増えてパフォーマンス劣化してきたら、他の言語かgRPCで作り変えてORMも排除すると思います。

画面はStreamlitというPythonをベースにWeb画面を作ってデータを可視化するライブラリを使います。通常はHTML + JavaScriptで作る部分ですが、JavaScriptを書きたくないという個人的な理由でStreamlitを選びました。次はFlutterで作りたいと考えています。

以下の公式の動画のように、コンポーネントPythonコードで書くことで画面表示を定義できてとても便利です。

youtu.be

StreamlitであればPythonのグラフ作成ライブラリのMatplotlibやSeabornと組み合わせてデータのグラフを簡単に画面に表示できるため、良い選択だったと思います。 

データパイプラインとデータの意味づけではrosbagから画像を抽出し、データベースとストレージに登録します。抽出するデータは主に一般道を走る自動運転車のカメラで撮影する画像になります。

意味づけのために物体検知を使い、映っている被写体を判定しています。自動運転の機械学習では、認識のためにカメラに映っている歩行者や対向車、バイク、信号機等を検知します。学習済みの物体検知(EfficientDet)で簡易的に被写体を検知し、検索時に被写体の数で絞り込みできるようにしています。データの意味づけには物体検知以外にも、画像の明るさや逆光、緯度経度と住所、日時と場所をベースにした天気、気温による検索を可能にしています。

データパイプラインはKubernetes Jobで定期的にデータを収集、解凍し、意味づけしてデータベースに登録します。

意味づけのおかげで、たとえば以下のように「信号機」が映っている逆光画像を検索できるようにしています。

f:id:cvusk:20210412083656p:plain

うおっまぶしっ

別の例として、天気は「雪」で「自転車」が映っている画像です。

f:id:cvusk:20210412083737p:plain

雪の日の自転車

どのくらいの期間で作ったか

上記の構成と機能で検索システムを作りました。

ところどころ再開発するつもりで、さっさと使えるようにすることを優先して開発しています。以下に各コンポーネントの開発所要時間をまとめます。

  • Kubernetesは構築済みのクラスターにノードグループを追加し、各コンポーネントを動かすマニフェストを書きました。所要時間は1時間以内です。
  • バックエンドAPIとテーブル設計は最低限の機能と正規化にとどめ、2週間程度で開発しています。
  • 画面はStreamlitのおかげで3日くらいで作れました。マークダウンやテーブル、チェックボックス等Web画面に必要なコンポーネントPythonのコード数行で書くことができるため、とても楽に作れました。
  • データパイプラインはrosbagを解凍しデータベースとストレージに登録するコードをシングルプロセスで書いています。今後収集するデータ量が増えたら並列処理を検討します。データの意味づけは学習済みの物体検知モデルや簡易的な明るさ評価を活用しています。データの意味づけは開発者の要望を聞きながらオンデマンドで追加していったため所要時間はまちまちですが、1機能1週間程度です。

作ったものを適宜リリースしていく方式で、最初はバックエンドAPIと画面だけを用意し、オリジナルのrosbagデータを取得した日時や場所だけで検索できる状態で社内公開しました。ここまで大体3週間で第1弾をリリースしています。

そこから毎週1機能追加を目標としました。1週間ごとに物体検知、天気や気温による検索、画像の明るさや逆光検索を追加していきました。

他の作業をこなしながら検索システムを作っていたのですが、1ヶ月強で作ったわりには便利なものが作れたと思います。

f:id:cvusk:20210412083828p:plain

大体3週間ちょっとで作る

機能追加時は最初にモック画面と動画を作り、機能のイメージを利用者に共有してから実物を作る流れを取りました。検索の仕様を言葉で要件定義する作業を減らし、見えて使えるものを作ることにこだわっています("Working software over comprehensive documentation"または"Done is better than perfect")。これもスピード優先で作るための工夫です。

もっと検索できるようにしたいデータがあった

ここまで2021年3月中に開発とリリースを進めてきました。社内でデータ検索システムを作っていることを宣伝したおかげで、当初は存在を知らなかったデータや、課題が不明確だったデータ活用のシーンが判明しました。

その一例がアノテーションされたデータの検索です。機械学習で使うデータは外注してアノテーションしているのですが、アノテーションされたデータも画像とJSON形式でストレージに貯まったままで、データベース管理や検索可能化はされていません。アノテーションされたデータが貯まれば貯まるほど、学習条件に合うデータを探すのに人手と時間を要するようになります。そこで、アノテーションされたデータを元データの意味とアノテーションを組み合わせて検索する機能を開発しました。

信号機のアノテーションされた画像(以下は「黄色信号」の含まれる画像)を検索できるようにしています。

f:id:cvusk:20210412083926p:plain

小さくて見えないけど黄信号が写っています

この過程でアノテーションされたデータを整理し数量をまとめることになりました。そこで判明したことは、アノテーションは特定のカテゴリに偏っており、本来考慮すべきだが、アノテーションが少ないデータカテゴリが存在することです。日々扱っているデータでも、整理、集計しないと実態を知ることはできないということです。

今回は画像データをターゲットに検索データを作りましたが、その他にもポイントクラウドのデータ等、自動運転には欠かせないデータが残っています。また、社内ストレージから発掘された未知のデータを検索できるようにしたいという要望が出てきています。

個人的には「類似した状況」を条件に検索できるようにしたいと思っています。自動運転では運転中の周囲の状況が運転の判断を左右することがあります。左を並走している自動車が車線変更して先方に入ってきたり、先行している自動車が右折したり、道路幅が狭くなっていたり、周囲に建物があったり、林の中を走っていたり、自転車が並走していたり・・・。多種多様な状況を認識して自動運転するため、こうした「状況」を検索できるようにすることで、状況に応じた運転ロジックの開発を効率化することができると思います。画像や動画を入力にして似ている状況を検索する類似状況検索のようなシステムを作りたいです。

まとめ

データは貯めるだけでもシステム開発とコストが必要となりますが、貯めるだけでなく使える状態にするためにはデータの意味づけと検索が必須になります。データは容量が増えれば増えるほど整理し管理する難しさが増します。加えてデータを使えていない状態では、データを使えない不便さに気づけない事態が頻繁に発生します。データ検索のあり方はデータの使い方や目的次第で千差万別です。今回の開発では素早く作って直しながら、本当に必要なものを目指していく方針がうまくハマりました。

最後に1つだけご紹介させてください。

4月21日にティアフォー初の【Tier IV SUMMIT 2021】をオンラインで開催します!

参加は無料で自動運転の最前線を語ります!ぜひご登録ください!

tier4summit.com

自動運転の実証実験を「安全」に進めるためのリスクアセスメントの活用について

こんにちは、ティアフォーで安全に関して担当している新宅と申します。今回は、全国で進めている実証実験を「安全」に進めるためのリスクアセスメントの活用についてご紹介します。実証実験に限らず、安全を担保することは自動運転車両を開発する上で必要不可欠であり、社会受容性を高めていくための最も重要な要素の1つとなります。

なお、ティアフォーでは「自動運転の民主化」をともに実現していくエンジニアを募集しています。今回ご紹介する安全だけではなく、様々なバックグラウンドをお持ちの方と開発を進めていく必要があります。下記ページから募集職種のリストをご覧いただき、興味を持った方はぜひお気軽にご連絡ください!

安全に関わる最近のデータや動き

リスクアセスメントについてお話する前に、安全に関わる最近のデータや動きについてご紹介します。

年間の交通事故死者数

2021年1月4日、警察庁が2020年の交通事故死者数は前年よりも376人少ない2,839人だったことを発表しました。この数字は、4年連続で戦後最小を更新し、かつ初めて3,000人を下回ったとのことです。

f:id:yst4:20210322103308p:plain

出典:警察庁ウェブサイト

この数字を見て、本記事を読んでいただいている皆さまは多いと思うでしょうか?それとも戦後最小を更新し続けているのだから少ないと思うでしょうか?おそらく皆さまそれぞれの感覚があるかと思います。しかし、1つはっきりしていることは、交通事故の数が限りなくゼロに近づけば同時に交通事故死者数も減っていきます。自動運転が普及することの意義の1つには、交通事故の数が限りなくゼロに近づいていくことにあります。一方で、仮に全て自動運転車になれば事故の数はゼロになるのでしょうか?不慮の事態を考慮すると、残念ながら自動運転が実現しても事故の数がゼロになるとは言えません。それでも、事故の数を限りなくゼロにすることを目指すべく、自動運転車の開発を進めています。これをエンジニアリング観点で言えば、許容可能なリスクの発生確率や損害の程度を定量的に定め、総合的に許容可能な範囲を決めながら安全な設計を行っています。

自動走行の実現に向けた国の直近の動き

 3月はじめに、「自動走行ビジネス検討会」から「自動走行の実現及び普及に向けた
取組報告と方針」Version5.0の概要案が示されたのでこちらも簡単にご紹介します。

f:id:yst4:20210322162229p:plain

出典:経済産業省・自動走行ビジネス検討会ウェブサイト

特に(1)では、本記事の主題である安全に関する記載があります。ここでは、米国NHTSA(運輸省道路交通安全局)の項目を参照しながら日本版セーフティレポート(仮称)と称して情報発信することの重要性を示し、さらに「走行環境・運行条件で想定されるリスクを網羅的に評価し、その安全対策をあらかじめ十分に行う、セーフティアセスメント(仮称)がきわめて重要」*1と判断しており、今年度を目途にセーフティアセスメント(仮称)に係るガイドラインが作成される予定となっています。また本概要案では、2025年度までに40か所以上で自動運転レベル4の無人自動運転サービスを実現する旨の記載もあり、今後5年以内に自動運転レベル4の普及が進んでいくことが考えられます。

米国NHTSAの項目について興味のある方は、https://www.nhtsa.gov/document/automated-driving-systems-20-voluntary-guidanceをご参照ください。また、エンジニアリング観点での「安全」については、安全への取り組み:②自動運転の安心・安全について - Tier IV Tech Blogでご紹介しているので是非ご覧ください。

ティアフォーの実証実験の安全をリスクアセスメントで担保

さて、昨年の交通事故死者数から自動運転車開発の意義の1つを、そして国でも安全に自動運転車を普及・実現させていくために様々な動きがあることを見てきました。さらに「自動走行ビジネス検討会」では、セーフティアセスメント(仮称)に係るガイドライン作成が予定されている旨お話しましたが、ここからは全国で進めている実証実験を「安全」に進めるためにティアフォーが既に活用している「リスクアセスメント」についてご紹介します。現在ティアフォーでは、異なる観点から「ODDアセスメント」と「走行ルートサーベイ」という2種類の「リスクアセスメント」を行っています。

※ODD:「Operational Design Domain」の略で、「運行設計領域」と呼ばれる自動運転システムが作動する前提となる走行環境条件のことです。

ODDアセスメントとは?

まず、なぜODDアセスメントが必要なのでしょうか?ティアフォーでは下記のような理由からODDアセスメントを実施しています。

  • 走行ルートを決める際、車両を走行させることなく自動走行の可否を事前に把握するため
  • 走行限界を超えている箇所について、事前の対策を行うことが出来るため
  • アセスメントの結果をドライバーやオペレータに周知することで、より安心・安全な走行を実現するため
  • アセスメントの結果を活用し、運用面でのリスクを可視化するため

実施にあたっては地図・走行経路、ODDユースケース、Pilot.Auto性能仕様、シミュレータ実施結果を使用します。それでは、ここからODDアセスメントの実施方法をご説明します。

ODDアセスメントの実施方法
  1. コースの細分化
    下図のように、コースをODDカテゴリに分割してロードセクション番号を付加し、ロードセクション単位で評価を実施していきます。

    f:id:yst4:20210323160102p:plain

  2. 各ロードセクションごとにユースケース定義を割当
    次に、各ロードセクションにおけるユースケース分析を行います。例えば、下図のように大枠として発車、同一車線内走行、右折、左折などとし、そこから同一車線内走行-通常走行(対向車線なし)や、左折-T字路交差点(信号なし)などのユースケースに当てはめていきます。

    f:id:yst4:20210324104932p:plain

  3. シナリオによる検証確認
    ロードセクションごとにユースケース定義を割り当てたら、ロードセクションのユースケースにおけるシナリオのシミュレーション・実車での検証結果の確認を行います。
  4. 各ロードセクションの評価
    そして各ロードセクションの評価をしていきます。下図のように、これまで行ってきたシナリオ総数とそのシナリオを検証した結果のNG数の割合である「性能限界率」を算出します。さらにそれだけでなく、「ODD範囲(=人的介入が必要なのか、もしくは誘導員を配置するなど運用面の対策やそもそもソフトウェアの調整が必要なのかといったもの)」、「限界要因(=性能限界に達している要因が自車両によるものなのか、もしくはカットインしてきた車両や工事中の場所を走行する場合など、外部のものなのかといった要因をより細かく見ていくもの)」をそれぞれ4段階に分けた点数付けを行い、合計点数を算出します。

    f:id:yst4:20210323162643p:plain
    合計点数を算出したら、下表にしたがってリスク評価を行います。点数が高ければよりリスクが高いことを示しており、運行計画の変更を検討していきます。

    f:id:yst4:20210323172456p:plain

  5. 評価結果からリスク軽減策の検討・実施

    最後に、4.で出てきた結果から安全余裕を確保するよう自動運転計画を修正し、リスクの軽減を図ります。リスク軽減策には、例えば車速の低減やより車間距離を取るなど、性能限界内での運用可能な手段を講じる場合もあれば、オーバーライド(=自動運転から手動運転に切り替えること)により、ルートの一部は必ず手動運転とすることで安全を担保するといった対策があります。下図がその一例となります。

    f:id:yst4:20210324104424p:plain

このような一連の流れを経て、ODDアセスメント行っています。リスク軽減策を実施した場合には、改めて評価を行うことでより安全に実証実験が進められるようにしています。

走行ルートサーベイとは?

次に、走行ルートサーベイについてご紹介します。既にご紹介したODDアセスメントと似たようなものではありますが、アプローチの仕方でいくつか異なる点が存在します。異なる2点を中心に走行ルートサーベイについてお話します。

走行環境条件の設定のパターン化参照モデルの活用

これは、「自動走行に係る官民協議会」で日本経済再生総合事務局が策定したもので、「無人自動運転移動サービスの導入を検討している企業・団体等向けに、導入の検討段階において参考となる導入地域の環境や条件についてパターンを整理した」*2ものになっています。無人自動運転移動サービスの実施に向けて「共通言語」として使用することで、より導入の検討が進むことを意図しています。

f:id:yst4:20210322172341p:plain

出典:https://www.kantei.go.jp/jp/singi/keizaisaisei/jidousoukou/index.html

このモデルを参照し、順番に実証実験実施予定地域の走行ルートサーベイを行います。例えば、1番目の時間については、実施予定地域・場所の日の出・日の入り時刻を調べることで、実証実験を安全に行うための開始・終了時間設定の参考にします。また8番目の交通量については、自治体から情報提供いただき、その道路の交通量を可視化(例:〇〇台/24時間など)することで、安全に実証実験を進めていくルートを策定する参考にします。自動運転の技術力を上げていくため、時にはチャレンジングなルートを策定することもありますが、先ほどご紹介したODDアセスメントと合わせて確認していくことで、チャレンジングでありながらも、安全を最優先して実証実験を行っています。

保険会社の事故データの活用

さらに、保険会社の事故データを活用して「事故多発地点」と「うっかり運転地点」を本サーベイの中に含めています。「過去5年間で3回以上事故が起こった地点」を「事故多発地点」、「直近半年間で一時不停止、踏切不停止、速度超過、一方通行違反が起こった地点」を「うっかり運転地点」と定義して「リスクの高い地点」を割り出しています。ご参考までに、「うっかり運転地点」の例を下図に示しています。一時不停止や一方通行違反があることが見て取れます。

f:id:yst4:20210324103510p:plain

実際に起こった事故だけでなく、「うっかり運転地点」まで加えている理由ですが、「ハインリッヒの法則」では「1つの重大事故の背後には29の軽微な事故があり、その背景には300の異常(ヒヤリ・ハット)が存在する」*3と言われており、ヒヤリハットをきちんと分析し、対策を立てることが安全を担保するのに重要であると言われています。これを今回の「うっかり運転地点」に置き換えると、「うっかり運転地点」が発生している場所は「重大事故が起こる可能性がある」ことを示していると言えるので、これらの情報も本サーベイに活用しています。なお、事故データの活用にあたり、ティアフォーがともに「自動運転の民主化」を実現するために連携している損害保険ジャパン株式会社様から提供いただいています。このように、ティアフォーだけでは提供出来ない情報を含めてご案内することで、より安全に対する優先度が高いことをおわかりいただけるのではないでしょうか。

まとめ

今回は、「安全」というキーワードをもとに直近のデータや国の動きをお話し、安全を実現するための「リスクアセスメント」についてご紹介しました。ティアフォーでは、安全を担保しながら自動運転の社会受容性を高めていけるよう引き続き開発を進めていきます。

Autowareにおける狭路走行のための走行経路計画

こんにちは。ティアフォーで自動運転システムを開発している渡邉です。
今回は、Autowareで使われている車両の走行経路の計画について簡単にご紹介したいと思います。

なお、ティアフォーでは「自動運転の民主化」をともに実現していくエンジニアを募集しております。今回ご紹介するような計画・制御に関わる分野だけでなく、様々なバックグラウンドをお持ちの方と開発を進めていく必要があります。下記ページから募集職種のリストをご覧いただき、興味を持った方はぜひお気軽にご連絡ください!

tier4.jp

走行経路計画とは

走行経路計画とは、読んで字のごとく、車両が走行してほしい経路を計画することです。走行経路を計画するときには、道路の形状や道路上の障害物といったものを考慮しながら、車両が実際に走行できるように目標経路を計画する必要があります。走行経路の計画で必要な要件としては、次のようなものが考えられます。

  • 走行可能なエリアからはみ出さないこと
  • 障害物に衝突しないこと
  • 必要以上の蛇行といった不自然な挙動をしないこと

人間が運転している場合、無意識のうちに走行したい経路を考えながらステアを切るといった操作をすると思います。しかし、自動運転では走行する経路を明示的に算出する必要があり、どのような曲線を描けば車両は安全に走行できるのか、を考える必要があります。また、車両が通れるかどうかの狭い道を通るようなシーンでは、車両の挙動を考えながら通過できる経路を考える必要があります。このようなシーンは人間でも難しい場合があると思います。

今回は、狭い道を通るシーンを例に取り上げながら、Autowareの走行経路の計画方法についてご紹介したいと思います。

狭路走行の難しさ

狭い道を走行するシーンでは、単純に道路の中央を走行するような経路を計画すれば良いわけではありません。なぜならば、車両の大きさによってはうまく曲がりきれずに走行可能なエリアをはみ出してしまうからです。狭い道を走行するために考慮しなければならないことは、大きく分けて

  • 道路の道幅、走行可能なエリア
  • 車両の形状
  • 車両の運動

の3つになります。道路や車両の形状を考慮する必要があることは明らかだと思いますが、走行経路を決める上では車両の運動の特性も考える必要があります。例えば一般的な4輪の自動車の場合、その場で旋回して姿勢を変えるといった運動はできません。自動車の向きを変えたいときにはステアを切って前後に走行することで徐々に自動車の向きを変える必要があります。このように、一般に自動車は空間を自由に動くことはできず、自動車が動くことが可能な範囲は限定されています。そのため、自動車の運動の制限を考慮しながら走行経路の計画を行う必要があります。

車両運動を考慮した走行経路計画

車両運動を考慮した走行計画を考えるためには、車両の運動がどのように記述されるかを事前に求めておく必要があります。Autowareでは、ある速度で走行しながらステアを切ったときに、そのステア角に対する車両の位置・姿勢の変化量を数式ベースで取り扱っています。このように、ある入力に対する車両挙動の変化を表す数式を車両運動の数理モデルと呼びます。車両運動の数理モデルを用いると、与えられたステア角に対して車両が走行する経路をシミュレートすることができるようになります。これにより、車両が走行可能な範囲を考慮することができるようになります。

走行経路を計画するとき、多くの場合通過可能なパターンは多数存在します。走行経路をひとつ決めるためには、何かの評価基準を与えて一番良いものを選ぶということを考えます。今回は、必要以上の蛇行や不自然な挙動を抑えたいため、ステアの角度や角速度、角加速度といったものをできる限り小さくするような走行経路を選択すると良さそうです。

ここまでをまとめると、走行経路を計画するための目標と条件は次のようになります。 

目標:

  1. ステアを切る角度・角速度・角加速度をできる限り小さくする
  2. 道路の中央からできる限り離れないようにする

条件:

  1. 車両は数理モデルで記述された運動の法則に従って動く
  2. 車両は走行可能なエリアからはみ出さない 

これらの目標や条件が数式として定式化できれば、定量的に走行経路の計画を行うことができるようになります。このように、目標や条件を数式として定式化して何かを計画する問題を数理最適化問題と呼びます。数理最適化問題は工学の分野だけでなく金融・経済など広い分野で登場するもので、その解き方は広く研究・開発されています。数理最適化問題の形で定式化することができれば、適切なソルバを用いることで条件を満たしつつできるだけ目標に近い解を得ることができます。上述した目標1にあるステア角度・角速度・角加速度や目標2にある道路中央からの距離はその数値そのものを扱えば良く、条件1の車両運動については数理モデルとして定式化されています。したがって、走行経路計画を数理最適化問題として扱うためには、条件2の車両が走行可能なエリアからはみ出さないことを数式的に表現する必要があります。

車両形状と走行可能領域

車両が走行可能エリア内にいるかどうかの判定は様々な表現方法がありますが、表現の仕方によっては数理最適化問題の形式が難しくなってしまう場合があります。 そのため、Autowareではできる限り解きやすい形式で走行可能領域に関する条件を与えられるように工夫しています。

Autowareでは、車両の中心線(図1の破線)と道路の中心線(図1の赤線)との距離を計算し、その距離と車両幅の半分の和が道路中心線から道路の端までの距離より小さいときに車両が走行可能エリア内にいると判定します。この判定を車両の前端・中央・後端に対して行うことで、車両全体が走行可能領域の中にいるかどうかを判定しています。

この判定方法を用いることで、走行経路の計画問題を二次計画問題と呼ばれるクラスの数理最適化問題へ帰着することができます。二次計画問題は数理最適化のなかでも頻出の形式で、オープンソースのソルバもあります。Autowareでは二次計画問題を解くためにOSQP*1というソルバを利用しています。

f:id:fwatanabe-t4:20210323154240p:plain

図1:走行可能領域の表現方法

Autowareでの適用例

では、実際に狭い道を走行している例をご紹介します。ここでは、S字カーブを走行するシーンを想定して走行経路を計画してみましょう。計画した結果を図2に示します。図中の赤線が道路中心線、白線が実際に計画された(後方車輪軸中心が通る)走行経路、黄色の四角い枠が白線に沿って走行したときの車両の外形(footprint)になります。まず、カーブ入口で中心に沿って走行すると走行可能エリアを逸脱してしまうため、カーブ内側を走行するような経路が引かれていることがわかります。また、S字カーブ内では車両が走行可能エリアを逸脱しないように計画されているだけでなく、中心線よりやや内側を走行して走行経路の曲率をできるだけ小さくしており、人間が見ても自然な経路が計画されていると思います。この結果から、走行可能エリアの逸脱を避けつつ、必要以上に大きくステアを動かさないような走行経路の計画が達成できていることがわかります。

f:id:fwatanabe-t4:20210326131049p:plain

図2:S字カーブの走行経路計画結果

まとめ

今回は、狭路走行を例に取り上げて走行経路の計画方法についてご紹介しました。Autowareでは、このアルゴリズムの導入により走行できるようになったシーンが増えました。しかし、経路計画にはまだ様々な課題が残っており、引き続き有効な手法の研究および開発が必要だと考えております。経路計画に限らず自動運転には難しい課題が多数ありますが、問題解決したときの喜びは大きいです。自動運転技術の発展に興味のある方は、ぜひ本記事冒頭のリンクからコンタクトしていただければと思います!

The Challenges of Making Decisions in Autonomous Driving

Autonomously controlling a car is challenging and requires solving a number of subproblems. If perception, which understands information from sensors, corresponds to the eyes or our system while control, which turns the wheel and adjusts the velocity, corresponds to the body having a physical impact on the car, then decision making is the brain. It must connect what is perceived with how the car is controlled.

In this blog post, we discuss the challenges in designing the brain of an autonomous car that can drive safely in a variety of situations. First, we introduce the scope and motivation for decision making before presenting how it is currently implemented in Autoware. We then talk about the trends observed in recent autonomous driving conferences and briefly introduce a few works that propose interesting new directions of research.

f:id:TierIV:20210322212536p:plain

Figure 1 - Typical sense-plan-act paradigm for autonomous agents

By the way, at Tier Ⅳ, we are looking for various engineers and researchers to ensure the safety and security of autonomous driving and to realise sharing technology for safe intelligent vehicles. Anyone who is interested in joining us, please contact us from the web page below. 

tier4.jp

Introduction to Decision Making

We spend our life making decisions, carefully weighing the pros and cons of multiple options before choosing the one we deem best. Some decisions are easy to make, while others can involve more parameters than can be considered by a single human. Many works in artificial intelligence are concerned with such difficult problems. From deciding the best path in a graph, like the famous Dijkstra or A* algorithms, to finding the best sequence of actions for an autonomous robot.

For autonomous vehicles, decision making usually deals with high level decisions and can also be called behavior planning, high-level planning, or tactical decision making. These high level decisions will then be used by a motion planner to create the trajectory to be executed by the controller. Other approaches consider planning as a whole, directly outputting the trajectory to execute. Finally, end-to-end approaches attempt to perform all functions in a single module by using machine learning techniques trained directly on sensor inputs. While planning is one of the functions handled by end-to-end techniques, we will not discuss them in this blog post as they tend to make decision making indistinguishable from perception and control.

The common challenge unique to decision making systems comes from the open-endedness of the problem, even for humans. For perception, a human can easily identify the different objects in an image. For control, a human can follow a trajectory without too many issues. But for decision making, two drivers might make very different decisions in the same situation and it is almost never clear what is the best decision at any given time. The goals of an autonomous car at least seem clear: reach the destination, avoid collisions, respect the regulations, and offer a comfortable ride. The challenge then comes from finding decisions that best comply with these goals. The famous “freezing robot problem” illustrates this well: once a situation becomes too complex, not doing anything becomes the only safe decision to make and the robot “freezes”. Similarly, the best way for a car to avoid collisions is not to drive. A decision system must thus often deal with conflicting goals. Challenges also arise from uncertainties in the perception and in the other agents. Sensors are not perfect and perception systems can make mistakes, which needs to be carefully taken into account as making decisions based on wrong information can have catastrophic consequences. Moreover, cars or pedestrians around the autonomous vehicle can act in unpredictable ways, adding to the uncertainty. Dedicated prediction modules are sometimes used and focus on generating predictions for other agents. The final challenge we want to mention here comes from dealing with occluded space. It is common while driving not to be able to perfectly perceive the space around the car, especially in urban areas. We might be navigating small streets with sharp corners or obstacles might be blocking the view (parked trucks for example), potentially hiding an incoming vehicle.

f:id:TierIV:20210322212939p:plain

Figure 2 - Standard autonomous driving architecture

Schwarting, Wilko, Javier Alonso-Mora, and Daniela Rus. "Planning and decision-making for autonomous vehicles." Annual Review of Control, Robotics, and Autonomous Systems (2018)

To summarize, we are concerned with the problem of making decisions on the actions of the car based on perceptions from sensors and the main challenging points are:

  • conflicting goals that require trade-offs;
  • uncertainties from perception, localization, and from the other agents;
  • occluded parts of the environment.

In the rest of this post we will try to introduce methods to make decisions and to tackle the aforementioned challenges.

Decision Making in Autoware

f:id:TierIV:20210322213104p:plain

Figure 3 - Autoware architecture

Autoware is an open source project providing a full stack software platform for autonomous driving including modules for perception, control and decision making. In the spirit of open source, its architecture is made to have each function be an independent module, making it easy to add, remove, or modify functionalities based on everyone’s own needs.

In this architecture, decision making occurs at two stages. At the Planning stage, various planning algorithms such as A* or the Model Predictive Control are used to plan and modify trajectories. Before that, at the Decision stage, the output of perception modules is used to select which planning algorithms to use for the current situation. For example, it can be decided that the car should perform a parking maneuver, in which case a specific parking module will be used (Motion Planning Under Various Constraints - Autonomous Parking Case - Tier IV Tech Blog). This Decision stage is implemented using a Finite State Machine (FSM), a common model used in rule-based systems. A state machine is basically a graph representing possible states of the system as nodes and the conditions to transition between these states as edges. State machines are easily designed by humans and offer decisions that are easy to understand and explain, which is very desirable in autonomous driving systems.

While the decision and planning modules of Autoware offer state-of-the-art driving in nominal cases, they are not well suited to deal with uncertainties or occluded space. There are no classical approaches to deal with these challenges but they are studied by a lot of ongoing research, which we will discuss in the next part.

Current Trends

We now introduce some of the most popular approaches for decision making seen in recent autonomous driving conferences (the IEEE Intelligent Vehicles Symposium and the International Conference on Intelligent Transportation Systems).

Deep Reinforcement Learning (DRL) is an application of deep neural networks to the Reinforcement Learning framework where the goal is to learn what action works best at each state of the system, corresponding to a policy Π(s) = a representing the decision a to take at state s. This usually requires learning a function Q(s,a) which estimates the reward we can obtain when performing action a at state s. In simple environments, this function can be computed by completely exploring the search space but when the state and action spaces become too large, like in autonomous driving problems, more advanced techniques must be used. DRL leverages the power of deep neural networks to approximate the function Q(s,a), allowing the use of RL in very complex situations. This requires training on a large number of example situations, which are usually obtained using a simulator. The biggest issue with using these techniques on real vehicles is the difficulty to guarantee the safety of the resulting policy Π(s). With large state spaces, it is difficult to ensure that there does not exist a state where the policy will take an unsafe action.

Mixed-Integer Programming (MIP) is an optimization technique which maximizes a linear function of variables that are under some constraints. Some of these variables must be restricted to integer values (e.g., 0 or 1) and can be used to represent decisions (e.g., whether a lane change is performed or not). The problem of an autonomous vehicle following a route can be represented with MIP by including considerations like collisions and rules of the road as constraints. Using commonly available solvers, the problem can then be solved for an optimal solution, corresponding to the best values to assign to the variables while satisfying the constraints. Many approaches applying MIP to autonomous driving have been proposed and while they can solve many complex situations, they cannot handle uncertain or partial information and require accurate models of the agents and of the environment, making them difficult to apply to real-life situations.

Monte-Carlo Tree Search (MCTS) is an algorithm from game-theory which is used to model multiplayer games. It is most famous for being used in AlphaGo, the first program able to defeat a human professional at the game of Go. The MCTS algorithm assumes a turn-based game where agents act in succession and builds a tree where each node is a state and where a child is the expected result from an action. If complete, the tree represents the whole space of possible future states of the game. Because building a complete tree is impossible for most real problems, algorithms use trial-and-error, similar to reinforcement learning, to perform sequences of actions before simulating the resulting outcome. This approach has the advantage of considering the actions of other agents in relation to our own decisions, making it useful in collaborative scenarios where the autonomous vehicle must negotiate with other vehicles (e.g., in highway merges). The main issue with MCTS is their computational complexity. As the tree must be sufficiently explored in order to confidently choose actions, it can be hard to use in real-time situations.

In the next section, we will present some recent works related to each of these trends.

Interesting Works

Graph Neural Networks and Reinforcement Learning for Behavior Generation in Semantic Environments

Patrick Hart(1) and Alois Knoll(2)

1- fortiss GmbH, An-Institut Technische Universität München, Munich, Germany.

2- Chair of Robotics, Artificial Intelligence and Real-time Systems, Technische Universität München, Munich, Germany.

IEEE Intelligent Vehicles Symposium (2020) https://arxiv.org/abs/2006.12576 

f:id:TierIV:20210322213643p:plain

Figure 4 - Vehicles of a scene represented as a graph

An issue with reinforcement learning is that their input is usually a vector of fixed size. This causes issues when tackling situations with varying numbers of other agents. This paper proposes to use Graphic Neural Networks (GNNs) to learn the policy of a reinforcement learning problem. GNNs are a neural network architecture that uses graphs as inputs instead of vectors, offering a solution that is more flexible and more general than previous works.

The graph used in this paper represents vehicles with the nodes containing state information (coordinates and velocities) and the edges containing the distance between two vehicles. Experiments on a highway lane change scenario are used to compare the use of GNNs against the use of traditional neural networks. It shows that while results are similar in situations close to the ones used at training time, the use of GNNs generalizes much better to variations in the input without a significant loss of performance.

Autonomous Vehicle Decision-Making and Monitoring based on Signal Temporal Logic and Mixed-Integer Programming

Yunus Emre Sahin, Rien Quirynen, and Stefano Di Cairano

MITSUBISHI ELECTRIC RESEARCH LABORATORIES

American Control Conference (2020) https://www.merl.com/publications/TR2020-095

f:id:TierIV:20210322213823p:plain

Figure 5 - (a) modules targeted by this work  (b) illustration of the MIP solution space

This work uses Mixed-Integer Programming to represent the problem of finding intermediate waypoints along a given route. The interesting parts of this work are its use of Signal Temporal Logic (STL) to represent some of the constraints, and a monitoring system that can identify if the environment changes in ways that deviate from the models used.

Temporal Logic is a language used to write rules which include some time information. In this work for example, intersections are regulated by a first-in-first-out rule, which in STL is written as f:id:mclement:20210323100344p:plain, which means “the car must not be at the intersection until it is clear”. The paper proposes an efficient way to encode STL rules as constraints, reducing the solution space of the problem (like in Figure 5b) and ensuring that they are satisfied by the solution of the MIP.

The paper tackles a simulated scenario with intersections and curved roads and shows that their solution can navigate a number of situations in real-time. They also show that their monitoring system can correctly detect unexpected changes in the environment, for example when another vehicle suddenly brakes. It is then possible to fall back to another algorithm or to solve the MIP problem again but with the new information added. This alleviates the main issue of MIP which is to depend on very good models of the environment as it is now possible to detect when these models make a mistake.

Accelerating Cooperative Planning for Automated Vehicles with Learned Heuristics and Monte Carlo Tree Search

Karl Kurzer(1), Marcus Fechner(1), and J. Marius Zöllner(1,2)

1- Karlsruhe Institute of Technology, Karlsruhe, Germany

2- FZI Research Center for Information Technology, Karlsruhe, Germany

IEEE Intelligent Vehicles Symposium (2020) https://arxiv.org/abs/2002.00497

f:id:TierIV:20210322214141p:plain

Figure 6 - (a) Use of the Gaussian mixture to select actions in the MCTS (b) Gaussian mixture

(b) taken from https://angusturner.github.io/generative_models/2017/11/03/pytorch-gaussian-mixture-model.html

As discussed in the previous section, MCTS are great to model interactions with other agents but are very hard to sufficiently explore. In order to make exploring the tree more efficient, this paper learns Gaussian mixture models that associate each state (i.e., node of the MCTS) some probabilities on the action space. These probabilities are used to guide the exploration of the tree.

A Gaussian mixture is a combination of several Gaussian probability distributions, allowing multiple peaks with different means and covariances (Figure 6b). In this paper, the actions modeled by these probabilities are pairs of changes in longitudinal velocity and in lateral position. This means that for a given state, the mixture must contain as many peaks as there are possible maneuvers such that their mean will correspond to the typical trajectory for that maneuver. On the highway for example, mixtures with 2 components can be used to learn the 2 typical maneuvers of keeping or changing lanes (Figure 6a).

This technique is interesting to make the exploration faster but the experiments of the paper do not show any performance improvements once enough exploring time is given. Having to fix the number of components can also be an issue when trying to generalize to various situations.

Conclusion

Decision making for autonomous driving remains a very important topic of research with many exciting directions currently being investigated.

Researchers and engineers at Tier IV are working to bring the results of this progress to Autoware as well as to develop new techniques to help make autonomous driving safer and smarter.

Intelのad-rss-libを大解剖!RSSのライブラリのアーキテクチャと中身を解説する!

こんにちは、ティアフォーでAutowareのSimulator開発を担当している片岡と申します。

弊社ではSimulationや実車評価を通して様々な側面からAutowareの性能評価と開発へのフィードバックを行っております。

今回はその一環でIntelRSS(Responsibility Sensitive Safety)という安全評価基準のライブラリを読み解きましたのでその中身について解説したいと思います。

また、ティアフォーでは「自動運転の民主化」をともに実現していく、Simulation EngineerやSafety Engineerを募集しています。

自動運転を実現するためには、極めて幅広い分野に対する研究開発や安全担保への取り組みを進めていく必要があります。もしご興味があれば以下のページからコンタクトいただければと思います。

tier4.jp

 Intel RSSとは?

youtu.be

Intel RSSIntel (Mobileye) が提唱している自動運転車両の安全性を評価する基準です。評価基準の詳細な計算ロジックが記述されている論文はarXivにおいて公開されております。

arxiv.org

RSSの概要

RSSはHD Mapと周囲のオブジェクトの動きから、自動運転車が取ってよい動作コマンド(縦方向、横方向加速度)の範囲を計算します。

公道において自動運転車両が出会う状況を数式で表現することで自動運転車両が取った行動の危険度を数値化することが可能になります。この機能は「この危険な状況は道路上にいる誰のせいで発生したのか」を表現することができるため、公道走行ログから危険なシナリオを抽出したり、様々なパラメータで実行されたSimulation Testの結果を分析するのに使うことができます。

このロジックは本質的にはAutoware等の自動運転システムに含まれるプランナーと等価なものであり、後述するad-rss-libのサポート対象からは外れているものの、障害物回避等の動きを行うことも可能なようです。

下の動画(CARLA 0.9.10のNew Feature紹介動画)においては実際にRSSに基づいて計算された制御入力を使ってUnstructuredな環境での障害物回避が行われています。

youtu.be

RSSは内部に状態機械を持っており、様々なシチュエーションに対して適切な評価関数を選択することで自動運転システムからの出力が適切かを評価することができます。

f:id:masaya-kataoka:20210315164514p:plain

https://intel.github.io/ad-rss-lib/images/ad-rss-map-situation_analysis.png

RSSと自動運転システムのIntegration

f:id:masaya-kataoka:20210315164013p:plain

https://intel.github.io/ad-rss-lib/images/ad-rss-lib-Integrate_Into_Sense-Plan-Act.png

RSSは自動運転のサブシステムとして実装することを考慮して設計されており、認識結果からワールドモデルを構築して自動運転システムからの出力を制限するといった使い方が想定されています。

また、後述するad-rss-libは様々な外部ツールとインテグレーションされており、インテグレーションもやりやすい仕様になっていることが伺えます。

ad-rss-lib

今回紹介しますのは、この論文に有る内容をC++で実装したOSSです。

ライセンスはLGPL2.1となっていて、Ubuntu16.04、18.04、20.04で動作します。

github.com

CARLAやBaiduのApolloといった自動運転業界で有名なOSSでもこのライブラリはインテグレーションされており、その使いやすさと注目度の高さが伺えます。

現在のversion(4.4.0)においては以下の内容がサポートされています。

  • 複数のマップバージョンをサポート(Map Integration)
  • 構造化されていない道路や歩道も扱える
  • 複数車線の道路、すなわち縦方向および横方向の安全距離と適切な対応の決定
  • 交差点、つまり、異なるジオメトリの2つ以上のルート、ルートの交差点のルール、優先権/通行権、および縦方向と横方向の適切な応答の決定

逆にサポート範囲外となっているのは以下の内容になります。

  • オクルージョンの考慮
  • 回避動作を生成するロジック
  • 横方向の衝突がない交差点の対応

ad-rss-libのパッケージ構成

ad-rss-libはC++とCMakeで書かれており、PythonのC APIを使用してPython Bindingsも製作されています。

ad-rss-libのコアライブラリは以下の2つのディレクトリに格納されています。

https://github.com/intel/ad-rss-lib/tree/master/ad_rss

https://github.com/intel/ad-rss-lib/tree/master/ad_rss_map_integration

ad_rssの中身

ad_rssは以下のようなディレクトリ構造になっています。

ad_rss

├── CMakeLists.txt
├── doc
├── generated
├── gtest-cmake.txt.in
├── impl

│   ├── ad_rss.cmake
│   ├── include
│   ├── src

│   │   ├── core
│   │   ├── situation
│   │   ├── unstructured
│   │   └── world
│   └── test
└── python

 CMakeLists.txtはCMakeの設定ファイルであり、ビルド手順が記述されています。

docディレクトリにはDoxygenの設定ファイルが入っており、こちらのドキュメントを生成するのに使われています。

intel.github.io

generatedディレクトリには縦方向の反応時間といったRSSの計算で使われるデータ型を文字列等から作成するためのユーティリティが含まれています。

implディレクトリは更に4つのディレクトリに整理されており、それぞれ

  • core : 下に記したディレクトリ群の中の関数を使うための関数群を収録
  • situation:各situationにおいて実行されるロジックを収録
  • unstructured:構造化されていないシーンでのロジックを収録
  • world:レーン座標系への変換や現在のsituationの認識等のロジックを収録

に分割されています。

レーン座標系への変換等は特に加速度の連続性を維持するためにかなり工夫がなされているようです。このあたりの綺麗に実装するのが難しいロジックをオープンにしてくれるのは非常にありがたいですね!

f:id:masaya-kataoka:20210315165258p:plain

https://intel.github.io/ad-rss-lib/images/lanes_with_different_width.svg

PythonディレクトリにはPythonのC APIを利用してPythonからad-rss-libのロジックをラップして使えるようにするための関数が入っています。

ad_rss_map_integrationの中身

ad_rss_map_integrationは以下のようなディレクトリ構造になっています。

ad_rss_map_integration

├── CMakeLists.txt
├── doc
├── generated
├── impl
└── python

implディレクトリ以外はad_rssディレクトリと同様のコードが入っています。

implにはRSSの内部で使われているHD Mapに関連するコードが収録されています。

 ad-rss-libの使い方

ApolloにおけるRSSのIntegration

Apolloは自動運転車の行動計画部分にRSSのモジュールをインテグレーションしています。

japan.cnet.com

具体的な実装箇所は以下のものになります。

github.com

このドキュメントによるとApolloのEMプランナー(Apolloのプランニングアルゴリズム)は3つのカテゴリーのタスクが交錯する反復的なアプローチをとっており、この中のタスクの一種としてRSSのタスクが実装されているようです。

https://github.com/ApolloAuto/apollo/raw/master/docs/specs/images/class_architecture_planning/image008.png

f:id:masaya-kataoka:20210315130447p:plain

https://raw.githubusercontent.com/intel/ad-rss-lib/master/doc/images/apollo_integration.png

実装されている内容としては

  1. 認識系からきた情報とHD Mapの情報を合わせてWorld Modelを構築
  2. World Modelから現在のSituationを判断
  3. RSSの計算式に基づいて適切な応答を計算
  4. 実際の制御入力における適切な応答を計算
  5. 安全な車間距離を取っているかを評価

というロジックになります。

CARLAにおけるRSSのIntegration

CARLAにおいてはRSS SensorとRSS Restrictorの2種類のIntegrationがされています。

f:id:masaya-kataoka:20210315130404p:plain

https://carla.readthedocs.io/en/latest/img/rss_carla_integration_architecture.png

carla.readthedocs.io

RSS Sensor

RSS Sensorは自車両の制御コマンドがRSSの基準を満たしているかをチェックするセンサーです。

  1. SimulatorのAPIを使用してActorの一覧を取得
  2. RSSに基づいて適切な制御コマンドの範囲を計算、その値に自車両の入力が入っているかを確認

www.youtube.com

上記の動画に有るとおり、様々な状況下でRSSをメトリクスとして自車両の危険な行動をチェックできています。

CARLAのver 9.10.0からはUnstructuredな環境での実行もサポートされており、市街地環境における本格的なSimulationの実現に向けて着々と進んでいる様子が見て取れます。

youtu.be

RSS Restrictor

RSS RestrictorはRSSのメトリクスを使い、NPC車両にその場面において危険な状況をうむ制御入力を入れないようにするためのモジュールです。

具体的な実装箇所はこちらになります。

github.com

この関数はPythonから呼び出せるようになっており、CARLAのPython APIで呼び出すことによってNPCが適切に振る舞うことができるようになるようです。

まとめ

今回は自動運転システムを支える評価基準の一種であるIntelRSSとそのオープンソース実装について解説しました。今後はこのツールを活用しAutowareの性能評価をさらに加速させ、安全安心でオープンソースな自動運転システム開発を強力に押し進めていきたいと思います。

eBPFやLD_PRELOADを利用した共有ライブラリの関数フック

こんにちは、ティアフォーでパートタイムエンジニアをしている石川です。

本記事では、楽に「動的ライブラリ(及び実行バイナリ)の特定の関数をフックして何かしらの処理をする」手法について紹介していきます。

この記事は、同じくパートタイムエンジニアの西村さんによる作業の成果を元にして、石川が執筆したものです。ソースコードや図のいくつかも西村さんによる貢献です。

また、ティアフォーでは「自動運転の民主化」をともに実現していく、学生パートタイムエンジニアを常時募集しています。自動運転を実現するためには、Softwareに関してはOSからMiddlewareそしてApplicationに至るまで、Hardwareに関してはSensorからECUそして車両に至るまで異なるスキルを持つ様々な人々が不可欠です。もしご興味があれば以下のページからコンタクトいただければと思います。 https://tier4.jp/careers/tier4.jp

この記事の主題

この記事の主題は、楽に「動的ライブラリ(及び実行バイナリ)の特定の関数をフックして何かしらの処理をする」( ≒ ソースコードを改変したりビルドしなおしたりしない) ことです。

ティアフォーを中心として開発されているAutowareは、ROS (Robot Operating System) という通信ミドルウェアに依存しています。今回はAutowareの性能解析という業務を進める上で、ROS1内部の特定区間の処理時間(実時間)を計測したくなり、上記目的を達成する手段を用意することになりました。

手段としては、eBPF(extended Berkeley Packet Filter) を利用した方法と、LD_PRELOADを利用した方法の2種類を紹介します。

eBPFを利用した方法

まず、eBPFとは何かについて説明します。 eBPFとは、Linux kernel内部で実行される独自の命令セットを持つ仮想マシンにおいて、ユーザ空間から実行プログラムを送り込み、指定したevent (下図におけるkprobes, uprobes, tracepoints, perf_events に対応)にフックするなどして実行することができる機能のことです。

eBPFのユーザは、BPF bytecodeをbpf(2)によってkernelに送りこみ、安全なコードであるかのverifyとコンパイル/実行をkernelが行います。eBPFのユーザがBPF bytecodeを直接書くのは稀で、通常はC言語likeな高級言語で記述することができます。eBPFのユーザプログラムとのデータのやりとりは、“maps” と呼ばれるデータ構造を通して容易に行うようになっています。

eBPF internal (http://www.brendangregg.com/ebpf.html より引用)

今回は、「動的ライブラリ(及び実行バイナリ)の特定の関数をフックして何かしらの処理をする」ことを実現したかったので、フックイベントとしてuprobesを使用します。uprobesとは、eBPFとは独立した概念であり、ユーザアプリケーションにbreakpointを仕込んで、kernelでハンドリングするための仕組みです。

uprobesにフックしてeBPFのコードを実行するためのAPIなどについては、iovisor/bccリポジトリにおいてある bcc Reference Guide を見ればよいです。今回は、ユーザコードの関数の開始地点にフックを仕込む attach_uprobe() を利用します。

例として、ROS1内部の ros::Publication::publish(ros::SerializedMessage const&) をフックして文字列をprintするだけのコードを示します。

from __future__ import print_function
from bcc import BPF

libroscpp_path = "/path/to/libroscpp.so"

bpf_text = """
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>

int publish(struct pt_regs *ctx) {
    uint64_t pid = bpf_get_current_pid_tgid();
    bpf_trace_printk("publish() called: msg addr - %p :pid/tid %d", (void *)ctx->di, pid);
    return 0;
}
"""

b = BPF(text=bpf_text)
b.attach_uprobe(name=libroscpp_path,
  sym="_ZN3ros11Publication7publishERNS_17SerializedMessageE",
  fn_name="publish")

while 1:
    try:
        (task, pid, cpu, flags, ts, msg) = b.trace_fields()
    except ValueError:
        continue
    print("%s" % msg)

以上のスクリプトを実行することにより、libroscpp.so を動的リンクして実行されている全てのROS1プログラムにおける、指定した関数をフックすることができます。

ところで、libroscpp.soはC++によって書かれたライブラリなので、関数名がマングリングされており (上のコードにおけるattach_uprobe()の第二引数に渡している “_ZN3ros11Publication7publishERNS_17SerializedMessageE”)、面倒なことにビルド済みのバイナリからマングリングされた関数名を調べなくてはなりません。

# Get offset address of the specified function
$ nm -D  /path/to/binary_file | c++filt | grep <function_name>
# Get the mangled function name
$ nm -D /path/to/binary_file | grep <offset>

さて、このようにすることで実行バイナリや動的リンクされるsoファイルに変更を加えることなく、Pythonスクリプトを実行するのみでユーザ空間の関数をフックすることに成功しましたが、計測のオーバーヘッドの面でデメリットがあります。

関数にフックしてeBPFのコードを実行する際には、毎回ユーザ空間からカーネル空間へのコンテキストスイッチが生じるため、高い頻度で呼ばれる関数のフックに対して無視できないオーバーヘッドが生じてしまうという点です(eBPFはkprobesに対するフックやkernel eventなどの、kernel空間で発生するイベントに対するフックに適している選択肢と言えます)。

これを解決するため、次はユーザ空間のみで完結するLD_PRELOADを用いた方法を検討します。

LD_PRELOADを利用した方法

LD_PRELOADとは、/lib/ld-linux.so.2 の動作を制御する環境変数の一つであり、環境変数LD_PRELOADに共有オブジェクトを指定して実行することによって先にLD_PRELOADで指定した共有オブジェクトがリンクされます。

したがって、その共有オブジェクトに同名の関数を定義しておくことによって、元の共有ライブラリに定義されている関数の代わりにLD_PRELOADで指定した共有オブジェクトの関数を実行させることが可能になります。

さて、今回実現したいのは「特定の関数の開始時にフックして、ある処理を挟む」ことなので、LD_PRELOADで置き換えた関数の実行後に元の関数に処理を戻します。これを実現する方法はいくつかありますが、一番簡単な方法はRTLD_NEXTハンドルを引数に指定して、dlsym(3)を呼びだすことです (LD_PRELOADで置き換えた後の関数内でその関数を呼び出しても、置き換えた後の関数を再帰的に呼びだすだけなので無限ループしてしまいます)。

RTLD_NEXTハンドルとはGNU拡張による特殊なハンドルであり、現在の共有オブジェクトの次の共有オブジェクト以降で発見されるシンボルの値を取ってきます。つまり今回の場合では、LD_PRELOADで置き換える前の本来の共有ライブラリにて定義されている関数のシンボルを取ってくることができます。ちなみにRTLD_NEXTハンドルを利用するためには、GNU拡張を有効にするためにdlfcn.h をincludeする前に #define _GNU_SOURCE する必要があります。

さて、eBPFを使用したときと同様にros::Publication::publish(ros::SerializedMessage const&) が呼び出された際にフックして特定の処理を挟むことをしてみます。以下のコードを共有ライブラリとしてコンパイルし、LD_PRELOADに指定しつつROS1のプログラムを実行します。

#define _GNU_SOURCE
#include<dlfcn.h>
#include<iostream>

using publish_type = bool (*)(void* ,void*);

extern "C" bool _ZN3ros11Publication7publishERNS_17SerializedMessageE(void *p, void *q) {
  void *orig_pub = dlsym(RTLD_NEXT, "_ZN3ros11Publication7publishERNS_17SerializedMessageE");
  std::cout << "call detect: publish 1st arg: " << p << " 2nd arg: " << q << std::endl;
  return ((publish_type)orig_pub)(p,q);
}

マングリングされたそれぞれの関数を再定義し (マングリングされた関数名を調べる方法は前章のとおり)、再定義した関数の内部から dlsym(3)にRTLD_NEXTハンドルを渡して元の関数のシンボルを取得し、その元の関数を呼びだしています。

まとめ

本記事では、楽に「動的ライブラリ(及び実行バイナリ)の特定の関数をフックして何かしらの処理をする」ことを目的とし、eBPFによる方法とLD_PRELOADによる方法を紹介しました。本記事において紹介した手法は、動的ライブラリをリンクして実行するアプリケーション全般(やアプリケーションの実行バイナリ自体)に使用できるものですので、幅広く応用先があると思います。

参照

github.com

ebpf.io

www.brendangregg.com

www.amazon.co.jp

www.amazon.co.jp

Autoware開発におけるrvizの活用ついて 

こんにちは、ティアフォーで自動運転システムを開発している高木です。今回はROS開発の心強い味方、「rviz」についてのちょっとしたお話を紹介していきたいと思います。主に開発者向けのノウハウについて書いていくので実装に踏み込んだ話が多くなりますが、そうでない方も画像だけ見れば概要は伝わると思いますので、こういった機能があることを知っていただき、ぜひ率直な感想をお聞かせください。

なお、ティアフォーでは「自動運転の民主化」をともに実現していく仲間を様々な分野から募集しています。下記ページから募集職種のリストをご覧いただき、興味を持った方はぜひコンタクトをお願いします。

tier4.jp

可視化について

Autowareは多数のプロセスが組み合わさって動作する複雑なシステムであり、最終的な出力となる車両の挙動のみを見て動作状況を把握するのは容易なことではありません。特にAutowareはオープンソースであるため、第三者が作成したプログラムとの連携が前提となります。そのため、それぞれの処理でどのような判断が行われており、どういった出力がなされているかを把握することは開発の効率や品質に大きな影響を与えます。

プログラムの動作を把握する方法としてはログ出力やデバッガなどが汎用的ですが、AutowareはROS上で動作するためrqtやrvizといった強力な周辺ツールを利用することができます。私達人間にとって視覚情報は極めて相性が高く、適切な可視化は、詳細な説明をせずとも直感的に動作を理解させるほどの効果を発揮します。というわけで、今回は可視化を行う際に大活躍するrvizについて語っていこうと思います。

rvizとは

ROSが提供しているデータの可視化を行うツールがrvizで、センサーから取得した映像や点群、各プログラムの出力結果を簡単に表示することができます。ティアフォーのデモ映像でもよく登場していて、例えばこの動画でもrvizを使っています。

www.youtube.com

それでは、ここから先はrvizの機能の細かいところについてお話していきます。

Marker/MarkerArray

恐らくrvizで何らかの表示を行いたいと考えた時、真っ先に思い浮かぶのがこれでしょう。ROS標準のメッセージ型(visualization_msg)の中で定義されており、typeを指定して様々な図形を描画することができます。

poseとpoints

知ってる方にとっては当たり前かもしれませんが、POINTSやLINE_LISTなどpointsを使用する図形ではposeとpointsが併用できます。挙動はpointsの全座標をpose.positionだけ平行移動してから、pose.positionを中心にしてpose.orientationの回転を行います。私はなぜかposeが無視されてpointsだけ使われると勘違いしていたのでpointsの時点で座標変換をしていました。同じ作業に煩わされている方がいるかもしれないのでメモとして残しておきます。

LINE_LISTとLINE_STRIP

Markerで線を描くにはLINE_LISTかLINE_STRIPを使います。LINE_LISTは頂点を2個ごとに使用して独立した直線を描き、LINE_STRIPは頂点を順番に繋いだ連続する直線を描きます。単に頂点の指定方法が違うだけかと思いきや、実は連続した直線を描く場合に微妙に挙動が異なります。下の画像は星形をそれぞれの方法で描いた結果なのですが、右のLINE_STRIPで描いた方は線の幅が均等になっていません。これは線と言いつつも内部的には四角形になっているからで、頂点で折り曲げるときに線の幅が縦に配置されてしまうためです。なので見た目を気にする場合はLINE_LISTを使ったほうが綺麗になります。

LINE_LISTとLINE_STRIPで描いた星形

透過度の罠

まずはこちらの画像をご覧ください。これは手前(Z=+1.0)に緑の三角形を、奥(Z=-1.0)に赤の三角形を配置したときの描画結果です。両方の図形で透明度は1.000に設定しています。

 続いて透過度を0.999にした結果がこちらです。再表示するたびにランダムで左右のどちらかの結果になります。赤の三角形が奥に配置されていて、Z座標は緑の三角形と2.0も離れているのにもかかわらず、手前に赤の三角形が表示されてしまう場合があります。

原因はMarker表示時に透過度に応じて以下のような設定が行われるためで、透過度を0.9998以上に設定した時にしか前後関係が正しく処理されません。

透過度 深度バッファ アルファブレンディング
0.9998以上 有効 (前後関係が正しく表示される) 無効 (上書きする)
0.9998未満 無効 (前後関係が正しく表示されない) 有効 (合成する)

両方有効にすれば良いのではないかと思ったかもしれませんが、そう簡単にはいかない事情があります。深度バッファは描画の順序によらず前後関係を正しく表示する方法であるのに対し、透過度を考慮するには背景色が必要なので奥の物体から処理しなければならないためです。残念なことに順序はMarkerで制御できないので、透過度をどちらが上になっても判別できるように調整するのが最も簡単な対応となるでしょう。

Display Plugin

Displayは主にrviz中央の3D画面に何かを表示する目的で使用します。特殊な用途でなければMarkerで十分表現可能だと思いますが、より高度な処理をしたければ自作のpluginを作成することになります。Markerに似た図形を描画するならogre_helpersにあるこれらのクラスが役立ちます。

  • Arrow
  • Axes
  • BillboardLine
  • Line
  • PointCloud
  • Shape

それでも満足できないという方はOgreという3D描画ライブラリを調べてみてください。ここから先はrvizというよりもOgreの領域になります。詳細は省きますが、大まかには次の手順で自由に図形を描画できるようになり、TRIANGLE_STRIPといったMarkerにはない描画方法の指定も行えるようになります。先ほどのogre_helpers内にあるline.cppの実装が分かりやすいです。

  1. 親となるSceneNodeから新しいSceneNodeを作成する
  2. ManualObjectを作成して新しいSceneNodeに関連付ける
  3. Materialを作成する
  4. Materialを使用してManualObjectの描画関数一式を実行する

 Panel Plugin

Panelはrvizの上下左右にGUIを追加できます。Qtというライブラリで実装されており、純粋なQtアプリケーションと似たような感覚で開発できるでしょう。固有の機能としてrvizが内部で管理しているデータにアクセスできるほか、設定をrviz本体と一緒に管理するための機能が用意されています。

個人的にはrvizと同じウィンドウでまとめて情報を表示できるのがメリットだと思っています。特段rviz固有の機能を使うわけではないのですが、使用頻度の高いデータを表示しておけば別途端末を立ち上げてrostopic echoする手間が省けます(rqtにもtopicを表示するpluginがありますが、自作する場合は最小限の情報を適切に配置できるのが強み)。このような感じでQGridLayoutとQLabelを組み合わせ、簡単に情報表示を行うpluginを作ってみたのですが、これだけでもかなり印象が変わってくるのではないでしょうか。

f:id:isamu-takagi:20210226030837p:plain

特に情報が常に視界に入っているというのが大きいです。能動的にデータを見に行く場合、気にすべきデータがどれなのかを把握し、適切なタイミングで確認しなければなりません。意外とこの作業の負担は大きく、確認し忘れなどヒューマンエラーが起こる頻度も高いです。しかし、このような表示があれば「何かエラーの部分が赤くなっているんだけど大丈夫なのだろうか」といった具合で疑問を抱かせ、迅速な対応を取ることが可能となります。

また、現地で実験を行っているスタッフを開発者が遠隔からサポートする際も役立ち、確認用のコマンドを細かく指示せずとも画面の表示を読み上げてもらうだけで大まかに状況を理解することができます。実際、スクリーンショットを一枚送ってもらうだけで問題を解決できたこともありました。今後は自動運転のサービス化を目指して車両の台数を増やしていかなければなりませんから、現地で活動するスタッフの規模も拡大していくと予想されます。そのとき、個人のスキルレベルに大きく依存せず、技術者と容易にコミュニケーションがとれるかどうかは、プログラムの重要な要素となっているはずです。

Tool Plugin

Toolはrviz上部に配置され、主に中央の3D画面を使った操作を作成できます。その性質上マウスとキーボードのイベントを処理するためのコールバック関数が提供されていて、素晴らしいことにマウスの座標と任意の平面の交点を計算する関数(getPointOnPlaneFromWindowXY)まで用意されています。もちろん、rvizにはInteractive Markerが用意されているので基本的にこちらを使えば良いのですが、例えば点群を任意の場所に出して障害物検知のテストを行いたい場合に便利だったりします。 

本来の使い方ではないような気もしますが(Tool内で処理を完結させた方が良いと思っています)、画面上でクリックされた座標をtopicとしてpublishするToolを作って利用しています。マウスがクリックされた時だけ処理を行ったり、ドラッグの始点と終点を取得したりと大抵は同じような操作が欲しくなるので該当部分をToolで実装しておき、利用したいプログラムでは結果をsubscribeすればお手軽にrvizとの連携が可能となるわけです。あくまでデバッグの範囲に限りますが、作っておくとそこそこ便利に使えます。

youtu.be

この動画では歩行者に見立てた点群をマウスで動かせるようにしてデバッグをしています。任意のタイミングで急に飛び出したり、車両に合わせて速度を調整するといった動きを用意するのはなかなか難しいのですが、マウスで歩行者の位置を操作すれば複雑なケースも自由自在にテストできます。突き詰めていくとやはり専用のツールには敵いませんが、基本的にrvizにはデバッグ用の情報を色々と出しているはずですので、同じ画面から空間を共有して操作できるというメリットは非常に大きいです。

まとめ

普段の開発では可視化まわりの処理にそこまで力を入れることはないと思います。しかし、ちょっとした工夫で効率が大きく変わったり、気づきにくいミスを防止できたりと大きな可能性を秘めている部分なので、余裕があればひと手間かけてみると良いかもしれません。