HN 日本語サマリー

← 一覧へ戻る
AI・機械学習

強化学習によるドローンレースのレシピ

A recipe for drone racing with reinforcement learning (mrandri19.github.io)

4 pointsby daww1 コメント

要約

この記事は、クアッドコプターシミュレーションシリーズの最終回で、強化学習(RL)を用いてドローンを飛行させる方法を解説しています。ホバリングからゲート通過まで、RLポリシーの訓練に役立つ実用的なテクニックやヒントが集められています。厳密な証明ではなく、経験則に基づいたレシピ形式で、コードの重要な部分のみが示されています。

全文翻訳

この記事は、クアッドコプターシミュレーションに関するシリーズの3回目で最終回です。最初の投稿と2番目の投稿では、まず2Dで、次に3Dでクアッドコプターの運動方程式を導出し、シミュレーションしました。この記事では、まず固定点にホバリングし、次に一連のゲートを通過するように、強化学習ポリシーを訓練します。以前の投稿はチュートリアル的でしたが、フリーボディ図から始めて、段階的にシミュレーションコードに到達しました。この記事はむしろレシピ的で、クアッドコプターレース用のRLポリシーを訓練するのに役立つと感じたトリックやテクニックのコレクションです。これらの方法がなぜ機能するのかについての厳密な証明はなく、経験的なものだけです。ウォークスルーではなくレシピであるため、この記事では各決定にとって重要なコードのみを示します。完全な環境はGitHubのmrandri19/quadcopter-racingにあります。ここに、訓練されたポリシーが8ゲートのループトラックを飛行している様子を示します。記事全体を通しての視覚化はRerunを使用して行われています。段階的なアプローチドローンレースシミュレーション、RL環境、報酬、モデルをすべて一度に実装しようとしても、うまくいきません。あまりにも多くのことがわずかにずれている可能性があり、ポリシーのパフォーマンスに影響を与え、デバッグする良い方法がないのです。そのため、タスクを3つの段階に分割し、それぞれが互いに積み重なるようにしました。「RLのハローワールド」:PPOとベクトル化されたstable-baselines3 envを使用して、倒立振子の制御問題を解決するポリシーを訓練する。MuJoCoを使用してカスタムクアッドコプターRL環境を構築し、アクションと観測スペースを選択し、高レベルのRLベースと低レベルのPコントローラーを分割し、ホバリング報酬を設定する。ホバリングからレースへと環境を拡張し、新しい報酬、観測、ランダム初期化設計を追加する。ベクトル化されたPPOで倒立振子を解決基本的なことから始め、ベクトル化されたRL問題をセットアップしましょう。stable-baselines3のPPOと、ベクトル化されたInvertedPendulum-v5(cartpole)環境を使用し、ポリシーが最大エピソード長に達することを確認します。from stable_baselines3 import PPO from stable_baselines3.common.env_util import make_vec_env from stable_baselines3.common.policies import ActorCriticPolicy def main() -> None: env = make_vec_env("InvertedPendulum-v5", n_envs=128) model = PPO( policy=ActorCriticPolicy, env=env, learning_rate=3e-3, # 3e-4 (default lr) * sqrt(n_envs) ~= 3e-3 n_steps=512, # default 2048より小さい、128 envsでそれほど多くのステップは不要 batch_size=1024, # default 64より大きい、効率向上のため n_epochs=5, # default 10より小さい、トレーニングを高速化、10回再フィットする必要はない verbose=1, ) model.learn(total_timesteps=750_000) ハイパーパラメータの選択について簡単に説明します。ロールアウトサイズはn_envs * n_stepsです。128個の環境とそれぞれ512ステップで、PPOはポリシー更新ごとに65,536の遷移を収集します。このバッチを十分に大きくすることが重要です。なぜなら、各PPO更新でトレーニングするデータセットに多様性を持たせたいからです。PPO更新の回数はtotal_timesteps / rollout_size、ここでは約11回です。ロールアウトサイズが十分に大きい場合、モデルがトレーニング中に「進化」するためには十分な更新回数も必要です。これはデータセットが固定されている教師あり学習ではありません。ポリシーが進化するにつれて、ロールアウトデータに新しいパターンが出現するため、この空間を探索するには十分なPPO更新が必要です。勾配ステップの回数はtotal_timesteps * n_epochs / batch_sizeです。各ロールアウトはbatch_sizeのミニバッチに分割され、n_epochs回再利用されます。バッチサイズが大きいほど勾配のノイズは少なくなりますが、PPO更新あたりのトレーニングイテレーションは少なくなります。結果この環境では、各生存ステップで+1の報酬が得られるため、エピソード長と報酬は正確に一致します。トレーニングは128個の並列環境で750kタイムステップ実行され、約11回のPPO更新に相当します。エピソード長は最初の更新からスムーズに増加し、600kステップまでにポリシーは1,000ステップのキャップに近づきます。レシピ:まずトイタスクでPPOとそのハイパーパラメータを検証してください。なぜなら、「アルゴリズムの設定ミス」と「環境が間違っている」ことを分離できるからです。カスタムクアッドコプター環境でのホバリング学習PPOとハイパーパラメータがトイタスクで機能することを示したので、クアッドコプター環境を開発し、簡単なホバリング報酬を設定しましょう。物理シミュレーターとしてMuJoCoを使用します。導入で述べたように、環境を実装するために必要なすべてのコードを網羅するのではなく、議論する価値のある部分のみを示します。残りはsrc/quadcopter_racing/part_2.pyにあります。アクション設計:CTBR + Pコントローラーポリシーは直接4つのモーターコマンドを出力しません。代わりに、集合推力とボディレート(CTBR)コマンドを出力します。これは、質量正規化された推力と望ましいロール、ピッチ、ヨーレートです。これは、Champion-level drone racing using deep reinforcement learningやDeep Drone Acrobatsなどのいくつかの論文で使用されているアクション設計です。低レベルの比例(P)コントローラーが、現在のボディフレーム角速度をフィードバックとして使用して、これを個々のモーターコマンドに変換します。def _controller( self, omega_body: NDArray, denormalized_actions: NDArray ) -> NDArray: f_des = denormalized_actions[:, 0] w_des = denormalized_actions[:, 1:4] w = omega_body Jw = self._J_diag * w u_w = self._K_p * (w_des - w) + np.cross(w, Jw) f = f_des * self._g * self._mass / self._max_thrust / 4 u_p, u_q, u_r = u_w[:, 0], u_w[:, 1], u_w[:, 2] motor = np.empty((self.num_envs, 4), dtype=np.float32) motor[:, 0] = f - u_p - u_q - u_r motor[:, 1] = f + u_p - u_q + u_r motor[:, 2] = f - u_p + u_q + u_r motor[:, 3] = f + u_p + u_q - u_r np.clip(motor, 0, 1, out=motor) return motor CTBRは、生のモーターコマンドよりもポリシーが学習しやすいアクション空間です。低レベルコントローラーが高速な姿勢レートダイナミクスを処理し、ポリシーは推力と望ましい回転について推論します。これは、BetaflightのようなほとんどのコントローラーがCTBR入力を受け取るため、実際のドローンにポリシーをデプロイしようとする際にも役立ちます。コードは、np.cross(w, Jw)という項を除いて、単純な比例コントローラーを実装しています。それが何をするのかを理解するために、ボディフレーム角速度{omega}のオイラー回転方程式を書いてみましょう。J {dot{\omega}} = {\tau} - {\omega} imes J {\omega} {\omega} imes J{\omega}という項は、回転する(ボディ)フレームで方程式を書くことから生じるジャイロスコピック結合です。これは3つの回転軸を互いに結合するため、純粋なロールレートがこの項を通じてピッチまたはヨー加速を生成する可能性があります。もしトルク{\tau} = K_p ({\omega}_{des} - {\omega})を直接コマンドした場合、この結合項は閉ループダイナミクスに残ってしまい、設計したい単純な比例応答を歪めてしまいます。これを+記号で戻してu_w = K_p * ({\omega}_{des} - {\omega}) + {\omega} imes J{\omega}とすると、オイラー方程式に代入したときに正確にキャンセルされます。J {dot{\omega}} = {\underbrace{K_p ({\omega}_{des} - {\omega}) + {\omega} imes J {\omega}}_{{\tau}}} - {\omega} imes J {\omega} = K_p ({\omega}_{des} - {\omega}) {\omega} imes J{\omega}の2つの項がキャンセルされ、各軸でクリーンな線形一次応答が得られます。これはフィードバック線形化(動的逆数とも呼ばれる)の小さな例です。既知の非線形ダイナミクスを使用して、その非線形性をキャンセルする正確なトルクを計算するため、残りの閉ループシステムは、ゲインK_pを設計した単純な線形システムのように振る舞います。レシピ:ポリシーにCTBRを出力させ、Pコントローラーにレートループを閉じさせるようにしてください。なぜなら、コントローラーが高速な姿勢ダイナミクスを吸収し、同じインターフェースが実際のハードウェアでも実行されるからです。観測設計観測は13次元のMuJoCoフリーボディ状態です。位置、姿勢クォータニオン、線形速度、ボディフレーム角速度です。obs[:, 0:3] = qpos[:, :3] # 位置 obs[:, 3:7] = qpos[:, 3:7] # 姿勢クォータニオン obs[:, 7:10] = qvel[:, :3] # 線形速度 obs[:, 10:13] = omega_body # ボディフレーム角速度 ホバリング報酬報酬は、ターゲットへの負の距離に対するポテンシャルベースの進行項、姿勢ペナルティ、クラッシュペナルティ、および小さなアリ