はじめに
PyBullet (Python上で動く物理シミュレータ) を使用して,ロボットアームを可視化して,動かしたい.
前記事では,エージェントの学習方法として "SARSA (TD学習)" を採用して,PyBullet 内の2軸ロボットアームの強化学習を実装した.
前記事の問題点は,SARSAが "on-policy" の学習手法であるため,行動の選択に使う方策と,Q値の更新に使う方策が同一となる点である.これは,探索(ε-greedy法)の影響を受けたQ値が学習されるため,最適方策への収束が遅くなる可能性がある.
本記事では,エージェントの学習方法として "Q学習 (off-policy TD学習)" を採用する.Q学習では,次状態での最大Q値を使ってQ値を更新するため,行動の選択方策に依らず,"最適方策" への収束を目指すことができる.
グリッパー付きの2軸ロボットアームを使用して,干渉物が存在しない環境下にて,複数のカメラから把持物体の位置を取得して,物体を把持する(下図はPyBullet上のグリッパー付き2軸ロボットアームである).赤枠がグリッパー,緑枠が把持したい物体,青枠がカメラ,青色の球が初期位置である.

本記事を実施してできる最終的なものは下記動画の通りである.エージェントの学習方法は "Q学習" である.
下記は,学習中の強化学習による経路生成の動画である.
下記は,学習後の強化学習による経路生成の動画である.
本記事で実装すること
- エージェントの学習方法は"Q学習"
本記事では実装できないこと (将来実装したい内容)
エージェントの学習手法
- DQN
- Actor-Critic
動作環境
- macOS Sequoia (バージョン15.5)
- Python3 (3.13.3)
- PyBullet (3.25) (物理シミュレータ)
- Numpy (2.3.0) (数値計算用ライブラリ)
PyBullet のインストール方法
PyBullet のインストール方法については下記にて,説明したため,割愛する.
PyBulletの使用方法
Pythonの物理シミュレータであるPyBulletの使用方法について説明する.
下記リンクのPyBullet公式で調べながら,PyBulletを使用している.
上記リンクを開くと,下図のようなサイトに飛ぶ.使用方法を調べるときは,下図の赤枠内の「PYBULLET QUICKSTART GUIDE」タグをクリックする.

「PYBULLET QUICKSTART GUIDE」タグをクリックすると,下図のようなドキュメントを見ることができる.基本的には,ドキュメントに記載されている関数の使用方法を見て,ソースコードを作成している.

PyBulletの関数の引数や戻り値をもっと知りたいのでしたら,上記ドキュメントを見た方がわかりやすいです.
2軸ロボットアームの定義
2軸ロボットアームの定義については下記にて,説明したため,割愛する.
グリッパーの定義
グリッパーの定義については下記にて,説明したため,割愛する.
グリッパー付き2軸ロボットアームのURDF作成
グリッパー付き2軸ロボットアームのURDF作成については下記にて,説明したため,割愛する.
URDFに関しては,下記にて説明したため,割愛する.
カメラによる把持物体の2次元位置を取得
PyBulletでカメラ情報を取得する方法については下記にて,説明したため,割愛する.
強化学習に関して
強化学習は機械学習の一つの種類である.強化学習とは,自分で試行錯誤をしてデータを集め,詰めたデータから良い行動を学習することである.
強化学習のイメージは下図の通りである.
1:「環境」から「エージェント」に対して,「状態」を渡す
2:「エージェント」が「状態」より,「行動」を選択する
3:「エージェント」から「環境」に対して,「行動」を実行する
4:「環境」が「行動」より,「状態」を更新する
5:「環境」から「エージェント」に対して,「報酬」を渡す
6:「エージェント」が「報酬」より,「学習」する
ここで,用語の定義をする.
| 用語 | 用語の定義 |
|---|---|
| エージェント | 行動の主体者 |
| 環境 | エージェントと相互作用を行う対象 |
| 状態 | エージェントの行動によって置かれるエージェントの状況 |
| 行動 | エージェントが起こすアクション |
| 報酬 | エージェントの行動に対する環境からのフィードバック |
本記事での "環境" は "PuBullet上のロボットと環境" である."エージェント" は "ロボットを動かすための頭脳" である.
以下では,本記事で使用する "環境" と "エージェント" を説明していく.
強化学習の環境に関して
強化学習の環境に関しては下記にて,説明したため本記事では割愛する.
強化学習のエージェントに関して
強化学習のエージェントに関して説明する.強化学習の全体像の中で,下図の赤枠で囲んだ「エージェント」を説明する.
エージェントは,環境から「状態」を受け取り,「行動」を行い,「報酬」を得て,次の「状態」へ遷移する.下記のように1エピソードが完了するまで,「状態(S)」,「行動(A)」,「報酬(R)」,「状態(S)」のループとなる.
$S_{0}, A_{0}, R_{0}, S_{1}, A_{1}, R_{1}, ...$
行動価値関数に関して
行動価値関数については下記にて,説明したため本記事では割愛する.
Q学習に関して
エージェントの学習方法の一種である "Q学習" に関して説明する.
Q学習とは,"off-policy の TD学習" の一種である.Q値の更新式は以下の通りである.
\displaylines{
Q(s_t, a_t) \leftarrow Q(s_t, a_t) + \alpha \left[ r_t + \gamma \max_{a'} Q(s_{t+1}, a') - Q(s_t, a_t) \right] \\
\\
文字の意味は以下の通り.\\
Q ... \text{行動価値関数},\; \alpha ... \text{学習率},\; \gamma ... \text{割引率} \\
s_t ... \text{時刻}t\text{の状態},\; a_t ... \text{時刻}t\text{の行動},\; r_t ... \text{時刻}t\text{の報酬} \\
s_{t+1} ... \text{次の状態},\; \max_{a'} Q(s_{t+1}, a') ... \text{次状態での最大Q値}
}
SARSAとQ学習の違いは,更新式における "次状態の価値の見積もり" にある.SARSAでは実際にε-greedy方策で選択した $a_{t+1}$ の Q値 $Q(s_{t+1}, a_{t+1})$ を使うのに対して,Q学習では次状態での全行動の中で最大の Q値 $\max_{a'} Q(s_{t+1}, a')$ を使う点が異なる.
SARSAとQ学習の比較を下表にまとめた.
| 比較項目 | SARSA | Q学習 |
|---|---|---|
| 更新タイミング | 1ステップごと | 1ステップごと |
| 更新に必要な情報 | $(s_t, a_t, r_t, s_{t+1}, a_{t+1})$ | $(s_t, a_t, r_t, s_{t+1})$ |
| 次状態の価値見積もり | $Q(s_{t+1}, a_{t+1})$(ε-greedy選択) | $\max_{a'} Q(s_{t+1}, a')$(最大値) |
| 方策の種類 | on-policy | off-policy |
| 最適方策への収束 | 探索の影響を受ける | 探索に依らず最適方策に収束 |
Q学習は "off-policy" の学習手法である.off-policyとは,"行動の選択に使う方策(行動方策)と,Q値の更新に使う方策(ターゲット方策)が異なる" ことを意味する.具体的には,行動はε-greedy方策で選択するが,Q値の更新には "greedy方策 (最大Q値を選択する方策)" を使う.そのため,探索時のランダムな行動がQ値の更新に影響しないため,最適方策に近いQ値を学習できる.
Q学習の学習フローを以下に示す.
1:初期状態 $s_0$ を取得する
2:ε-greedy方策で行動 $a_t$ を選択する
3:行動 $a_t$ を実行して,報酬 $r_t$ と次の状態 $s_{t+1}$ を得る
4:$Q(s_t, a_t)$ を更新式で更新する (次行動の選択は不要)
5:$s_t \leftarrow s_{t+1}$ として2に戻る
SARSAと異なる点は,"ループ前に最初の行動を選択する必要がない" 点である.Q学習では,更新式に次行動 $a_{t+1}$ が含まれないため,ループ内で行動を選択してからステップを実行し,次状態の最大Q値でQ値を更新するだけでよい.
Q学習の更新式の意味を分解すると下式の通りとなる.
\displaylines{
Q(s_t, a_t) \leftarrow Q(s_t, a_t) + \alpha \left[ r_t + \gamma \max_{a'} Q(s_{t+1}, a') - Q(s_t, a_t) \right] \\
\\
\underbrace{r_t + \gamma \max_{a'} Q(s_{t+1}, a')}_{\text{TD目標値 (最適行動を仮定した1ステップ先の見積もり)}} - \underbrace{Q(s_t, a_t)}_{\text{現在の推定値}} = \underbrace{\delta_t}_{\text{TD誤差}} \\
\\
Q値の更新 = Q値 + \alpha * TD誤差 \\
TD誤差が正なら Q値を増やす(現在の推定より良い状態だった)\\
TD誤差が負なら Q値を減らす(現在の推定より悪い状態だった)
}
TD目標値として "次状態で取り得る最大のQ値" を使うことで,実際に選択した行動の影響を排除し,常に最適な行動を仮定した上でQ値を更新する.これにより,探索が多いエピソード序盤でも,最適方策に近いQ値を学習し続けることができる.
行動の選択方法 (ε-greedy法)
行動の選択方法 (ε-greedy法) に関しては下記にて,説明したため本記事では割愛する.
ソースコード一覧
本記事で使用する全ソースコードの一覧を下表にまとめる.フォルダ名が記載されているものは,そのフォルダを作成して,フォルダ内にファイルを保存してください.
URDFファイル
| ファイル名 | 概要 |
|---|---|
camera_back_to_front.urdf |
手前方向(-Y方向)を向いているカメラの定義 |
camera_front_to_back.urdf |
奥行き方向(+Y方向)を向いているカメラの定義 |
camera_left_to_right.urdf |
右方向(+X方向)を向いているカメラの定義 |
camera_right_to_left.urdf |
左方向(-X方向)を向いているカメラの定義 |
camera_up_to_down.urdf |
下方向(-Z方向)を向いているカメラの定義 |
grasp_object.urdf |
把持物体の定義 |
robot_2dof.urdf |
グリッパーなし2軸ロボットアームの定義 |
environment_2dof.urdf |
2軸ロボットアームの環境を定義 |
robot_2dof_hand.urdf |
グリッパー付き2軸ロボットアームの定義 |
Pythonファイル
| ファイル名 | 概要 |
|---|---|
constant.py |
定数の定義 (Q学習追加) |
main.py |
全体的なメイン処理 |
pybullet_grasp.py |
PyBulletの把持物体 |
pybullet_gripper.py |
PyBulletのグリッパー |
pybullet_robot.py |
PyBulletのロボット |
pybullet_camera.py |
PyBulletのカメラ |
pybullet_agent_controller.py |
PyBulletのエージェント制御 (Q学習対応) |
BaseAgent.py |
PyBulletのエージェントの抽象クラス |
RandomAgent.py |
PyBulletのランダムに動くエージェント |
MonteCarloAgent.py |
PyBulletのモンテカルロ法で学習するエージェント |
SarsaAgent.py |
PyBulletのSARSAで学習するエージェント |
QLearningAgent.py |
PyBulletのQ学習で学習するエージェント |
pybullet_environment_controller.py |
PyBulletの強化学習用の環境制御 |
BaseEnvironment.py |
PyBulletの強化学習用の環境の抽象クラス |
DiscretizeEnvironment.py |
PyBulletの強化学習用の離散値環境 |
pybullet_main.py |
PyBulletのメイン処理 (Q学習ループ) |
pybullet_main.py の変更点に関して
pybullet_main.py の __learning() メソッドをQ学習用に変更した点について説明する.
SARSAとQ学習の学習ループの違いを下表にまとめた.
| 比較項目 | SARSA (part3) | Q学習 (part4) |
|---|---|---|
| 最初の行動選択 | ループ前に選択 | whileループ内で選択 |
add() の呼び出し |
add(s, a, r, s', a') |
add(s, a, r, s') |
| Q値の更新タイミング | ステップごとの add() 内 |
ステップごとの add() 内 |
update() の役割 |
ε更新のみ | ε更新のみ |
Q学習でループ前の行動選択が不要な理由は,"更新式に次行動 $a_{t+1}$ が含まれない" ためである.SARSAでは次のステップ前に $a_{t+1}$ が必要だったが,Q学習では次状態 $s_{t+1}$ の最大Q値を使うため,次行動を事前に決める必要がない.
Q学習の __learning() の主要部分を抜粋すると以下の通りとなる.
while not done:
# 行動の選択 (ε-greedy方策)
action = self.__agent.get_action(state)
# 1ステップ実行: (s, a) → (s', r, done)
next_state, reward, done = self.__env.step(action)
# Q値を即時更新: Q(s,a) ← Q(s,a) + α[r + γ*max_a'(Q(s',a')) - Q(s,a)]
self.__agent.add(state, action, reward, next_state)
# 状態の更新 (s ← s')
state = next_state
# エピソード終了: 探索確率 ε の更新 (Q値更新はadd()内で完了済み)
self.__agent.update()
QLearningAgent.py の実装に関して
QLearningAgent.py では,SARSA (SarsaAgent.py) と比較して,以下の点が異なる.
add() メソッドの引数変更
SARSAでは add(state, action, reward, next_state, next_action) だったが,Q学習では次行動が不要なため,add(state, action, reward, next_state) に変更した.また,Q値の更新式として,次行動の Q値の代わりに "次状態での最大Q値 $\max_{a'} Q(s_{t+1}, a')$" を使う.
\displaylines{
Q(s_t, a_t) \leftarrow Q(s_t, a_t) + \alpha \left[ \underbrace{r_t + \gamma \max_{a'} Q(s_{t+1}, a')}_{\text{TD目標値}} - Q(s_t, a_t) \right]
}
QLearningAgent.py の add() メソッドの実装を抜粋すると以下の通りとなる.
def add(self, state, action, reward, next_state):
"""
データの追加 + Q値の即時更新 (1ステップごとに呼ぶ)
Q学習の更新式:
Q(s, a) ← Q(s, a) + α * [r + γ * max_a'(Q(s', a')) - Q(s, a)]
パラメータ
state(tuple) : 現在の状態
action(int) : 現在の行動
reward(float) : 報酬
next_state(tuple) : 次の状態
"""
key = (state, action)
# 次状態での全行動のQ値を取得して,最大値を選択
next_Qs = [self.__Q[next_state, a] for a in range(self.__n_action)]
max_next_Q = max(next_Qs)
# TD誤差の計算: r + γ * max_a'(Q(s', a')) - Q(s, a)
td_error = reward + self._GAMMA * max_next_Q - self.__Q[key]
# 行動価値関数の更新
self.__Q[key] = self.__Q[key] + self._ALPHA * td_error
update() メソッドの役割
SARSAと同様に,update() は ε 更新のみである (Q値更新は add() で完了済み).
パラメータファイル
Q学習は qlearning_param.json にQ値を保存する.学習パラメータ (γ, α, ε など) はSARSAと同一の値とした.
| パラメータ | 値 |
|---|---|
| 割引率 γ | 0.9 |
| 学習率 α | 0.1 |
| 探索確率の最小値 ε_min | 0.1 |
| 探索確率の最大値 ε_max | 0.9 |
| 探索確率の減衰率 | 0.9999 |
PyBulletのメイン処理 (pybullet_main.py)
p.connect() 関数の引数である p.DIRECT と p.GUI に関しては下記にて,説明したため本記事では割愛する.
PyBulletでロボットを動かす
上記にて,ソースコードを説明した.
main.py ファイルを実施することによって,PyBullet上のロボットを動かしていく.
経路生成の動画
Q学習に動くエージェントによる経路生成の動画を載せる.
動画1:学習時の経路生成の動画
動画2:学習完了後の経路生成の動画
Q学習はoff-policyの学習手法であるため,SARSAと比較して,より最適な方策に収束することが期待される.探索時のランダムな行動がQ値の更新に影響せず,常に "次状態で最も価値の高い行動" を仮定してQ値を更新するためである.
そのため,次回はQ学習ではなく,より表現力の高いDQN (Deep Q-Network) を採用することで,状態数が爆発的に増加するケースにも対応できるようにする.
おわりに
本記事では,Pythonを使用して,下記内容を実装しました
- エージェントの学習方法としてQ学習を採用して,PyBullet 内の2軸ロボットアームの強化学習
次記事では,下記内容を実装していきます.
参考文献
本記事を作成する上で参考にした内容を記載する.
参考記事
参考書籍
- ゼロから作るDeep Learning ❹ ―強化学習編



