自動運転AIチャレンジ2023（simulation大会）のシミュレータ+AWSIMを自動実行したい

# この記事の対象者
- 自動運転AIチャレンジ2023（simulation大会）に参加しており、planning機能追加など試行錯誤に取り組み中である
- planning機能追加など試行錯誤は楽しいが、機能評価１回あたり数分ほど掛かることがしばしば辛いことがある。シミュレータやrvizのGUIの見るべき箇所をしばしば見逃してしまう。ジャッジサーバ提出前に同じコマンドを何度も実行してるとしばしばコマンドミスして精神衛生上よくない。[同じ作業を繰り返し実行する場合は自動化すべきである](https://www.oreilly.co.jp/books/9784873119847/)の教えに感銘を受けたことがある方。など
- [「テスト書いてないとかお前それ、@t_wadaの前でも同じこと言えんの？」](https://mercan.mercari.com/articles/19386/)

# シミュレータ+AWSIMを自動実行したい

自動運転AIチャレンジ2023（simulation大会）では、planning機能追加など試行錯誤がやりやすい環境が提供されております。
これまでの大会では基本的には[autowarefoundationのautoware](https://autoware.org/)を参照し、標準モジュールやそれらparameterを調整することが勝利への近道でありましたが、今回はよりシンプル構成である[autoware-micro](https://automotiveaichallenge.github.io/aichallenge2023-racing/customize/index.html)が提供されており、autowareの標準機能やparameterを調整するというよりかは、PythonRoboticsのモジュールや独自の工夫を取り入れること、および、試行錯誤を繰り返すことが競争ポイントとなりそうです。

https://automotiveaichallenge.github.io/aichallenge2023-racing/customize/index.html

# ジャッジサーバ提出前に同じコマンドを何度も実行するのが辛くなってきたよ

独自の機能を追加するのは往々にして夜中になりがちです。しかしながら、夜中２時に目をこすりながら同じコマンド実行繰り返すのはミスが起きやすく、睡眠時間に影響を及ぼします。ここで自動化を活用することで解決を試みます。
というものの、以下の部分をスクリプト化するだけなのでシンプルです。せっかくなので`result.json`まで確認できると便利そうです。

https://automotiveaichallenge.github.io/aichallenge2023-racing/online/index.html

> 事前準備（aichallenge_submitの圧縮や結果出力用フォルダの生成）
> ```bash
> #aichallenge2023-racingディレクトリで
> cd docker/evaluation
> bash advance_preparations.sh
> ```
> dockerイメージのビルド
> ```bash
> #aichallenge2023-racing/docker/evaluationディレクトリで
> bash build_docker.sh
> ```
> 
> dockerコンテナの起動（起動後、自動でautowareが立ち上がり、自動運転が開始されます）
> GPU版のAWSIMを使っている方は
> ```bash
> #aichallenge2023-racing/docker/evaluationディレクトリで
> bash run_container.sh
> ```
> result.jsonの確認
> 評価完了後、outputフォルダに以下のファイルが格納されます。
> - result.json
> - rosbag.db3
> - rviz_capture.mp4
> - autoware.log

# シミュレータ+AWSIMを自動実行したいスクリプト（`autorun.sh`）の概要

#### 実行方法
前提条件
- [SetUp手順](https://automotiveaichallenge.github.io/aichallenge2023-racing/setup/index.html)に沿って各種インストール/事前準備完了していること
- 同様に、autowareのサンプルコードの手動実行が確認できていること
- `${HOME}/aichallenge2023-racing`ディレクトリが存在していること

:::note warn
今はGPU版のみ対応
:::

実行コマンド

```bash
# スクリプトのダウンロード
cd ${HOME}/aichallenge2023-racing
wget https://raw.githubusercontent.com/seigot/aichallenge-tools/main/aichallenge2023-sim-winter/autorun.sh
wget https://raw.githubusercontent.com/seigot/aichallenge-tools/main/aichallenge2023-sim-winter/stop.sh
# シミュレータ+AWSIMを自動実行、二回目以降はここだけ実行してもOK
bash autorun.sh -l 100
```

結果
`${HOME}/aichallenge2023-racing/result.txt`に結果一覧を出力するようにしており、別ターミナルからこれを参照するとよい。以下のようなログが出力されるはず。
（されなかったらごめんなさい、今はGPU版のみ対応）

```bash
$ cd ${HOME}/aichallenge2023-racing
$ tail -f result.txt
Player rawLapTime distanceScore lapTime isLapCompleted isTimeout trackLimitsViolation collisionViolation
20231229123146_automotiveaichallenge_main	248.398743	100	353.398743	true	false	[  3,  6,  4,  1,  0]	[  2,  0,  0,  0]
20231229124031_automotiveaichallenge_main	248.6506	100	363.6506	true	false	[  4,  7,  4,  1,  0]	[  2,  0,  0,  0]
20231229124856_automotiveaichallenge_main	231.108246	47.77975	381.108246	false	false	[  6,  10,  5,  1,  0]	[  2,  0,  0,  0]
20231229125602_automotiveaichallenge_main	128.798477	47.51332	273.798462	false	false	[  5,  8,  5,  2,  0]	[  2,  0,  0,  0]
...
```

# シミュレータ+AWSIMを自動実行したいスクリプト（`autorun.sh`）の中身

<details><summary>省略</summary>

:::note warn
N=2で動作確認済、今後もスクリプト更新していくかもしれません
https://github.com/seigot/aichallenge-tools/blob/main/aichallenge2023-sim-winter/autorun.sh
:::

```autorun.sh
#!/bin/bash -x

#
# 自動実行用のスクリプト
# README記載のOnline提出前のコード実行手順をスクリプトで一撃でできるようにしてる

LOOP_TIMES=10
SLEEP_SEC=180

# check
AICHALLENGE2023_DEV_REPOSITORY="${HOME}/aichallenge2023-racing"
if [ ! -d ${AICHALLENGE2023_DEV_REPOSITORY} ]; then
   "please clone ~/aichallenge2023-racing on home directory (${AICHALLENGE2023_DEV_REPOSITORY})!!"
   return
fi

function run_autoware_awsim(){

    # MAIN Process
    # Autowareを実行する
    # run AUTOWARE
    AUTOWARE_ROCKER_NAME="autoware_rocker_container"
    AUTOWARE_ROCKER_EXEC_COMMAND="cd ~/aichallenge2023-racing/docker/evaluation; \
    			bash advance_preparations.sh;\
 			bash build_docker.sh;\
    		        rocker --nvidia --x11 --user --net host --privileged --volume output:/output --name ${AUTOWARE_ROCKER_NAME} -- aichallenge-eval" # run_container.shの代わりにrockerコマンド直接実行(コンテナに名前をつける必要がある)

    echo "-- run AUTOWARE rocker... -->"    
    echo "CMD: ${AUTOWARE_ROCKER_EXEC_COMMAND}"
    gnome-terminal -- bash -c "${AUTOWARE_ROCKER_EXEC_COMMAND}" &
    sleep 5
}

function get_result(){

    # 起動後何秒くらい待つか(sec)
    WAIT_SEC=$1

    # wait until game finish
    sleep ${WAIT_SEC}

    # POST Process:
    # ここで何か結果を記録したい
    AUTOWARE_ROCKER_NAME="autoware_rocker_container"
    RESULT_TXT="result.txt"
    RESULT_JSON_TARGET_PATH="${HOME}/aichallenge2023-racing/docker/evaluation/output/result.json"
    TODAY=`date +"%Y%m%d%I%M%S"`
    RESULT_TMP_JSON="result_${TODAY}.json" #"${HOME}/result_tmp.json"
    GET_RESULT_LOOP_TIMES=180 # 30min
    VAL1="-1" VAL2="-1" VAL3="-1" VAL4="false" VAL5="false" VAL6="false" VAL7="false"
    for ((jj=0; jj<${GET_RESULT_LOOP_TIMES}; jj++));
    do
	if [ -e ${RESULT_JSON_TARGET_PATH} ]; then
	    mv ${RESULT_JSON_TARGET_PATH} ${RESULT_TMP_JSON}
	    # result
	    VAL1=`jq .rawLapTime ${RESULT_TMP_JSON}`
	    VAL2=`jq .distanceScore ${RESULT_TMP_JSON}`
	    VAL3=`jq .lapTime ${RESULT_TMP_JSON}`
	    VAL4=`jq .isLapCompleted ${RESULT_TMP_JSON}`
	    VAL5=`jq .isTimeout ${RESULT_TMP_JSON}`
	    VAL6=`jq .trackLimitsViolation ${RESULT_TMP_JSON} | tr -d '\n'`
	    VAL7=`jq .collisionViolation ${RESULT_TMP_JSON} | tr -d '\n'`
	    break
	fi
	# retry..
	sleep 10
    done

    if [ ! -e ${RESULT_TXT} ]; then
	echo -e "Player\trawLapTime\tdistanceScore\tlapTime\tisLapCompleted\tisTimeout\ttrackLimitsViolation\tcollisionViolation" > ${RESULT_TXT}
    fi
    TODAY=`date +"%Y%m%d%I%M%S"`
    OWNER=`git remote -v | grep fetch | cut -d"/" -f4`
    BRANCH=`git branch | cut -d" " -f 2`	    
    echo -e "${TODAY}_${OWNER}_${BRANCH}\t${VAL1}\t${VAL2}\t${VAL3}\t${VAL4}\t${VAL5}\t${VAL6}\t${VAL7}" >> ${RESULT_TXT}
    echo -e "${TODAY}_${OWNER}_${BRANCH}\t${VAL1}\t${VAL2}\t${VAL3}\t${VAL4}\t${VAL5}\t${VAL6}\t${VAL7}"

    # finish..
    bash stop.sh
}

function preparation(){

    # stop current process
    bash stop.sh

    # リポジトリ設定など必要であれば実施（仮）
    echo "do_nothing"

    # 古いresult.jsonは削除する
    RESULT_JSON_TARGET_PATH="${HOME}/aichallenge2023-racing/docker/evaluation/output/result.json"
    if [ -e ${RESULT_JSON_TARGET_PATH} ]; then
	rm ${RESULT_JSON_TARGET_PATH}
    fi
}

function do_game(){
    SLEEP_SEC=$1
    preparation
    run_autoware_awsim
    get_result ${SLEEP_SEC}
}

function save_patch(){
    _IS_SAVE_PATCH=$1
    if [ "${_IS_SAVE_PATCH}" == "false" ]; then
	return 0
    fi
    mkdir -p patch
    TODAY=`date +"%Y%m%d%I%M%S"`
    git diff > ./patch/${TODAY}.patch    
}

# 引数に応じて処理を分岐
# 引数別の処理定義
IS_SAVE_PATCH="false"
while getopts "apl:s:" optKey; do
    case "$optKey" in
	a)
	    echo "-a option specified";
	    run_awsim;
	    exit 0
	    ;;
	p)
	    echo "-p option specified";
	    IS_SAVE_PATCH="true";
	    ;;
	l)
	    echo "-l = ${OPTARG}"
	    LOOP_TIMES=${OPTARG}
	    ;;
	s)
	    echo "-s = ${OPTARG}"
	    SLEEP_SEC=${OPTARG}
	    ;;
    esac
done

# main loop
echo "LOOP_TIMES: ${LOOP_TIMES}"
echo "SLEEP_SEC: ${SLEEP_SEC}"
save_patch ${IS_SAVE_PATCH}
for ((i=0; i<${LOOP_TIMES}; i++));
do
    echo "----- LOOP: ${i} -----"
    do_game ${SLEEP_SEC}
done
```

</details>

# まとめ

- planning機能追加など試行錯誤の際は、なるべく評価を自動化して試行錯誤に集中できるようにすると良さそうな気がする
- 上記のために、シミュレータ+AWSIMを自動実行するスクリプトを作成
- １回あたりの評価が楽になった事はもちろん、夜中にエージングテストする事も可能になった。何回かに１回の頻度で発生する不具合も事前に見つけやすくなったと思う。
- 動画キャプチャ、logの可視化、クラウド上での実行など拡張できそうな可能性を感じるが、そこまでするともはやジャッジサーバでありきりがなさそうなのでここまでにしておく。

# つぶやき

（今回はオンライン評価環境が充実しているので、これを駆使できれば手元で自動実行したい需要って実はそこまで多くないのでは。。と思ったりもします）

# 参考

https://jsae.or.jp/jaaic/

https://github.com/AutomotiveAIChallenge/aichallenge2023-racing

https://automotiveaichallenge.github.io/aichallenge2023-racing/online/index.html