AWS DeepRacerを始めてみた

# はじめに
こんにちは。
お久しぶりです。１年ぶりの投稿となります。
昨年のアドカレではRaspberripy PicoのPIOの記事を書きました。
今年は何を書こうかネタに悩んでおりました。
昨年の記事の一文を持ってきますと、

https://qiita.com/fude-t/items/d2baf1c98ba807273dcf


```
機械学習で、(USBではない)昔のゲームコントローラーを操作するAIを作りたいと考えていました。
```
です。これに関する何かをやろうと考えておりました。
図も昨年の記事から持ってきましょう。

```
┌─────┐              ┌──────────────────┐    ┌─────────────────┐
│     ├──────────────► capture board    ├────►                 │
│ N64 │              └──────────────────┘    │      PC         │
│     │     ┌───────┐                        │   (TensorFlow)  │
└──▲──┘     │       │                        │                 │
   │        │ Pico  │                        └──────┬──────────┘
   │        │ (PIO) │                               │
   └────────┤       ◄───────────────────────────────┘
            └───────┘    Emulate N64Controller via PIO
                        (USB to N64 Controller Adaptor)
```

言ってしまえば、今回取り組むジャンルに近いところは図でいうと、右上のPC(TensorFlow)に相当します。

しかし、私は機械学習の知識は皆無です。
そこで、なにかハードルが低めのものから取り組めないかと考えていました。

ぼーっとawsのサービス一覧を眺めていたときに、ふと目に止まったのがAWS DeepRacerでした。
「もしかしたら、機械学習のとっかかりとしては良いかもしれない———。」

アドカレ駆動開発になっている気がしないでもないですが、趣味の活動なのでまぁ良いでしょう。

一年経って、図でいうところのPIOのブロックも完成していないので、完成までの期間はまるでサグラダファミリアのような状態です(=エターなる: 個人開発者界隈でたまに使われる、永遠に開発が終わらないという意味)


# とりあえずAWS DeepRacerのコンソールを開いてみる
↓GettingStartedのスクショ
![getting-started.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2320268/6d7008d2-4b7f-5976-a58b-82efa3bbb6fb.png)
英語でしたが、ブラウザの日本語翻訳かけても割と自然な文章で読めました。それほど難しい英語は出てこない(機械学習の用語はともかく)ので、英語のままでも問題なく取り掛かることはできそうです。

- 初めてアクセスするとGetting Startedのダイアログが表示されました
  - とりあえず読みました
  - →読み終えるとどうやら、次はモデルというものを作成するところからスタートするらしいです
- モデルを作る
- 作ったモデルを学習させる
  - →評価ボタンが押せるようになるので、とりあえずモデルを評価します。
  - 動画が視聴できるので、試しに視聴してみると改善したい点がでてきました。

↓↓次作るモデルは、改善策を投入しよう↓↓
- 改善策を取り入れたモデルを新規作成する
- モデルを評価する
  - 動画を視聴して、改善策がうまくいったかどうかチェックする
- ...以下、改善策投入&評価のループ



# あれ、この改善プロセスはあのゲームに似ているぞ...?
アーマード・コア フォーミュラフロント(以下ACFFと記載)というゲームはご存知でしょうか？
20年前のゲームなので知らない方も多くいらっしゃると思います。簡単に説明します。

アーマード・コアシリーズは 1997年に株式会社フロム・ソフトウェアより第一作が初代プレイステーションで発売されました。
プレイヤー自身の手でアーマード・コアと呼ばれるメカを操縦します。
初代プレイステーションのコントローラは当初、アナログスティックは存在しませんでした。


<a title="Evan-Amos, Public domain, via Wikimedia Commons" href="https://commons.wikimedia.org/wiki/File:PlayStation_SCPH_1000_with_Controller.png"><img width="512" alt="PlayStation SCPH 1000 with Controller" src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d6/PlayStation_SCPH_1000_with_Controller.png/512px-PlayStation_SCPH_1000_with_Controller.png?20161215224524"></a>

↑初期のコントローラにはアナログスティックはありません。

そのため、視点移動や機体の移動はアナログスティックを使用せず、全てボタン操作で行います。
アナログスティック登場以降のゲームからゲームを触り始めた方からすると、非常に奇妙に思われるかもしれません。

- 右を向きたいときは十字ボタンの右を押します
- 左を向きたいときは十字ボタンの左を押します
- (カメラは前方を見据えたまま)右に移動したいときはR1ボタンを押します
- (カメラは前方を見据えたまま)左に移動したいときはL1ボタンを押します
- 上を向きたい場合は、L2ボタンを押します
- 下を向きたい場合は、R2ボタンを押します

デフォルトではこのようなキーアサインになっています。
私も初めて操作した時はかなり戸惑いましたが、数日で慣れました。
アナログスティック登場後も、このキーアサインのままプレイし続けることが可能でした。
私も、このキーアサインのままプレイし続けておりました。
なによりも、デフォルトのキーコンが身体に馴染んでおり、変更が困難でした。

2004年になって、PSPが発売されました。
<a title="Psp-1000.jpg: Evan-Amos
derivative work: Alphathon™, CC BY-SA 3.0 &lt;https://creativecommons.org/licenses/by-sa/3.0&gt;, via Wikimedia Commons" href="https://commons.wikimedia.org/wiki/File:PSP-1000.png"><img width="512" alt="PSP-1000" src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/PSP-1000.png/512px-PSP-1000.png?20120225163748"></a>
しかし、PSPにはL2ボタンとR2ボタンが存在しません。
これでは、上下を向くことができないのです。
普通に考えると、アーマード・コアの操縦など、出来ないように思われます。

しかし、おそらく時代の流れでアーマード・コアシリーズもPSPへ参入することになったのでしょう。
アーマード・コアフォーミュラフロントというタイトルが発表されました。

驚くべきことに、製品説明を読むと、自分でアーマード・コア(AC)を操作して遊ぶゲームではないようです。

AIの設定をカスタマイズして、敵のACとプレイヤーがカスタマイズしたAC(AI操縦)を戦わせてストーリーを進めるようなゲーム性でした。

CMが印象的でしたので今でも記憶に残っています。

```
勝敗は、
頭脳が決める。
```
というキャッチコピーとともに、
AIを思い通りに動かせずに、対戦相手に負けてしまうといった演出のCMでした。

:::note info
(考察)なぜAI操作オンリーの作品が登場したのか？

PSPではL2,R2ボタンが存在しないため、アーマード・コアの操縦はできない
↓
AIで操作するゲームが誕生した
 
という思考ロジックは私個人が勝手に妄想しているだけなので、本当のところは分かりません...。 その数年後にポータブルシリーズとして、プレイヤーの手で操作するアーマード・コア作品が発売されました。大会も開催されました。実際のところは人が操作するにはなんとかボタンが足りたという事です。
:::


何はともあれ、ここでお伝えしたいのは、ACFFという、AI操作オンリーのアーマード・コア作品が誕生していた、ということです。

## ACFFにおける改善のプロセス
AWS DeepRacerの改善のプロセスと比較して、どのように似ているのかについて参考のため、ACFFにおける改善のプロセスについてもご紹介したいと思います。

おそらく、自分の好きな(or敵に勝てる)戦闘スタイルとして動く様なAI設定やパーツ選定を初めに考えることになると思います。

「この武器を使いたいんだ！」というのがあれば、その武器にあった戦闘スタイルを選択することになるでしょう。
(あるいは、デフォルトで5種類の機体がセットされているので、テキトーにどれかを選択して始められた方もいらっしゃると思います。)
戦闘スタイルもいくつか例を上げましょう。まずは敵との距離についてです。
- 近距離戦闘型
- 中距離戦闘型
- 遠距離戦闘型

このゲームにはブレードという武器が存在するため、ブレードを運用するには近距離戦闘型の要素が必要になってくると思います。
また、機体の移動スピードもパーツ選定で変更できるので、この辺りも考慮が必要です。
各パーツの重量やブーストパーツによって、ブースト移動時の速度が大きく変わるため、これらのパーツ選定については注意が必要です。
自機の移動がノロイと、遠距離戦闘をしたいのにもかかわらず、敵の移動の方が早ければ、いつまで経っても遠距離戦闘に必要な距離を確保することができません。

そう、AI設定をしたからといって、期待通りの動きをしてくれるとは限らないのです。
対戦動画を観戦して、期待通りの動きになっているか確認します。
もし、期待通りになっていなければ、AI設定を見直して、期待通りの動きになる様に修正します。もし、パーツ構成に問題があると考えるなら、パーツもより適切なものに変更します。

また、自分がカスタマイズしたAIを実践に投入して、対戦を観戦していくなかで、

```
「あの戦闘スタイルなら勝てるかも」
```
や
```
「今回は負けたけど、自APが底を尽くよりも早く敵のAPを0にすれば良いので、
  次は瞬間火力に特化した武装に変更すればダメージレースで勝てるはず」
```
など、
```
「遠距離や中距離での戦闘なら被弾は少ないが、
  近距離戦闘になった途端に被弾が増大し、ダメージレースで負けてしまう。　
  であれば、AI設定を遠距離や中距離のみの行動をとるように調整すれば勝てるはず。
  こちらの武器は中・遠距離でも当たるものにする必要がある。」
```

などの改善案が思い浮かびます。

AIならではの戦闘スタイルも考えられます。
武器には威力と弾速に優れたスナイパーライフルがあるのですが、遠距離まで敵をロックできる反面、ロックできる範囲が狭くなってしまい、敵を補足する事が困難になる、というシステム面での長所と短所が設定されています。
人が操作するのであればこうしたゲームバランスは大変うまく働くのですが...
そう、ACFFはAIが操作するのです。サイトが狭くても敵をロックできたりすると考えられます。
つまり、人が操作するのであれば遠距離でスナイパーライフル、という選択がメジャーですが、
AIが操作するのであれば、中、近距離でスナイパーライフル、という選択が取れるかもしれません。

アーマード・コアにおけるスナイパーライフルなどの武器特性については、
YouTubeチャンネル「桜井政博のゲーム作るには」の2024/01/16に投稿されました、
「カスタマイズは想像で遊ぶ 【企画・ゲーム設計】」
という動画でも取り上げられていました。
<iframe width="560" height="315" src="https://www.youtube.com/embed/PGk_iblB4AI?si=XFPJMC5pwe2xqK_i&amp;start=90" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

ミサイルの回避についても触れておきましょう。
熟練のプレイヤー(人)であれば、ミサイルをほぼ全弾回避する事が可能です。
しかし、AIではミサイルを回避(素避け)することは困難です。
ミサイルを回避することはあきらめて、デコイというもので自機の代わりにミサイルの的になってもらうことで、ミサイルの被弾を抑えます。
このように、熟練プレイヤー(人)ならではの「ミサイル素避け」のように特定の行動をとる操縦はAIでは上手く行動してくれないため、それを補うパーツ選択が重要な場合があります。
私がACFFをプレイしていく中で印象的だったのは、AIにふさわしいパーツ選択をする、というよりかは、

「操作のおぼつかない新人プレイヤー(人)でも敵に勝てるよう、ふさわしいパーツ選択をしてあげよう」
という気持ちになりました。

つまり、操作のおぼつかない初心者(人)でも、敵に勝ちやすいアセンブルをしようという戦略が私の中で出来上がったのです。

戦闘スタイルで距離について述べましたが、そのほかの要素についても触れましょう
機体の重さについてです
| 機体の重さ | 移動 | 防御 |
|:-:|:-:|:-:|
| 軽量  | 素早い | 脆い|
| 中量  | 普通 | 普通 |
| 重量  | 遅い | 素晴らしい|

重量級は防御力に優れていますが、移動が遅いため、敵からの弾を被弾しやすくなります。
軽量級は防御力に優れてはいませんが、移動は素早いため、敵の弾を避ける力を秘めています。

つまり、ある程度の熟練プレイヤーの目にはこう見えています

| 機体の重さ | 移動 | 防御 | 人の操縦による回避のしやすさ |
|:-:|:-:|:-:|:-: |
| 軽量  | 素早い | 脆い| 素晴らしい|
| 中量  | 普通 | 普通 | まぁ、なんとか...。 |
| 重量  | 遅い | 素晴らしい|難しい|


一部、武器カテゴリ毎の適正距離についても述べておきます

| 距離 | 武器のカテゴリ |
|:-:|:-:|
| 近距離  |  ブレード、ハンドガン、ライフル、グレネード、ショットガン、マシンガン、バズーカ、火炎放射器 |
| 中距離 | ライフル、バズーカ、ミサイル  |
| 遠距離 |  スナイパーライフル、ミサイル |

距離と武器のカテゴリについては、私の経験に基づいて記載しているため、一概にこれが正しいとは限りません。
AIにこれらの武器を装備させて、実践投入し、
実戦の動画を確認して、次の改善策を思いつき、うまく改善策を投入・成功させることで、より良いAIが生まれます。

## AWS DeepRacerにおける改善のプロセス(素人版)
私はAWS DeepRacer素人なので、素人なりの改善プロセスについて記述します。

モデルについてですが、

1. modelを作成
2. modelに学習させる
3. modelを評価する

のステップを行うことになると思います。

スクリーンショット等をご覧いただいた方がイメージしやすいかと思うので、貼ります。

1. modelを作成
- 名前を入力し、学習させるサーキットコースを選択(時計回り、反時計回りかの選択が可能)
- レースタイプを選択
  - タイムトライアル
  - 物体回避
  - 競争
- 強化アルゴリズムとハイパーパラメータの指定
　　- PPOかSACを選択
- PPOの場合のハイパーパラメータの一覧
  - 勾配降下法のバッチサイズ
  - エポック数
  - 学習率
  - エントロピー
  - 割引係数
  - 損失タイプ
  - Number of experience episodes between each policy-updating iteration
    - ※1でグレーアウトされており、変更できませんでした
- SACの場合のハイパーパラメータの一覧
  - 勾配降下法のバッチサイズ
  - 学習率
  - SAC alpha(α) value
  - 割引係数
  - 損失タイプ
  - Number of experience episodes between each policy-updating iteration
    - 各ポリシー更新時に、何エピソード分のデータを収集するか

- アクションスペース(行動空間)の定義
　- ステアリングの角度とスピードの範囲を指定できます
　- デフォルトでは次のようになっています
　　　- ステアリング角度: -30度~30度
　　　- スピード: 0.5m/s ~ 1m/s
![default-action-space.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2320268/3241bebb-faab-ec1e-a72f-ee9ae3c5c03d.png)
私はひとまず、PPOでデフォルト設定のままモデルを作成しました。


2. modelに学習させる

![new-fir-learning.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2320268/38f43da5-aed0-009a-0bed-c0e47aeb1118.png)
学習中のビデオストリーミングは、学習が完了すると視聴できなくなります。学習中のみビデオストリーミングで様子が確認できます。


 3: modelを評価する


![new-fir-eval.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2320268/3bbd4fb6-dd22-1c80-a128-8f71644f771d.png)
レースタイプはタイムトライアルを選択しました。
今回使用したモデルでは、33.975で一周できたようです。

↓1つ目のモデルの成績
| Trial | Time(MM:SS.mmm) | Off-track |
|:-:|:-:|:-:|
| 1 |00:33.975| 0 |
| 2 |00:32.288| 0 |
| 3 |00:33.030| 0 |



評価は評価完了後も動画が閲覧できます
<iframe width="560" height="315" src="https://www.youtube.com/embed/9N5KTViG_wk?si=kimbUT7YYNxN7ANX" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

動画を見て改善点について考えてみます。
タイムトライアルですので、単純に走るスピードをあげれば良いと考えました。
それに、コースも複雑なものではなく大変シンプルなものを選んでいます。おそらく、タイムは良くなるでしょう。
次に作成するモデルでは行動空間として、スピードの範囲を早い時速も含めるように範囲を広げてみます。
![new-sec-model-settings.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2320268/02ca345c-1072-2977-ed3e-7ec9830c8aa4.png)

改善策を投入した2つ目のモデルの評価ムービーです
<iframe width="560" height="315" src="https://www.youtube.com/embed/Gyu75aASGQc?si=R04jSjc-SMg02epM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
1週目は狙い通り早いタイムでゴールする事ができました。
しかし、2週目と3週目はそれぞれで3回と2回コースアウトしてしまい、ペナルティが加算されてしまいました。（一度のコースアウトで2秒加算）
アクションスペースで範囲指定した、速度を上げてもコースアウトしない様な工夫が必要そうです。

↓2つ目のモデルの成績
| Trial | Time(MM:SS.mmm) | Off-track |
|:-:|:-:|:-:|
| 1 |00:16.267| 0 |
| 2 |00:24.317| 3 |
| 3 |00:22.276| 2 |



# で、機械学習のスキルは身についたの？
ここまでは、AWS DeepRacerの車両において、速度とステアリング角度の指定をするくらいしかしていません。(他はデフォルト設定のままでやりました。)
モデル評価時の走りを動画で確認して、改善すべきポイントを考え、次のモデルを改善します。
改善したであろうモデルを評価させて、動画を確認して、さらに改善すべきポイントを考えます。
この繰り返ししか私はまだしていません。
評価関数の指定などはできていません。
そう、現時点では、まるっきりやっていることはACFFと同様なのです。
ゲーム性として考えた時にモデルの学習に60分かかるため、ACFFと比較すればそこがボトルネックです。
ACFFの改善のプロセスを詳細に語ったのには理由があります。
AIならではのパーツ選択がACFFでは有効な選択肢の一つです。
AWS DeepRacerでもこの考え方は活きるシーンはあるかもしれません。
ACFFの経験が、AWS DeepRacerのAI作成に活かせるかもしれない。そう考えたためです。

## 評価関数をいじる
とはいえ、やはり評価関数をいじってみたくなりました。 改善したいアイデアについては
株式会社DNPデジタルソリューションズ 様のこちらのページで公開されていたPDF資料16ページに書かれている図そのままです。
↓こちらのページの「PDF ver」の画像がボタンになっています

https://www.dnp-ds.co.jp/tech_fun/tech_fun_007.html

コースによってはうねうねとしたコースではあるものの、直線的なウネウネコースであれば、車両が直線的なコースどりをすれば、より短いタイムで走ることができる場合があると思います。



素人の私には、評価関数の書き方はよくわからなかったので、ChatGPTに教えてもらいました。

私からの質問(一部抜粋)
```
たとえば、コースがうねうねしているとして、センターラインに沿って走るよりも、
センターラインから多少ズレても、まっすぐ走ることで、早くコースを走り抜けれる場合があるとします。
これを実現してくれるモデルはどのようにしたら作れるでしょうか？
```

これにたいしてChatGPTによる回答は(一部抜粋)
```
(1) 報酬関数の設計
コース中央に沿うことを奨励するだけではなく、
「ゴールに向けて効率的に進む」行動を評価する報酬関数を設計します。
例: ゴール方向を意識した報酬関数
- 入力データ: 車両の進行方向(進行ベクトル)とゴール方向(ゴールベクトル)のなす角。
- 設計意図: ゴールに向けた直進を奨励し、不要な曲がりを抑制。

この設計では、ゴールに向かう進行方向を重視し、細かい曲がりが必要ない場面では直進を奨励します。
```

```Python
import math

def reward_function(params):

    # パラメータの取得
    heading = params['heading']  # 車両の進行方向（度数）
    waypoints = params['waypoints']  # コースのウェイポイント
    closest_waypoints = params['closest_waypoints']  # 現在の位置に最も近いウェイポイントのインデックス

    # ゴール方向を計算（ウェイポイントから次の目標点へのベクトル）
    next_point = waypoints[closest_waypoints[1]]
    prev_point = waypoints[closest_waypoints[0]]
    track_direction = math.atan2(next_point[1] - prev_point[1], next_point[0] - prev_point[0])
    track_direction = math.degrees(track_direction)  # ラジアンを度に変換

    # 車両の進行方向とコースの方向の差を計算
    direction_diff = abs(track_direction - heading)
    if direction_diff > 180:
        direction_diff = 360 - direction_diff

    # 報酬を計算
    reward = 1.0 - (direction_diff / 180.0)

    return max(reward, 0.1)  # 報酬がゼロにならないようにする
```

```
(2)行動空間の調整
効率的に走るために、ステアリング角度を広めに取りつつも、細かい調整が可能なようにする必要があります。

行動空間例
- ステアリング角度: -20° 〜 +20°(±5°刻み)
- 速度: 1.0 〜 3.0 m/s (加速しやすい範囲)

この行動空間では、エージェントが直進を選択する余地を広げ、必要な場合にのみ曲がれるようになります。
```

```
(3) 視覚入力を活用
カメラ映像を使用することで、エージェントがコースの形状や周囲の状況を理解し、効率的なルートを判断できるようにします。

画像解析の目的
- コースの全体像を把握: 現在の位置だけでなく、遠くのカーブや直線の状況を予測
- 最適ルートの推定: コースアウトせずに進める範囲内で最短ルートを推定。

AWS DeepRacerでは、カメラ映像を自動的にニューラルネットワークに取り込む仕組みがあります。
そのため、画像解析の詳細な実装をしなくても、
以下のポイントを報酬関数に組み込むことでカメラ映像の効果を引き出せます: 

- 車両がトラックの左側や右側を走行している場合にも適切な報酬を与える
- コースの外縁近くを走行することを奨励する報酬を追加する。
```

```
(4)トラック幅を活用
コースの幅を考慮し、トラックの中央に執着せず、効率の良い走行を奨励します。

例: トラック幅を利用した報酬
- トラックの中央からの距離ではなく、トラックの外縁からの距離を重視。
- トラックの許容範囲内で直進的に進むほど高い報酬を付与。

```

```Python
def reward_function(params):
    track_width = params['track_width']
    distance_from_center = params['distance_from_center']

    # 距離がトラック幅の50%未満であれば高い報酬
    if distance_from_center < rack_width *0.5:
        reward = 1.0
    else:
        reward = 0.1 # トラック外側では低い報酬

    return reward

```

```
(1) 報酬関数の設計
(2) 行動空間の調整
(3) 視覚入力を活用
(4) トラック幅を活用
```
と(1)~(4)のアドバイスをいただきました。
(1)についてはモデル作成時に関数を入力できるので、それをコピペで貼り付ければ良さそうです。
(3)と(4)を含めて評価関数に記述する方法がぱっと見でわからなかったので、とりあえず(1)と(2)のみ取り込んだモデルを作成し、直線的にウネウネとした道があるコースを選択し学習・評価を行いました。
(学習については1時間かけました。)

モデルにはwindingという名前をつけました。

<iframe width="560" height="315" src="https://www.youtube.com/embed/TFG4-5hoY60?si=qYMg1YRatfo6-cIr" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

動画を見ていただくとわかるのですが、頻繁にコースアウトしてしまいます。
そこで改善策を考えてみました。

ChatGPTに教えてもらった評価関数では、直近2つのwaypointsからベクトルを算出しています。
2つのwaypointsだけでなく、数個先のwaypointsも考慮すれば改善するのではないかと考えました。

↓もう一度ChatGPTに質問します。
```
closest_waypoints[0]と[1]をチェックしていますが、[2],[3],[4]を確認するのは良くないのでしょうか？
```

↓ChatGPTからの回答抜粋
```Python
import math

def reward_function(params):

    # パラメータの取得
    heading = params['heading']  # 車両の進行方向（度数）
    waypoints = params['waypoints']  # 全てのウェイポイント
    closest_waypoints = params['closest_waypoints']  # 最も近いウェイポイントのインデックス

    # 近くの複数ウェイポイントを取得
    next_point = waypoints[closest_waypoints[1]]
    second_next_point = waypoints[(closest_waypoints[1] + 1) % len(waypoints)]
    third_next_point = waypoints[(closest_waypoints[1] + 2) % len(waypoints)]

    # 複数ウェイポイントの方向を計算（平均方向）
    avg_direction = math.atan2(
        (second_next_point[1] - next_point[1]) + (third_next_point[1] - second_next_point[1]),
        (second_next_point[0] - next_point[0]) + (third_next_point[0] - second_next_point[0])
    )
    avg_direction = math.degrees(avg_direction)

    # 車両の進行方向と平均方向の差を計算
    direction_diff = abs(avg_direction - heading)
    if direction_diff > 180:
        direction_diff = 360 - direction_diff

    # 報酬を計算
    reward = 1.0 - (direction_diff / 180.0)

    return max(reward, 0.1)  # 報酬がゼロにならないようにする
```

この評価関数を投入したモデルをwinding2という名前をつけて、1時間の学習を行った後で評価を行いました。

<iframe width="560" height="315" src="https://www.youtube.com/embed/iPBwDsid2Ik?si=lDIg9fq-GBzsGVIC" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

改善策を投入したはずのモデルでも頻繁にコースアウトしてしまいました。
行動空間を変更して改善するか、学習時間を変えてみるなど試してみると良いかもしれません。
または(1)~(4)のアドバイスをしっかりと取り込んで改善するかを確認する、
あるいは、根本的に改善策の実装方針を変えるか、などをした方が良さそうです。

簡単に改善はできない、かなり歯応えのあるコンテンツであることは感じ取れます。


# ACFF経験者向け、AWS DeepRacerの特徴
最後に、せっかくなのでACFFとAWS DeepRacerのざっくり比較を行いました。テキトーなので、真に受けないでください。
ACFF経験者であれば、こちらの表をご覧いただくことで、AWS DeepRacerにとっつきやすくなるのではと思います。

|| ACFF | AWS DeepRacer |
|:-:|:-:|:-:|
|AIの設定| グラフをいじるorチップを指定。チップの場合は、0:00~3:00の間で30秒ごとに区間を分けて、優先度が高い行動をとる指定が可能 | 行動空間として、速度とステアリング角度の範囲指定, 自作の評価関数などの設定が可能 |
|AIの反映| 一瞬 | 強化学習のため、学習に時間が必要。デフォルト設定だと1時間かかる。学習に時間をかけるほど良いAIが出来上がる場合もあり、逆効果の場合もある。 |
|AIの評価| 対戦モードを観戦(早送りが可能)| 動画が閲覧できるまで数分かかる|
|費用|ソフト買い切り(ただし、そもそも強化学習ではない)|[料金表](https://aws.amazon.com/jp/deepracer/pricing/?nc=sn&loc=7)をご参照ください|
|改善プロセス|前述の通り|前述の通り|
|リーグ|オンライン(フォーミュラW)はサービス終了。オフラインとしてはcpuのみのリーグが存在|AWS DeepRacerリーグは2024年シーズンのリーグが最後のリーグ。オンラインのリーグはサービスを継続している模様([リーダーボード](https://awsdeepracerleagueleaderboard.com/)) ※執筆者が詳しく調べられていないため、詳細な情報は読者様にて調べていただくことをおすすめします。|
|対戦|友人のチームデータを持ち寄ればAI同士で対戦可能。またはPSP版であればアドホック通信でローカル対戦が可能。アドホックパーティ経由であればインターネット越しに対戦が可能と思われる。|※本記事の執筆者が詳しく調べられていないため不明|
|機体のカスタマイズ|100億を越える組み合わせ(たぶん)|シングルカメラorマルチカメラ x LIDARセンサーのありなし|
|サービス終了の可能性|PSPやPS2のソフトとハードウェアが壊れるまで遊べる|触れるうちに触っておくのが無難|



# 商標について
- AWS DeepRacerはアマゾン　テクノロジーズ　インコーポレイテッドの商標または登録商標です
- プレイステーション は株式会社ソニー・インタラクティブエンタテインメントの商標または登録商標です
- PSP は株式会社ソニー・インタラクティブエンタテインメントの商標または登録商標です
- PS2 は株式会社ソニー・インタラクティブエンタテインメントの商標または登録商標です
- アーマードコア は株式会社フロム・ソフトウェアの商標または登録商標です