0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TinyLidarNetの学習を始める前に、まずラベルと入出力の単位を確かめると、学習をやり直す回数を減らせます。私は自動運転AIチャレンジ2026のEnd to End AI部門で、この小さなLiDARモデルを使って決勝戦2位になりました。その途中で踏んだ問題を、次の参加者が確認できる手順にします。

この記事でできること

  • 学習データのラベルが、出力層の範囲に収まるか調べる
  • 加速度の正側・負側の倍率を、学習と推論でそろえる
  • LiDARの実際のレンジと、前処理の正規化を区別する
  • Hydraの上書きが、学習で読むキーに届いているか確かめる

最新版では、私が出したPR #362が2026年10月2日にマージされ、共通の設定ファイルと加速度の倍率が追加されています。「コードに倍率を足す」旧版の対処と、「設定を確認する」現在の対処を分けて説明します。

手元のAWSIM走行と同時刻のLiDAR・モデルの舵

決勝で使ったモデルを手元のAWSIMで走らせた記録です。大会本番のレースではありません。モデルの外には減速・復帰のルールも置いています。

この記事は、公式リポジトリの dev、コミット 50d6f65038b7ebd41620c442c25cfedcc1b3c7a6 を2026年10月3日に確認して書いています。実測データは開発時に収録・集計したものです。自分の作業コピーで、まず版を記録してください。

# ホスト側、aichallenge-racingkartのリポジトリ直下
git rev-parse HEAD
git branch --show-current
git status --short

設定ファイルや処理が以下と違う場合は、先に版の違いを確認します。作業中の変更を残したまま、更新コマンドを無条件に実行する必要はありません。

先に見る4項目

確認するもの 何を防ぐか 現在の確認場所
教師ラベルの分布・単位 出力範囲の外の正解を学習させる 抽出後の accelerations.npy・steers.npy
加速度の倍率と制御モード 学習時と推論時の指令の大きさが違う tiny_lidar_net_common.param.yaml とノード固有の設定
スキャンの正規化 同じ壁を別の距離として入力する 抽出処理、学習データセット、推論前処理
Hydraのキー 設定が追加されるだけで学習の条件が変わらない config/train.yaml と --cfg job

1. ラベルの範囲と、出力層の範囲を先に並べる

公式サンプルのTinyLidarNetは、LiDARの距離の並びを入力し、[加速度, 舵] の2つを出します。出力層は tanh です。実数の式では出力範囲は $(-1,1)$ で、絶対値の大きな入力ほど飽和します。浮動小数点の計算では端の値に丸められることもあるので、「1.0が絶対に出ない」ことより、ラベルの範囲と学習のしやすさを確認します。

私が公式のサンプルデータ24本を集計したとき、加速度指令は次の分布でした。これは生の制御トピックの集計で、LiDARの時刻に同期した学習サンプル数とは違います。

ファイル名による分類 rosbag本数 加速度指令の数 分布
ファイル名に mpc がないもの 21 22,062 −1、0、+1の3値。+1が80.7%
ファイル名に mpc があるもの 3 8,508 +0.7が98.4%。最小−1.6

公式サンプルデータの加速度ラベル

分類はファイル名によります。生成元の制御器をすべて確認した分類ではありません。

特に−1.6は、倍率1.0の tanh の出力範囲から外れます。範囲内でも、ラベルがほぼ一定なら、センサーから加速度を回帰する手がかりは少ないかもしれません。ただし、この分布だけで「加速度学習が失敗する原因」を特定したわけではありません。

舵も同じです。21本のデータでは23.4%が±1.0 radでした。制御指令の値と、車体が実際に出した舵角は区別してください。指令を出せることと、その角度で曲がれることは同じではありません。

自分の抽出済みデータを調べる

次のコードを check_labels.py として保存します。NumPyだけを使い、学習処理は動かしません。ラベルのファイル数、要素数、最小・最大、頻度の高い丸め値を表示します。

check_labels.py
from pathlib import Path
import sys
import numpy as np

root = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("dataset/train")
for name in ("accelerations", "steers"):
    files = sorted(root.rglob(f"{name}.npy"))
    if not files:
        raise SystemExit(f"{root}: {name}.npy がありません")
    arrays = [np.load(p, allow_pickle=False).reshape(-1) for p in files]
    a = np.concatenate(arrays)
    if any(not len(x) for x in arrays) or not np.isfinite(a).all():
        raise SystemExit(f"{name}: 空のデータ、NaNまたはinfがあります")
    values, counts = np.unique(np.round(a, 2), return_counts=True)
    order = np.argsort(counts)[-5:][::-1]
    top = [(float(values[i]), float(counts[i] / len(a))) for i in order]
    print(name, "files=", len(files), "N=", len(a),
          "min=", float(a.min()), "max=", float(a.max()), "top=", top)
# 公式の開発コンテナ内、学習用workspaceで実行
cd /aichallenge/ml_workspace/tiny_lidar_net
python3 check_labels.py dataset/train
python3 check_labels.py dataset/val

この集計は分布の点検です。訓練・検証の独立性は、同じ走行の近接フレームが両方に混ざらないよう、走行単位で分けて確認します。

2. 加速度の倍率は、学習と推論で同じにする

旧版で起きたこと

旧版の control_mode: "ai" は、加速度の出力を±1.0にクリップし、その値を加速度指令として使っていました。ラベルを大きくしても、出力側の範囲は広がりませんでした。

また、control_mode: "fixed" ではモデルの加速度出力を使わず、設定値 acceleration を出します。既定値は0.6です。これは ai の値と掛け合わせる設定ではありません。

現在の設定

PR #362以降は、次のファイルで学習・推論の共通設定を指定できます。

aichallenge/workspace/src/aichallenge_submit/tiny_lidar_net_controller/config/tiny_lidar_net_common.param.yaml

確認した版の既定値は次のとおりです。

tiny_lidar_net_common.param.yaml
/**:
  ros__parameters:
    model:
      input_dim: 750
    max_range: 30.0
    accel_scale: 1.0
    decel_scale: 1.0

正の教師加速度は accel_scale、負の教師加速度は decel_scale で割って学習します。推論では、クリップした出力に、正なら accel_scale、負なら decel_scale を掛けます。

たとえば、説明用に両方を2.0とすると、+1.37は+0.685、−1.6は−0.8になり、推論で2倍すると元の単位に戻ります。これは計算例で、2.0が最適という実験結果ではありません。

選ぶ倍率は、自分の教師ラベルの範囲を見て決めます。最大絶対値と等しい倍率ではラベルが±1に達するので、出力層の飽和まで含めて検討してください。負の目標加速度と、車両へのブレーキ操作の関係も、別に確認が必要です。

既存の重みに倍率だけを掛けても、その倍率で学習したモデルにはなりません。

加速度を学習する場合は、学習時の倍率を記録し、同じ値を推論で使います。倍率を変えて学習し直した重みと、設定を一緒に管理してください。fixed モードではこの加速度倍率は使われず、ノード固有の acceleration が使われます。

私は大会では舵だけを学習し、加速度と減速はモデルの外で決めました。今回の共通設定の修正は、入出力の対応を扱いやすくするものです。この修正を使った加速度学習で、走行性能が改善したという比較はしていません。

3. センサーの最大距離と、正規化に使う距離は違う

サンプルデータでのLiDARは、750点・前方約180°・range_max: 25.0・約20 Hzでした。反射が返らなかった方向は inf です。仕様表の修正もPR #117でマージされています。

# AWSIMと同じROS_DOMAIN_IDの開発コンテナ内で実行
# ドメイン番号は自分の実行設定に合わせる
ros2 topic echo --once --no-arr /sensing/lidar/scan
ros2 topic hz /sensing/lidar/scan

一方、私が使ったサンプルの正規化は30 mでした。25 m以下の有限値は、正規化後に最大で約0.833になり、無反射を30 mで埋めた点だけが1.0になります。

実スキャンと25 m・30 mのレンジ

これは、学習と推論がそろっていれば、ただちに不具合になるという話ではありません。私も30 mのままで走らせました。正規化の値域がそろった場合との性能比較はしていません。

危険なのは、学習は30 mのまま、推論だけ25 mにすることです。たとえば同じ10 mの壁が、学習時は0.333、推論時は0.4になり、入力の尺度が変わります。

現在の共通設定は学習データセットと推論ノードの max_range をそろえます。ただし、rosbagから .npy を作る抽出スクリプトには、別に ExtractionConfig.max_scan_range: 30.0 があります。共通ファイルを変更しても、抽出済みのファイルや過去の重みは自動で作り直されません。

確認した版の抽出スクリプトには、max_scan_range を変えるコマンドライン引数はありません。前処理を変えるときは、次を順に確認します。

  1. 抽出時に inf を何mで埋め、どこでクリップしたか
  2. 学習側が何mで割るか
  3. 推論側が生スキャンをどう処理して、何mで割るか
  4. 変更後の設定で重みを学習し直したか

大きなレンジに変更した場合、以前の抽出で切り捨てた距離や inf の区別を、抽出済みデータから戻せるとは限りません。必要なら元のrosbagから作り直します。

4. Hydraの上書きは、最終設定を表示して確かめる

学習の損失重みは train: の下にあります。舵だけ学習するキーは、現在も次の形です。

cd /aichallenge/ml_workspace/tiny_lidar_net
python3 train.py train.loss.accel_weight=0.0 --cfg job

--cfg job は学習を始めずに、Hydraが構成した設定を表示します。表示の train → loss → accel_weight が0.0なら、上書き先は合っています。

旧READMEでは loss.accel_weight=0.0 と書かれていました。エラー文に従って +loss.accel_weight=0.0 とすると、最上位に別の loss が追加されるだけで、学習が読む train.loss.accel_weight は変わりません。このREADMEはPR #356で修正済みです。

正しい上書きをした設定の該当箇所
train:
  loss:
    steer_weight: 1.0
    accel_weight: 0.0

なお --cfg job の表示だけでは、共通YAMLの中身まで読んだことにはなりません。共通YAMLは train.py の実行時に読み込まれます。学習開始時に出る Common parameters: の表示も確認してください。common_param_path の既定値は開発コンテナ内の絶対パスなので、ホストで学習するなら、実在する共通ファイルのパスを指定します。

学習を実行する場合は --cfg job を外し、訓練・検証データの場所も自分の環境に合わせます。

python3 train.py \
  data.train_dir=/path/to/train_dir \
  data.val_dir=/path/to/val_dir \
  train.loss.steer_weight=1.0 \
  train.loss.accel_weight=0.0

舵だけ学習した重みで走る場合は、推論の control_mode を fixed にします。損失から除いた加速度出力を、ai モードで使わないためです。

手元で使う確認リスト

  • 使用したコミット、重み、共通YAML、ノード固有YAMLを一組として記録した
  • 訓練と検証を別の走行に分け、ラベルの単位・分布を見た
  • 正負の倍率で割ったラベルが、意図した出力範囲に収まる
  • 学習と推論の倍率、入力点数、正規化が一致している
  • 抽出済みデータの前処理も、現在の設定と合っている
  • --cfg job で損失のキーを確認した
  • 舵だけ学習したモデルでは、加速度出力を使わない

サンプルが走らないとき、最初にモデルを大きくする必要はありません。私の場合は、入出力と設定の対応を確認するだけで、学習を疑う前に切り分けられる問題がありました。

方法全体は1本目のE2E部門の記事、教師に使った制御はPure PursuitとMPCの記事にまとめています。

参考文献・出典

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?