日本語版を先に、英語版はその後に続きます。 / Japanese section first, English follows.
AWSIM/Autoware ベースの自動運転レースに向けた、ROS 不要のオフライン解析ツール集です。 ラップタイムやペナルティの検出、レース後のリプレイ生成、追い越し可能区間の可視化、 横方向オフセットの幾何計算などを、レコーディング済みの rosbag (mcap) だけを入力にして 行います。一つ一つのツールは小さく、依存も最小限です。
pip install -e ".[bag]"
python -m bagkit.v2x your_race.mcap --out-dir out/
python -m replay.replay --bag your_race.mcapサンプルの mcap は同梱していません(理由は samples/README.md と本ファイル末尾の
「限界と罠」を参照)。手元のレースの mcap ファイルをそのまま渡してください。
追い越し可能区間マップ(trackgeo)と二乗余弦の横オフセット(lateral)は、決勝(9/19-20)の後に追加します。
各ツールについて、何をするか、1 コマンドでの実行例、実測値を 1 つずつ示します。
ROS を使わずに /v2x/vehicle_positions を CDR 手デコードし、車両ごとの CSV と統計 JSON を
書き出します。
python -m bagkit.v2x race.mcap --out-dir out/実測: 466 秒のレースに対して 0.5 秒で完了。対象車両のサンプリングレートは 14.99 Hz (sigma 0.005)。
コントローラが実際に発行した /planning/scenario_planning/trajectory を CDR 手デコードします。
python -m bagkit.trajectory race.mcap実測: デコーダ本体は約 20 行。外部の非公開モジュールへの依存はありません。
公開ボードの rosbag にはペナルティ用のトピックがありません。評価系がペナルティ中に v_max を厳密に 1.3889 m/s (5.00 km/h) にクランプすることを使い、そのプラトーからペナルティ 区間を検出します。
python -m bagkit.penalty --bag race.mcap --raceline line.csv実測: 手元の 2 レースに対する検証で recall 2/2 および 3/3、誤検出 1 件、実行 6.3 秒。
make eval などが出力するレースディレクトリ(または単体の mcap)から、10 Hz の統合
タイムライン、周回・停止統計、車間距離、ニアミス、ペナルティを含むイベントログを作ります。
python -m replay.replay --bag race.mcap実測: 484 秒のレース、4,843 サンプルの処理に 3.3 秒。
aichallenge_submit に対する 11 種類の静的チェック(ホストパス、DDS 設定、ビルド時の
pip 実行、外部参照、環境変数のデフォルト値、C++ メンバー、単位の取り違えなど)を行います。
presubmit check --repo-root .実測: 154 件のテストが通過。誤検出だったチェック(コメントや docstring 内の内部パス 参照を実コードと誤検出する問題)は既に修正済みで、自分たちの提出ツリーに対して実行した 結果は 0 FAIL / 5 WARN でした。
レースディレクトリを runs.csv に変換し、環境フィンガープリントを記録し、2 回の実行結果を
比較します。
python -m evalwrap ingest-race output/20260101-000000 --out runs.csv --run-id my-run実測: 105 件のテストが通過。
YAML の上書き差分から提出用 tarball を作る variant.py、実行結果を順位付けする
ledger.py、レーシングラインの途中で停止するオポーネント軌道を作る roadblock.py を
含みます。
python -m racekit.variant --spec variant.yaml --out submit/variant.tar.gz実測: roadblock.py は指定した waypoint で速度列だけを 0 にランプダウンし、他の列は
1 バイトも変更しません(本リポジトリのテストで保証)。
自分たちの aichallenge_submit のコピーから、stock / pp-slow / pacer / blocker
の 4 種類のオポーネント tarball を作ります。
opponents/build-opponent.sh pp-slow --speed-kmh 15実測: 82 件のテストが通過。shellcheck もクリーンです。
コントローラのログから [MPCInfeasibleDiag] 系の診断行を読み取り、どの仮説がどれだけの
事象を説明するかを、レース全体に対して検証します。オフラインの QP レプリカも含みます。
python -m mpcdebug.infeasible_verdict controller.log実測: 「corridor の行だけが infeasible の原因になりうる」という仮説について、
not_lateral に分類される事象が 1 件でもあれば、その仮説と矛盾することを機械的に
検出します。
- V2X は 15 Hz(50 ms クロックの 3 スロットのうち 2 スロット)、位置ノイズは概ね 1 cm。
- ペナルティ中の速度プラトーは厳密に 1.3889 m/s(5.00 km/h)。
- 実測されたカート同士の横方向接触距離は約 1.50 m(中心間)。
- MPC のホライズンは「ステップ数 × 解像度」で決まり、狭いのはコースではなく設定。
- 追い越し可能区間の判定は、lanelet2 の全レーンレット多角形の和集合を使うべきで、 占有格子地図(OGM)や境界線の点群最近傍だけでは過大・過小評価する。
- どのツールもサーキット固有のデータ(ラインやマップ)を前提にしています。別のコースに そのまま使うと数値は再検証が必要です。
- HART(軌道予測、v0.2 で公開予定)は 15 Hz の生データをそのまま渡すと速度推定が 約 25% 低く出ます。内部で 20 Hz にリサンプルしてから使ってください。
- 横方向オフセットの計算はコリドーに振幅をクリップされて 0 になった瞬間、所要時間も 0 と評価されるため、速度則と組み合わせる際はゼロ振幅のケースを明示的にガードする 必要があります(決勝後に追加する lateral モジュールで説明します)。
- 追い越しのシミュレーションは初期条件に敏感で、1 回の実行では意味のある比較になりません。 ペアテストで少なくとも 15 回の実行が必要でした。
Apache-2.0 ライセンスです。コードの一部は AI アシスタント(Claude)の支援を受けて 作成・整理されました。追い越し可能区間の考え方や横方向オフセットの手法について、 fis-teria, topse, iASL, NITS の公開された取り組みから着想を得た部分があります。この場を 借りて感謝します。
A collection of ROS-free, offline analysis tools for AWSIM/Autoware-based autonomous racing. Lap and penalty detection, post-race replay, overtake-window visualisation, all driven from a recorded rosbag (mcap) alone. Each tool is small and has minimal dependencies.
pip install -e ".[bag]"
python -m bagkit.v2x your_race.mcap --out-dir out/
python -m replay.replay --bag your_race.mcapNo sample mcap ships with this release (see samples/README.md and "Limits and traps"
below for why). Point these commands at a bag from your own local race.
The overtake-window map (trackgeo) and the raised-cosine lateral offset (lateral) will be added after the finals (9/19-20).
One command and one measured number per tool.
Hand-decodes /v2x/vehicle_positions CDR without ROS, writing a per-car CSV and a
stats JSON.
python -m bagkit.v2x race.mcap --out-dir out/Measured: 0.5 s on a 466 s race. The recorded vehicle's sampling rate was 14.99 Hz (sigma 0.005).
Hand-decodes /planning/scenario_planning/trajectory, the line the controller
actually published.
python -m bagkit.trajectory race.mcapMeasured: the decoder itself is about 20 lines, with no dependency on a private internal module.
Public board bags carry no penalty topic. The evaluator clamps v_max to exactly 1.3889 m/s (5.00 km/h) for a penalty's duration, and this looks for that plateau.
python -m bagkit.penalty --bag race.mcap --raceline line.csvMeasured: on two local validation runs, recall 2/2 and 3/3, one false positive, 6.3 s to run.
Turns a race output directory (or a single bare mcap) into a synced 10 Hz timeline, per-car lap/stop stats, pairwise distances, near-contacts, and a merged penalty/event log.
python -m replay.replay --bag race.mcapMeasured: 3.3 s to process a 484 s race, 4,843 samples.
11 static checks against aichallenge_submit: host paths, DDS settings, build-time
pip installs, references outside the submitted tree, empty environment-variable
defaults, C++ member issues, unit-confusion traps, and more.
presubmit check --repo-root .Measured: 154 tests pass. A false-positive class (a check that flagged internal-path references sitting inside comments or docstrings, not real code) has already been fixed; running it against our own tree gives 0 FAIL / 5 WARN.
Turns a race directory into runs.csv, records an environment fingerprint, and
compares two runs.
python -m evalwrap ingest-race output/20260101-000000 --out runs.csv --run-id my-runMeasured: 105 tests pass.
variant.py builds a submission tarball from a YAML override diff, ledger.py ranks
run results, and roadblock.py builds an opponent trajectory that stops dead partway
around the line.
python -m racekit.variant --spec variant.yaml --out submit/variant.tar.gzMeasured: roadblock.py ramps only the speed column to zero at the requested waypoint
and changes not a single other column (checked by this repository's own tests).
Builds stock / pp-slow / pacer / blocker opponent tarballs from a copy of your
own aichallenge_submit.
opponents/build-opponent.sh pp-slow --speed-kmh 15Measured: 82 tests pass, and shellcheck is clean on the build script.
Reads [MPCInfeasibleDiag]-style diagnostic lines from a controller log and tests a
set of candidate causes against every infeasible event in the race, not a hand-picked
sample. Includes an offline QP replica.
python -m mpcdebug.infeasible_verdict controller.logMeasured: if a hypothesis claims the corridor rows are the only possible source of
infeasibility, this mechanically flags any event classified not_lateral as a direct
contradiction of that hypothesis.
- V2X arrives at 15 Hz (2 of 3 slots of a 50 ms clock), with roughly 1 cm of position noise.
- The penalty speed plateau is exactly 1.3889 m/s (5.00 km/h).
- Measured centre-to-centre lateral contact distance between karts is about 1.50 m.
- The MPC horizon is horizon steps times resolution; a narrow corridor is a configuration fact, not a track fact.
- Overtake-window feasibility should be computed from the union of every lanelet2 lanelet polygon; an occupancy-grid raytrace or a nearest-point search over boundary polylines both over- or under-estimate it.
- Every tool here assumes circuit-specific data (a line, a map). Re-verify the numbers before reusing them on a different track.
- HART (the trajectory predictor, planned for v0.2) under-estimates speed by about 25% if fed raw 15 Hz data directly; resample to 20 Hz first.
- The lateral-offset time law has a zero-amplitude trap: once the corridor clips a live manoeuvre's amplitude to zero, the time-to-build calculation also reads zero. A speed law built on top of it needs to guard that case explicitly (see the docstring on the lateral module, added after the finals).
- Overtake simulations are sensitive to initial conditions; a single run is not a meaningful comparison. Paired testing needed at least 15 runs per side before a result was trustworthy.
Apache-2.0. Some of this code was written and organised with the help of an AI assistant (Claude). The overtake-window approach and the lateral-offset method were informed by public work from fis-teria, topse, iASL, and NITS; thanks to all of them.