認識機能の評価#
Autoware の認識機能(perception)の認識結果から mAP(mean Average Precision)などの指標を計算して性能を評価する。
Autoware の実行時には perception topic を保存する。その後、後処理の中で評価を行う。
pass/fail を判定する topic は scenario.yaml に記述される evaluation_task に基づく。解析したい topic はターミナル引数から指定する。指定しない場合はデフォルトの値が使用される。
また、通常のpass/fail の判定に加えて、planning_control の評価項目である planning_factor を使用できる。詳細はこちらを参照。
事前準備#
perception では、機械学習の学習済みモデルを使用する。 モデルを事前に準備していないと Autoware から認識結果が出力されない。 何も評価結果が出てこない場合は、この作業が正しく出来ているか確認する。
モデルファイルのダウンロード#
モデルは Autoware のセットアップ時にダウンロードされる。 モデルのダウンロード方法は、使用しているに Autoware のバージョンによって異なるのでどちらの手法が使われているか確認する。 以下のパターンが存在する。
ansibleでダウンロード#
スクリプト実行時にDownload artifacts? [y/N]と出てくるのでyを入力してエンターを押す(Autoware foundationのmainだとこちら)
https://github.com/autowarefoundation/autoware/blob/main/ansible/roles/artifacts/tasks/main.yaml
パッケージのビルド時に自動でダウンロード#
少し古い Autoware.universe を使用している場合はこちら、13b96ad3c636389b32fea3a47dfb7cfb7813cadcのコミットハッシュまではこちらが使用される。
lidar_centerpoint/CMakeList.txt
モデルファイルの変換#
ダウンロードした onnx ファイルはそのまま使用するのではなく、TensorRT の engine ファイルに変換して利用する。 変換用のコマンドが用意されているので、Autoware のワークスペースを source してコマンドを実行する。
$HOME/autowareに Autoware をインストールしたとして説明する。
source $HOME/autoware/install/setup.bash
ros2 launch lidar_centerpoint lidar_centerpoint.launch.xml build_only:=true
変換コマンドが終了すると、engine ファイルが出力されている。 モデルのダウンロード方法に合わせて出力先が変わるので、適切なディレクトリに出力されているか確認する。
ansibleでダウンロード#
以下のファイルが出力される。
$HOME/autoware_data/lidar_centerpoint/pts_backbone_neck_head_centerpoint_tiny.engine
$HOME/autoware_data/lidar_centerpoint/pts_voxel_encoder_centerpoint_tiny.engine
パッケージのビルド時に自動でダウンロード#
$HOME/autoware/install/lidar_centerpoint/share/lidar_centerpoint/data/pts_backbone_neck_head_centerpoint_tiny.engine
$HOME/autoware/install/lidar_centerpoint/share/lidar_centerpoint/data/pts_voxel_encoder_centerpoint_tiny.engine
評価方法#
まずはじめに、セットアップ手順に従ってセットアップを完了させます。
セットアップが終了したら、~/driving_log_replayer_v2/sample_datasetにあるサンプル rosbagを使用して認識機能の評価を行うことができます。
ros2 launch driving_log_replayer_v2 driving_log_replayer_v2.launch.py \
scenario_path:=$HOME/driving_log_replayer_v2/perception.yaml \
sensing:=false
あるいは
ros2 launch driving_log_replayer_v2 driving_log_replayer_v2.launch.py \
scenario_path:=$HOME/driving_log_replayer_v2/perception.yaml \
remap_arg:="/sensing/lidar/top/velodyne_packets,/sensing/lidar/left/velodyne_packets,/sensing/lidar/right/velodyne_packets"
[!NOTE]
サンプル rosbagは、点群を生成するためのpacketsと/sensing/lidar/concatenated/pointcloudが含まれています。そのため、トピックを重複させないためにremapもしくはsensingの非有効化をする必要があります。
launch を立ち上げると以下が順に実行され、評価される。
logging_simulator.launch、ros2 bag playコマンドを立ち上げる- rosbag から出力されたセンサーデータを Autoware が受け取り、perception モジュールが認識を行う
- そこで出力された対象の topic を保存しておく
- rosbag の再生が終了した後、保存した rosbag を1つのメッセージごとにparseさせ対象の topic を評価する
評価結果#
pass/fail を判定する topic の subscribe 1 回につき、以下に記述する判定結果が出力される。
正常#
シナリオのCriterionタグのCriteriaを満たすこと。
sample の scenario.yaml は以下のようなっており、
Criterion:
- PassRate: 95.0 # How much (%) of the evaluation attempts are considered successful.
CriteriaMethod: num_gt_tp # refer https://github.com/tier4/driving_log_replayer_v2/blob/develop/driving_log_replayer_v2/driving_log_replayer_v2/criteria/perception.py#L136-L152
CriteriaLevel: hard # Level of criteria (perfect/hard/normal/easy, or custom value 0.0-100.0)
Filter:
Distance: 0.0-50.0 # [m] null [Do not filter by distance] or lower_limit-(upper_limit) [Upper limit can be omitted. If omitted value is 1.7976931348623157e+308]
- PassRate: 95.0 # How much (%) of the evaluation attempts are considered successful.
CriteriaMethod: num_gt_tp # refer https://github.com/tier4/driving_log_replayer_v2/blob/develop/driving_log_replayer_v2/driving_log_replayer_v2/criteria/perception.py#L136-L152
CriteriaLevel: easy # Level of criteria (perfect/hard/normal/easy, or custom value 0.0-100.0)
Filter:
Distance: 50.0- # [m] null [Do not filter by distance] or lower_limit-(upper_limit) [Upper limit can be omitted. If omitted value is 1.7976931348623157e+308]
- pass/fail を判定する topic のsubscribe 1回に対して、0.0-50.0[m]の距離にあるobjectで、tpのobject数がhard(75.0%)以上の場合。ResultのFrameがSuccessになる。
- pass/fail を判定する topic のsubscribe 1回に対して、50.0-1.7976931348623157e+308[m]の距離にあるobjectで、tpのobject数がeasy(25.0%)以上の場合。ResultのFrameがSuccessになる。
- また、
PassRate >= 正常数 / 全受信数 * 100の条件を満たすとき、ResultのTotalがSuccessになる。
異常#
正常の条件を満たさない場合
評価スキップ#
以下の場合に、FrameSkipに1足す処理のみ行う。 FrameSkipは評価をskipした回数のカウンタ。
- 受信したobjectのヘッダー時刻の前後75msec以内に真値が存在しない場合
- 受信したobjectのfootprint.pointsの数が1か2の場合(この条件はperception_evalが更新されたらなくなる予定)
評価スキップNoGTNoObject#
- フィルタ条件によって真値と認識結果がフィルタされ評価されなかった場合(評価結果PassFailのオブジェクトの中身が空の場合)
ignore_frames#
ignore_frames に指定したフレームは評価対象外となり、解析結果にも含まれない。。但し、frame results には追加される(=pickle ファイルには残る)。FrameSkip のカウント対象となり、result.jsonl には {"Info": {"Reason": "IGNORED_FRAME"}} として出力される。
ignore_frames はシナリオ (Evaluation.ignore_frames) と launch 引数で設定でき、launch 引数の方が優先される。値は以下のトークンをカンマ区切りで並べたもの。
| トークン | 意味 | 例 |
|---|---|---|
N |
t4_dataset のフレームインデックス (FrameName) が N のフレーム。 |
3 |
A-B |
t4_dataset のフレームインデックスが A 以上 B 以下のフレーム。 | 0-4 |
first:N |
評価したフレームのうち、順番が最初の N フレーム。 | first:2 |
last:N |
評価したフレームのうち、順番が最後の N フレーム。 | last:1 |
例: ignore_frames:="0-4,10,first:1,last:2"
N と A-B は dataset のフレームインデックスに対する指定だが、first:N と last:N は評価対象となるフレームの順番に対する指定なので、dataset のフレームインデックスを調べなくても使用できる。自車や認識モジュールの状態が安定していないシーンの先頭・末尾を除外する用途を想定している。
last:N は rosbag を読んでいる途中では確定できないため、評価したフレームのうち最後の N フレームは書き出しを rosbag の最後まで遅延させ、除外フレームとして書き出す。last:N で除外されたフレームは、メトリクス・解析・カバレッジの計算前に frame results から削除される。
評価スクリプトが使用する Topic 名とデータ型#
pass/fail を判定する topic は scenario.yaml で定義する evaluation_task に基づく。
| evaluation_task | データ型 |
|---|---|
| detection | autoware_perception_msgs/msg/DetectedObjects |
| tracking | autoware_perception_msgs/msg/TrackedObjects |
| prediction | TBD |
| fp_validation | autoware_perception_msgs/msg/DetectedObjects |
pass/fail とは関係なしに分析をしたい topic はターミナル引数の USE_CASE_ARGS で定義できる。
| 引数 | データ型 |
|---|---|
| evaluation_detection_topic_regex | autoware_perception_msgs/msg/DetectedObjects |
| evaluation_tracking_topic_regex | autoware_perception_msgs/msg/TrackedObjects |
| evaluation_prediction_topic_regex | TBD |
| evaluation_fp_validation_topic_regex | autoware_perception_msgs/msg/DetectedObjects |
また、評価を通じて得られた結果を rosbag に書き込む。
| topic 名 | データ型 |
|---|---|
| /driving_log_replayer_v2/marker/ground_truth | visualization_msgs/msg/MarkerArray |
| /driving_log_replayer_v2/marker/results | visualization_msgs/msg/MarkerArray |
logging_simulator.launch に渡す引数#
- localization: false
- planning: false
- control: false
注:アノテーション時とシミュレーション時で自己位置を合わせたいので bag に入っている tf を使い回す。そのため localization は無効である。
依存ライブラリ#
認識機能の評価はperception_evalに依存している。
依存ライブラリとの driving_log_replayer_v2 の役割分担#
driving_log_replayer_v2 が ROS との関係部分や pass/fail を判定する部分を担当する。perception_eval がデータセットを使って実際に評価する部分を担当するという分担になっている。
perception_eval は ROS 非依存のライブラリなので、ROS のオブジェクトを受け取ることができない。
また、timestamp が ROS ではナノ秒、t4_dataset は nuScenes をベースしているためマイクロ秒が採用されている。
このため、ライブラリ使用前に適切な変換が必要となる。
また、perception_eval から返ってくる評価結果を ROS の topic で 保存し可視化する部分も担当する。
perception_eval は、driving_log_replayer_v2 から渡された検知結果と GroundTruth を比較して指標を計算し、評価を出力する部分を担当する。
simulation#
シミュレーション実行に必要な情報を述べる。
入力 rosbag に含まれるべき topic#
t4_dataset で必要なトピックが含まれていること
車両の ECU の CAN と、使用している sensor の topic が必要 以下は例であり、違うセンサーを使っている場合は適宜読み替える。 CAMERA が複数ついている場合は、搭載されているすべての camera_info と image_rect_color_compressed を含める。 尚、sensing の true or false によって/sensing/lidar/concatenated/pointcloudは重複しないように remap される。
| topic 名 | データ型 |
|---|---|
| /pacmod/from_can_bus | can_msgs/msg/Frame |
| /sensing/camera/camera*/camera_info | sensor_msgs/msg/CameraInfo |
| /sensing/camera/camera*/image_rect_color/compressed | sensor_msgs/msg/CompressedImage |
| /sensing/lidar/concatenated/pointcloud | sensor_msgs/msg/PointCloud2 |
| /sensing/lidar/*/velodyne_packets | velodyne_msgs/VelodyneScan |
| /tf | tf2_msgs/msg/TFMessage |
CAN の代わりに vehicle の topic を含めても良い。
| topic 名 | データ型 |
|---|---|
| /pacmod/from_can_bus | can_msgs/msg/Frame |
| /sensing/camera/camera*/camera_info | sensor_msgs/msg/CameraInfo |
| /sensing/camera/camera*/image_rect_color/compressed | sensor_msgs/msg/CompressedImage |
| /sensing/lidar/concatenated/pointcloud | sensor_msgs/msg/PointCloud2 |
| /sensing/lidar/*/velodyne_packets | velodyne_msgs/VelodyneScan |
| /tf | tf2_msgs/msg/TFMessage |
| /vehicle/status/control_mode | autoware_vehicle_msgs/msg/ControlModeReport |
| /vehicle/status/gear_status | autoware_vehicle_msgs/msg/GearReport |
| /vehicle/status/steering_status | autoware_vehicle_msgs/SteeringReport |
| /vehicle/status/turn_indicators_status | autoware_vehicle_msgs/msg/TurnIndicatorsReport |
| /vehicle/status/velocity_status | autoware_vehicle_msgs/msg/VelocityReport |
入力 rosbag に含まれてはいけない topic#
| topic 名 | データ型 |
|---|---|
| /clock | rosgraph_msgs/msg/Clock |
clock は、ros2 bag play の--clock オプションによって出力しているので、bag 自体に記録されていると 2 重に出力されてしまうので bag には含めない
evaluation#
評価に必要な情報を述べる。
シナリオフォーマット#
ユースケース評価とデータベース評価の 2 種類の評価がある。 ユースケースは 1 個のデータセットで行う評価で、データベースは複数のデータセットを用いて、各データセット毎の結果の平均を取る評価である。
データベース評価では、キャリブレーション値の変更があり得るので vehicle_id をデータセット毎に設定出来るようにする。 また、Sensing モジュールを起動するかどうかの設定も行う。
サンプル参照
評価結果フォーマット#
サンプル参照
perception では、シナリオに指定した条件で perception_eval が評価した結果を各 frame 毎に出力する。 全てのデータを流し終わったあとに、最終的なメトリクスを計算しているため、最終行だけ、他の行と形式が異なる。
以下に、各フレームのフォーマットとメトリクスのフォーマットを示す。 注:結果ファイルフォーマットで解説済みの共通部分については省略する。
各フレームのフォーマット
{
"Frame": {
"FrameName": "評価に使用したt4_datasetのフレーム番号",
"FrameSkip": "評価が飛ばされた回数の合計。発生する条件は評価結果の項目を参照",
"criteria0": {
// criteria0の結果、真値と認識結果が存在する場合
"PassFail": {
"Result": { "Total": "Success or Fail", "Frame": "Success or Fail" },
"Info": {
"TP": "フィルタ済みobjectの中でTPと判定された数",
"FP": "フィルタ済みobjectの中でFPと判定された数",
"FN": "フィルタ済みobjectの中でFNと判定された数"
},
"Objects": {
// 評価したobjectの情報 別途説明する
}
}
},
"criteria1": {
// criteria1の結果、真値と認識結果が存在しない場合
"NoGTNoObj": "真値と認識結果がフィルタされて評価できなかった回数"
}
}
}
情報のフォーマット
{
"Frame": {
"Info": {
"Reason": "評価されなかった理由。NO_GROUND_TRUTH: 受信したobjectのヘッダー時刻の前後75msec以内に真値が存在しない。IGNORED_FRAME: ignore_frames の設定で評価対象外になった。"
},
"FrameSkip": "評価が飛ばされた回数の合計。objectの評価を依頼したがdatasetに75msec以内の真値がなく場合、または、footprint.pointsの数が1か2の場合に発生する"
}
}
警告のフォーマット
{
"Frame": {
"Warning": {
"Reason": "評価されなかった理由。INVALID_ESTIMATED_OBJECTS: 受信したobjectを変換できなかった(footprint.pointsの数が1か2の場合など)。"
},
"FrameSkip": "評価が飛ばされた回数の合計。objectの評価を依頼したがdatasetに75msec以内の真値がなく場合、または、footprint.pointsの数が1か2の場合に発生する"
}
}
Objectsデータのフォーマット
json schemaを参照
メトリクスデータのフォーマット
evaluation_taskがdetectionまたはtrackingの場合
{
"Frame": {
"FinalScore": {
"Score": {
"TP": {
"ALL": "すべてのラベルのTP率",
"label0": "label0のTP率",
"label1": "label1のTP率"
},
"FP": {
"ALL": "すべてのラベルのFP率",
"label0": "label0のFP率",
"label1": "label1のFP率"
},
"FN": {
"ALL": "すべてのラベルのFN率",
"label0": "label0のFN率",
"label1": "label1のFN率"
},
"TN": {
"ALL": "すべてのラベルのTN率",
"label0": "label0のTN率",
"label1": "label1のTN率"
},
"AP(Center Distance)": {
"ALL": "すべてのラベルのAP率(Center Distance)",
"label0": "label0のAP率(Center Distance)",
"label1": "label1のAP率(Center Distance)"
},
"APH(Center Distance)": {
"ALL": "すべてのラベルのAPH率(Center Distance)",
"label0": "label0のAPH率(Center Distance)",
"label1": "label1のAPH率(Center Distance)"
},
"AP(IoU 2D)": {
"ALL": "すべてのラベルのAP率(IoU 2D)",
"label0": "label0のAP率(IoU 2D)",
"label1": "label1のAP率(IoU 2D)"
},
"APH(IoU 2D)": {
"ALL": "すべてのラベルのAPH率(IoU 2D)",
"label0": "label0のAPH率(IoU 2D)",
"label1": "label1のAPH率(IoU 2D)"
},
"AP(IoU 3D)": {
"ALL": "すべてのラベルのAP率(IoU 3D)",
"label0": "label0のAP率(IoU 3D)",
"label1": "label1のAP率(IoU 3D)"
},
"APH(IoU 3D)": {
"ALL": "すべてのラベルのAPH率(IoU 3D)",
"label0": "label0のAPH率(IoU 3D)",
"label1": "label1のAPH率(IoU 3D)"
},
"AP(Plane Distance)": {
"ALL": "すべてのラベルのAP率(Plane Distance)",
"label0": "label0のAP率(Plane Distance)",
"label1": "label1のAP率(Plane Distance)"
},
"APH(Plane Distance)": {
"ALL": "すべてのラベルのAPH率(Plane Distance)",
"label0": "label0のAPH率(Plane Distance)",
"label1": "label1のAPH率(Plane Distance)"
}
},
"MOTA": {"https://github.com/tier4/autoware_perception_evaluation/blob/develop/docs/ja/perception/metrics.md#tracking"},
"MOTA": {"https://github.com/tier4/autoware_perception_evaluation/blob/develop/docs/ja/perception/metrics.md#tracking"},
"IDswitch": {"https://github.com/tier4/autoware_perception_evaluation/blob/develop/docs/ja/perception/metrics.md#id-switch"},
"Error": {
"ALL": {
"average": {
"x": "x座標",
"y": "y座標",
"yaw": "ヨー角",
"length": "長さ",
"width": "幅",
"vx": "x方向の速度",
"vy": "y方向の速度",
"nn_plane": "最近傍面の距離"
},
"rms": {
"x": "x座標",
"y": "y座標",
"yaw": "ヨー角",
"length": "長さ",
"width": "幅",
"vx": "x方向の速度",
"vy": "y方向の速度",
"nn_plane": "最近傍面の距離"
},
"std": {
"x": "x座標",
"y": "y座標",
"yaw": "ヨー角",
"length": "長さ",
"width": "幅",
"vx": "x方向の速度",
"vy": "y方向の速度",
"nn_plane": "最近傍面の距離"
},
"max": {
"x": "x座標",
"y": "y座標",
"yaw": "ヨー角",
"length": "長さ",
"width": "幅",
"vx": "x方向の速度",
"vy": "y方向の速度",
"nn_plane": "最近傍面の距離"
},
"min": {
"x": "x座標",
"y": "y座標",
"yaw": "ヨー角",
"length": "長さ",
"width": "幅",
"vx": "x方向の速度",
"vy": "y方向の速度",
"nn_plane": "最近傍面の距離"
}
},
"label0": "label0の誤差メトリクス"
}
}
}
}
evaluation_taskがfp_validationの場合
{
"Frame": {
"FinalScore": {
"GroundTruthStatus": {
"UUID": {
"rate": {
"TP": "表示UUIDのTP率",
"FP": "表示UUIDのFP率",
"TN": "表示UUIDのTN率",
"FN": "表示UUIDのFN率"
},
"frame_nums": {
"total": "GTが評価されるフレーム番号のリスト",
"TP": "GTがTPとして評価されるフレーム番号のリスト",
"FP": "GTがFPとして評価されるフレーム番号のリスト",
"TN": "GTがTNとして評価されるフレーム番号のリスト",
"FN": "GTがFNとして評価されるフレーム番号のリスト"
}
}
},
"Scene": {
"TP": "シーンのTP率",
"FP": "シーンのFP率",
"TN": "シーンのTN率",
"FN": "シーンのFN率"
}
}
}
}
真値カバレッジ
最終行には、デグレード評価トピックについて dataset の真値をどれだけ評価できたかも出力される。FinalScore と並べて書かれ、Result.Summary は変更されない。
{
"Frame": {
"FinalScore": {},
"GtFrames": "dataset の評価区間に含まれる真値フレーム数",
"GtFramesEvaluated": "有効に評価された推定結果に紐付いた真値フレーム数(重複なし)",
"Coverage": "GtFramesEvaluated / GtFrames を小数第4位で丸めた値",
"SkipReasons": {
"INVALID_ESTIMATED_OBJECTS": "受信したobjectを変換できずスキップしたフレーム数",
"NO_GROUND_TRUTH": "75msec以内に真値が見つからずスキップしたフレーム数",
"IGNORED_FRAME": "ignore_frames の設定で評価対象外にしたフレーム数"
}
}
}
Coverage が想定より低い場合、dataset の真値フレームが一度も評価されていないことを意味する(真値の時刻に十分近いobjectのメッセージが無い場合など)。そのフレームの TP も FN もメトリクスに含まれない。
pickle ファイル#
データベース評価では、複数の bag を再生する必要があるが、ROS の仕様上、1 回の launch で、複数の bag を利用することは出来ない。 1 つの bag、すなわち 1 つの t4_dataset に対して launch を 1 回叩くことなるので、データベース評価では、含まれるデータセットの数だけ launch を実行する必要がある。
データベース評価は 1 回の launch で評価できないため、perception では、result.jsonl の他に scene_result.pkl というファイルを出力する。 pickle ファイルは python のオブジェクトをファイルとして保存したものであり、perception_eval の PerceptionEvaluationManager.frame_results を保存している。 pickle ファイルに記録した object をすべて読み込み、dataset の平均の指標を出力することでデータセット評価が行える。
走行考慮検出評価の出力ファイル#
evaluation_config_dict に advanced_detection_metrics を設定した場合(サンプルシナリオのコメントアウトされた例を参照。perception_eval 1.4 以降が必要)、enable_metrics_details の設定に関わらず、result_archive/<topic>/ の scene_result.pkl の隣に以下の 2 ファイルが出力される。fp_validation では出力されない。
| ファイル | 内容 |
|---|---|
advanced_detection_metrics.json |
シナリオ単位のレポート(strict JSON、未定義の値は null)。status(ok / empty / error)、評価した topic・task・frame id、データセット名と lanelet2 地図の有無、設定とオブジェクトフィルタのダイジェスト、フレーム数(frame_results, ignored, evaluated, skipped, prepared)、フィルタ毎のカバレッジ、メトリクスの values、perception_eval の warnings。 |
advanced_detection_samples.npz |
値の計算元となったフレーム毎のサンプル(perception_eval の PreparedSamples.to_npz)。データベース評価の各シーンを再生せずに後からプールするためのもの。sha256 はレポートの samples_file に記録される。status が error の場合は出力されない。 |
ignore_frames に指定したフレームは他のメトリクスと同様に両ファイルから除外されるが、scene_result.pkl には残る。メトリクスを計算できない場合(perception_eval が古い、地図が無い等)も評価自体には影響せず、レポートが status: error と理由(error)付きで出力される。
データベース評価の結果ファイル#
シナリオに複数の dataset を記述したデータベース評価の場合には、結果出力先ディレクトリに database_result.json というファイルが出力される。
形式はメトリクスのフォーマット と同じ