文章目录
- [技术文档:基于 A2C 算法的双足机器人行走控制](#技术文档:基于 A2C 算法的双足机器人行走控制)
-
- [1. 项目概述](#1. 项目概述)
-
- [1.1 项目目标](#1.1 项目目标)
- [1.2 技术栈](#1.2 技术栈)
- [2. 系统架构设计](#2. 系统架构设计)
- [3. 环境设计与状态空间](#3. 环境设计与状态空间)
-
- [3.1 观测空间(Observation Space)](#3.1 观测空间(Observation Space))
- [3.2 动作空间(Action Space)](#3.2 动作空间(Action Space))
- [3.3 奖励函数(Reward Function)](#3.3 奖励函数(Reward Function))
- [4. 核心算法:A2C 与 GAE](#4. 核心算法:A2C 与 GAE)
-
- [4.1 算法原理](#4.1 算法原理)
- [4.2 广义优势估计(GAE)](#4.2 广义优势估计(GAE))
- [4.3 损失函数(Loss Function)](#4.3 损失函数(Loss Function))
- [5. 网络结构](#5. 网络结构)
- [6. 训练流程与超参数配置](#6. 训练流程与超参数配置)
-
- [6.1 训练循环](#6.1 训练循环)
- [6.2 关键超参数](#6.2 关键超参数)
- [7. 代码模块说明](#7. 代码模块说明)
-
- [7.1 目录结构](#7.1 目录结构)
- [7.2 核心类与函数](#7.2 核心类与函数)
- [8. 快速部署与运行指南](#8. 快速部署与运行指南)
-
- [8.1 环境配置](#8.1 环境配置)
- [8.2 训练启动](#8.2 训练启动)
- [8.3 模型恢复与验证](#8.3 模型恢复与验证)
- [9. 实验结果与收敛性分析](#9. 实验结果与收敛性分析)
-
- [9.1 预期收敛曲线](#9.1 预期收敛曲线)
- [9.2 奖励组成分析](#9.2 奖励组成分析)
技术文档:基于 A2C 算法的双足机器人行走控制
项目名称 :Humanoid Walking Control via Advantage Actor-Critic (A2C)
仿真平台 :PyBullet
深度学习框架 :PyTorch
文档版本 :1.0
日期:2026-07-29
1. 项目概述
1.1 项目目标
本项目旨在通过**无模型的深度强化学习(Model-free Deep Reinforcement Learning)**方法,训练一个双足机器人(Humanoid)在物理仿真环境中学会稳定向前行走。智能体(Agent)通过与环境的实时交互,自主探索最优的关节控制策略,无需人类示教或预定义步态。
1.2 技术栈
| 组件 | 技术选型 | 用途 |
|---|---|---|
| 物理引擎 | PyBullet 3.x | 刚体动力学仿真、碰撞检测、URDF 模型加载 |
| 深度学习 | PyTorch 2.x | 策略网络与价值网络的构建、自动微分、GPU 加速训练 |
| 强化学习算法 | A2C (Advantage Actor-Critic) | 在线策略梯度方法,适用于连续动作空间 |
| 依赖管理 | Python 3.8+ / Conda | 环境隔离与包管理 |
2. 系统架构设计
系统采用经典的 Actor-Critic(演员-评论家) 架构,包含三大核心模块:
- 仿真环境模块(Environment):封装 PyBullet,负责重置物理状态、执行动作、返回观测值与奖励。
- 智能体模块(Agent):包含策略网络(Actor)和价值网络(Critic)。
- 训练调度模块(Trainer):负责采样轨迹、计算优势估计(GAE)、执行反向传播并更新网络参数。
#mermaid-svg-NKJzEccF1ESOW6my{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NKJzEccF1ESOW6my .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NKJzEccF1ESOW6my .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NKJzEccF1ESOW6my .error-icon{fill:#552222;}#mermaid-svg-NKJzEccF1ESOW6my .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NKJzEccF1ESOW6my .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NKJzEccF1ESOW6my .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NKJzEccF1ESOW6my .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NKJzEccF1ESOW6my .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NKJzEccF1ESOW6my .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NKJzEccF1ESOW6my .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NKJzEccF1ESOW6my .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NKJzEccF1ESOW6my .marker.cross{stroke:#333333;}#mermaid-svg-NKJzEccF1ESOW6my svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NKJzEccF1ESOW6my p{margin:0;}#mermaid-svg-NKJzEccF1ESOW6my .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NKJzEccF1ESOW6my .cluster-label text{fill:#333;}#mermaid-svg-NKJzEccF1ESOW6my .cluster-label span{color:#333;}#mermaid-svg-NKJzEccF1ESOW6my .cluster-label span p{background-color:transparent;}#mermaid-svg-NKJzEccF1ESOW6my .label text,#mermaid-svg-NKJzEccF1ESOW6my span{fill:#333;color:#333;}#mermaid-svg-NKJzEccF1ESOW6my .node rect,#mermaid-svg-NKJzEccF1ESOW6my .node circle,#mermaid-svg-NKJzEccF1ESOW6my .node ellipse,#mermaid-svg-NKJzEccF1ESOW6my .node polygon,#mermaid-svg-NKJzEccF1ESOW6my .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NKJzEccF1ESOW6my .rough-node .label text,#mermaid-svg-NKJzEccF1ESOW6my .node .label text,#mermaid-svg-NKJzEccF1ESOW6my .image-shape .label,#mermaid-svg-NKJzEccF1ESOW6my .icon-shape .label{text-anchor:middle;}#mermaid-svg-NKJzEccF1ESOW6my .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NKJzEccF1ESOW6my .rough-node .label,#mermaid-svg-NKJzEccF1ESOW6my .node .label,#mermaid-svg-NKJzEccF1ESOW6my .image-shape .label,#mermaid-svg-NKJzEccF1ESOW6my .icon-shape .label{text-align:center;}#mermaid-svg-NKJzEccF1ESOW6my .node.clickable{cursor:pointer;}#mermaid-svg-NKJzEccF1ESOW6my .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NKJzEccF1ESOW6my .arrowheadPath{fill:#333333;}#mermaid-svg-NKJzEccF1ESOW6my .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NKJzEccF1ESOW6my .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NKJzEccF1ESOW6my .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NKJzEccF1ESOW6my .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NKJzEccF1ESOW6my .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NKJzEccF1ESOW6my .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NKJzEccF1ESOW6my .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NKJzEccF1ESOW6my .cluster text{fill:#333;}#mermaid-svg-NKJzEccF1ESOW6my .cluster span{color:#333;}#mermaid-svg-NKJzEccF1ESOW6my div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NKJzEccF1ESOW6my .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NKJzEccF1ESOW6my rect.text{fill:none;stroke-width:0;}#mermaid-svg-NKJzEccF1ESOW6my .icon-shape,#mermaid-svg-NKJzEccF1ESOW6my .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NKJzEccF1ESOW6my .icon-shape p,#mermaid-svg-NKJzEccF1ESOW6my .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NKJzEccF1ESOW6my .icon-shape .label rect,#mermaid-svg-NKJzEccF1ESOW6my .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NKJzEccF1ESOW6my .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NKJzEccF1ESOW6my .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NKJzEccF1ESOW6my :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 观测状态 s_t
采样动作 a_t
奖励 r_t, 终止状态 done
计算优势 & 目标值
更新价值参数
更新策略参数
环境 Environment
策略网络 Actor
训练器 Trainer
价值网络 Critic
3. 环境设计与状态空间
3.1 观测空间(Observation Space)
算法动态检测环境中的可动关节数量(本项目为 4 个),构建观测向量。具体包含:
| 特征组 | 具体内容 | 维度 |
|---|---|---|
| 身体信息 | X, Y, Z 坐标;Roll, Pitch, Yaw 欧拉角;线速度 (Vx, Vy, Vz);角速度 (Wx, Wy, Wz) | 12 |
| 关节信息 | 每个可动关节的角度 (Position) 与角速度 (Velocity) | n_joints * 2 = 8 |
| 总维度 | - | 20 |
3.2 动作空间(Action Space)
- 类型:连续(Continuous)。
- 维度 :
n_joints = 4。 - 数值范围 :每个动作值被限制在
[-1, 1]区间,对应关节目标角度的幅度(单位:弧度)。 - 控制模式 :位置控制(
POSITION_CONTROL),通过setJointMotorControl2设置目标角度。
3.3 奖励函数(Reward Function)
奖励函数是引导智能体行为的核心,设计如下:
R = R f o r w a r d + R e n e r g y + R a l i v e + R f a l l R = R_{forward} + R_{energy} + R_{alive} + R_{fall} R=Rforward+Renergy+Ralive+Rfall
- 前进奖励 ( R f o r w a r d R_{forward} Rforward):
1.0 * delta_x。鼓励机器人沿 X 轴正向移动。 - 能量惩罚 ( R e n e r g y R_{energy} Renergy):
-0.01 * mean(action^2)。抑制关节高频抖动,鼓励平滑运动。 - 存活奖励 ( R a l i v e R_{alive} Ralive):
+0.1。每步给予微小正奖励,防止智能体因害怕惩罚而主动摔倒不动。 - 摔倒惩罚 ( R f a l l R_{fall} Rfall): 当身体高度低于 0.3m 或滚转角/俯仰角超过 57°(1.0 rad)时,触发
-10.0并终止当前回合(Episode)。
4. 核心算法:A2C 与 GAE
4.1 算法原理
本项目采用 优势演员-评论家(A2C) 算法。与传统 REINFORCE 算法相比,A2C 通过引入优势函数(Advantage Function) 降低了梯度估计的方差。
策略梯度(Policy Gradient) 更新公式如下:
∇ J ( θ ) ≈ E ∇ θ log π θ ( a t ∣ s t ) ⋅ A ( s t , a t ) \nabla J(\theta) \approx \mathbb{E} \left \\nabla_\\theta \\log \\pi_\\theta(a_t\|s_t) \\cdot A(s_t, a_t) \\right ∇J(θ)≈E∇θlogπθ(at∣st)⋅A(st,at)
其中 A ( s t , a t ) A(s_t, a_t) A(st,at) 即为优势函数,衡量动作值 Q ( s t , a t ) Q(s_t, a_t) Q(st,at) 相对于基准状态值 V ( s t ) V(s_t) V(st) 的优势。
4.2 广义优势估计(GAE)
为了权衡偏差与方差,我们使用 GAE(Generalized Advantage Estimation) 计算优势函数:
A t G A E ( γ , λ ) = ∑ l = 0 ∞ ( γ λ ) l δ t + l A^{GAE(\gamma, \lambda)}t = \sum{l=0}^{\infty} (\gamma \lambda)^l \delta_{t+l} AtGAE(γ,λ)=l=0∑∞(γλ)lδt+l
其中:
- γ \gamma γ(折扣因子)= 0.99,控制对未来奖励的重视程度。
- λ \lambda λ(GAE 系数)= 0.95,控制优势估计的方差与偏差平衡。
- δ t = r t + γ V ( s t + 1 ) − V ( s t ) \delta_t = r_t + \gamma V(s_{t+1}) - V(s_t) δt=rt+γV(st+1)−V(st),即时序差分误差(TD Error)。
4.3 损失函数(Loss Function)
网络总损失由三部分组成:
-
策略损失(Policy Loss) :
L p o l i c y = − E log π θ ( a t ∣ s t ) ⋅ A t L_{policy} = -\mathbb{E} \left \\log \\pi_\\theta(a_t\|s_t) \\cdot A_t \\right Lpolicy=−Elogπθ(at∣st)⋅At
配合熵正则化(Entropy Bonus) ,鼓励探索: L e n t r o p y = − β ⋅ H ( π θ ) L_{entropy} = -\beta \cdot \mathcal{H}(\pi_\theta) Lentropy=−β⋅H(πθ),其中 β = 0.01 \beta=0.01 β=0.01。
-
价值损失(Value Loss) :
使用均方误差(MSE): L v a l u e = 1 2 E ( V ϕ ( s t ) − R t ) 2 L_{value} = \frac{1}{2} \mathbb{E} \left (V_\\phi(s_t) - R_t)\^2 \\right Lvalue=21E(Vϕ(st)−Rt)2,其中 R t R_t Rt 是实际折扣回报。
5. 网络结构
考虑到状态空间较小(20维)且是连续控制任务,网络采用全连接多层感知机(MLP),而非 Transformer。
| 网络模块 | 层次 | 输入维度 | 输出维度 | 激活函数 |
|---|---|---|---|---|
| 共享特征层 | Linear + BN | 20 | 256 | ReLU |
| Linear + BN | 256 | 256 | ReLU | |
| Actor 头 | Linear | 256 | 4 (均值) | Tanh (隐式) |
| Log_Std | - | 4 (可学习) | - | |
| Critic 头 | Linear | 256 | 1 | 无 (线性输出) |
- 动作采样 :Actor 输出动作均值 μ \mu μ 和对数标准差 log σ \log\sigma logσ,构建对角高斯分布 N ( μ , σ ) \mathcal{N}(\mu, \sigma) N(μ,σ) 进行采样。
6. 训练流程与超参数配置
6.1 训练循环
- 采样 :智能体在当前策略下运行 N = 256 N=256 N=256 个仿真步(
update_interval),收集轨迹片段。 - 计算 GAE :利用 Critic 网络估计的最后状态价值 V ( s t + 1 ) V(s_{t+1}) V(st+1),反向计算每一步的优势值和回报。
- 标准化:对优势值进行 Z-score 标准化(减去均值,除以标准差),使训练更稳定。
- 梯度更新:对收集的批次数据计算上述三种损失,合并后进行反向传播。
- 重置:若机器人摔倒或超出步数限制,重置仿真环境并开始新 Episode。
6.2 关键超参数
| 参数名称 | 符号 | 数值 | 说明 |
|---|---|---|---|
| 最大训练步数 | max_steps |
1,000,000 | 物理仿真总步数 |
| 折扣因子 | γ \gamma γ | 0.99 | 未来奖励衰减率 |
| GAE 系数 | λ \lambda λ | 0.95 | 权衡优势估计的偏差/方差 |
| 学习率 | lr |
3e-4 | Adam 优化器学习率 |
| 更新批量大小 | update_interval |
256 | 每收集 256 步更新一次 |
| 最大单回合步数 | max_episode_steps |
500 | 防止无限循环 |
| 熵系数 | β \beta β | 0.01 | 控制探索程度 |
| 梯度裁剪 | clip_grad_norm |
0.5 | 防止梯度爆炸 |
7. 代码模块说明
7.1 目录结构
src/
├── train_humanoid_mlp.py # 主训练脚本(包含环境、网络、训练循环)
└── models/ # 自动保存的训练检查点
├── humanoid_checkpoint_50000.pth
└── humanoid_model.pth # 最终模型
7.2 核心类与函数
Config(dataclass):统一管理所有超参数,支持动态修改。PolicyNetwork(nn.Module):定义共享特征层、Actor 头和 Critic 头。HumanoidEnv:reset():重置关节角度和位置,返回初始观测。_get_obs():动态拼接身体与关节状态,自适应维度。step(action):执行动作,结算奖励,返回下一状态和终止标志。
train():主循环,实现轨迹收集、GAE 计算、模型优化与日志输出。
8. 快速部署与运行指南
8.1 环境配置
bash
# 创建虚拟环境(可选)
conda create -n rl_walk python=3.10
conda activate rl_walk
# 安装依赖
pip install torch numpy pybullet tqdm
8.2 训练启动
bash
python train_humanoid_mlp.py
- 默认以
render=False启动(无图形界面,训练更快)。 - 若要实时观察训练过程,请将脚本中
env = HumanoidEnv(render=False)改为render=True。
8.3 模型恢复与验证
如果训练中断,只需注释掉 train() 函数中的模型初始化,加载保存的 checkpoint 即可继续(需在代码中补充加载逻辑)。
9. 实验结果与收敛性分析
9.1 预期收敛曲线
- 初期(0 ~ 50k 步):平均 Episode 奖励为负(约 -50 ~ -20),机器人频繁摔倒,缺乏协调性。
- 中期(50k ~ 300k 步):奖励快速上升,机器人学会保持髋部平衡,能够踉跄前行。
- 后期(300k ~ 1M 步):奖励趋于收敛,机器人展现稳定、周期性的行走步态,平均速度接近 0.5 m/s。
9.2 奖励组成分析
- 前进奖励 贡献逐步上升,成为奖励的主要来源。
- 能量惩罚 有效抑制了高频抖动,使步态更加自然。
- 熵正则化 确保了策略的多样性,防止过早陷入局部最优。