【SONIC源码阅读系列4】完整RL闭环:PPO和梯度流
阶段三:Token → Policy → Action → PPO
这一阶段我们把上一阶段得到的 Universal Token 接到完整 RL 闭环里。
上一阶段:
motion reference → E → z → F S Q → token \text{motion reference} \rightarrow E \rightarrow z \rightarrow \mathrm{FSQ} \rightarrow \text{token} motion reference→E→z→FSQ→token
现在继续往后:
token + s t → D dyn → μ t → π ( a t ∣ s t ) → a t → Isaac Lab → r t , s t + 1 → PPO \text{token}+s_t \rightarrow D_{\text{dyn}} \rightarrow \mu_t \rightarrow \pi(a_t|s_t) \rightarrow a_t \rightarrow \text{Isaac Lab} \rightarrow r_t,s_{t+1} \rightarrow \text{PPO} token+st→Ddyn→μt→π(at∣st)→at→Isaac Lab→rt,st+1→PPO
这里最重要的一点是:UniversalTokenModule 是 Actor 内部的 backbone,Actor 再在它外面包了一层 Gaussian policy。
也就是说,从软件结构看:
Actor
├── UniversalTokenModule
│ ├── Encoder
│ ├── FSQ
│ └── G1 Dynamic Decoder
│
└── Gaussian action distribution
这一点在 actor_critic_modules.py 中非常明确:Actor.__init__() 通过 custom_instantiate() 创建 self.actor_module,而 SONIC 配置把这个 actor_module 指向 UniversalTokenModule。Actor 自己负责 action distribution。(GitHub)
1. 先看清楚整个 Actor
如果把我们前面分析的内容压缩成一个数学函数,SONIC Actor 可以写成:
μ t = D d y n ( Q ( E ( x t ) ) , s t ) \mu_t = D_{\mathrm{dyn}}\left( Q(E(x_t)), s_t \right) μt=Ddyn(Q(E(xt)),st)
其中:
- x t x_t xt:motion/tokenizer observation,例如未来 10 帧运动参考;
- E E E:G1 / SMPL / Teleop encoder;
- Q Q Q:FSQ;
- z t = Q ( E ( x t ) ) z_t=Q(E(x_t)) zt=Q(E(xt)):64 维 flattened latent token;
-
s
t
s_t
st:robot proprioception,也就是
actor_obs; - D d y n D_{\mathrm{dyn}} Ddyn:G1 dynamic decoder;
- μ t \mu_t μt:动作分布的 mean。
然后 Actor 在外面定义:
a t ∼ N ( μ t , σ 2 ) a_t\sim\mathcal N(\mu_t,\sigma^2) at∼N(μt,σ2)
所以完整的 policy 是:
π θ ( a t ∣ o t ) = N ( D θ ( Q ( E θ ( x t ) ) , s t ) , σ θ 2 ) \boxed{ \pi_\theta(a_t|o_t)= \mathcal N \left( D_\theta(Q(E_\theta(x_t)),s_t), \sigma_\theta^2 \right) } πθ(at∣ot)=N(Dθ(Q(Eθ(xt)),st),σθ2)
这里已经出现了 SONIC 非常关键的一层结构:
motion latent 决定“想怎么动”,proprioception 决定“当前机器人状态下应该怎么执行”。
这也是为什么上一阶段强调 g1_dyn 的输入是 token_flattened + proprioception。它使 decoder 具备状态反馈性质。
2. Actor.forward() 到底做了什么?
源码中 Actor.forward() 的核心流程非常干净。
它首先拿到 obs_dict。SONIC 当前配置设置了 input_obs_dict: true,因此整个 observation dictionary 会传给 UniversalTokenModule。源码里对应的是:
net_input = obs_dict
然后执行:
output = self.actor_module(net_input, ...)
如果训练阶段开启 auxiliary loss,Actor 要求 backbone 同时计算这些 loss;最后从 output 中取出 output["action_mean"]。(GitHub)
所以可以把这里理解成:
o t ⟶ Actor { μ t L a u x o_t \overset{\text{Actor}} {\longrightarrow} \begin{cases} \mu_t\\ L_{\mathrm{aux}} \end{cases} ot⟶Actor{μtLaux
这里的 action_mean 已经来自上一阶段我们分析的 G1 dynamic decoder。
3. 为什么还需要 Gaussian Distribution?
拿到 action_mean 后,Actor 会构造:Normal(mean, std)
源码中 update_distribution() 明确创建了 PyTorch 的 Normal distribution;std 可以采用直接参数化或 log_std 参数化,并且支持 clamp。(GitHub)
因此:
μ t = f θ ( o t ) \mu_t = f_\theta(o_t) μt=fθ(ot)
之后:
a t = μ t + σ θ ϵ , ϵ ∼ N ( 0 , I ) a_t=\mu_t+\sigma_\theta\epsilon,\qquad \epsilon\sim\mathcal N(0,I) at=μt+σθϵ,ϵ∼N(0,I)
这个 stochastic action 是 PPO 探索所需要的。
源码里的 act() 会先更新 distribution,然后执行 self.distribution.sample();同时返回 action_mean 和 action_sigma。(GitHub)
所以训练时实际上保存的是三样东西:
- sampled action a t a_t at
- mean μ t \mu_t μt
- standard deviation σ t \sigma_t σt
以及最关键的:
log π θ ( a t ∣ o t ) \log\pi_\theta(a_t|o_t) logπθ(at∣ot)
4. 一个很容易忽略的地方:Rollout 时到底取哪一个 timestep?
这个 repo 当前 Actor 支持 temporal observation buffer。
Actor.rollout() 会先把当前 observation 放进 obs_dict_buffer,然后把整个 history 输入 backbone,最后设置 last_step_only=True。也就是说,如果当前 buffer 是:
[ o t − k + 1 , … , o t − 1 , o t ] [o_{t-k+1},\ldots,o_{t-1},o_t] [ot−k+1,…,ot−1,ot]
那么 backbone 可以处理整个序列,但最终只使用 (t) 时刻的 action mean。源码在 rollout() 中明确这么做。(GitHub)
对于当前 SONIC release,max_rollout_history 的实际配置需要结合具体 experiment config 判断;默认 Actor 类本身的参数是 1。这里不要把 motion reference 的 future horizon 和 policy 的 temporal history 混为一谈。
这两个时间方向完全不同:
| 时间结构 | 含义 |
|---|---|
num_future_frames=10 | 当前时刻向未来看 10 帧 motion reference |
max_rollout_history | policy 向过去看多少个 observation |
FSQ token | 将未来运动参考压缩成 latent |
proprioception | 当前机器人状态 |
这其实构成了一个很有意思的时序结构:
x t : t + 9 ⏟ future reference → z t \underbrace{x_{t:t+9}}_{\text{future reference}} \rightarrow z_t future reference xt:t+9→zt
同时:
s t − k : t ⏟ robot history → policy \underbrace{s_{t-k:t}}_{\text{robot history}} \rightarrow \text{policy} robot history st−k:t→policy
所以 SONIC 的 controller 同时利用了 future intent 和 current physical state。
5. Action 到底是什么?
这里需要特别区分三个概念:
motion token ≠ policy action ≠ motor torque \boxed{ \text{motion token} \neq \text{policy action} \neq \text{motor torque} } motion token=policy action=motor torque
对于当前 SONIC training path:
Motion token
大致是:
z t ∈ R 64 z_t\in\mathbb R^{64} zt∈R64
对应两个 32-D FSQ token。
Policy action
经过 g1_dyn decoder 后得到 G1 body action。
当前 G1 body controller 使用 29 个 body joints。repo 的 action/joint ordering 定义了完整的 G1 29-DOF 顺序,包括腿、腰、肩、肘和腕。(GitHub)
Simulator / robot command
这个 action 随后进入 Isaac Lab 的 action manager。官方 training reference 将这里描述为 Joint Position Action Space。(GitHub)
因此可以把这一层写成:
z t , s t → D d y n → a t 29 → q t t a r g e t → robot dynamics z_t,s_t \rightarrow D_{\mathrm{dyn}} \rightarrow a_t^{29} \rightarrow q_t^{target} \rightarrow \text{robot dynamics} zt,st→Ddyn→at29→qttarget→robot dynamics
具体的低层 PD / actuator dynamics 再由 Isaac Lab 和机器人配置处理。
7. 环境 step:Action 从网络真正进入机器人
训练循环中的核心 interaction 位于 TRLPPOTrainer._rollout_step()。
源码结构非常清晰:
- policy 根据 observation 生成 action;
- 将 observation、action、log probability 等保存进
RolloutStorage; - 调用
self.env.step(policy_state_dict); - 环境返回新的 observation、reward、done 和 info;
- 保存这些 transition;
- 重复
num_steps_per_env次。(GitHub)
其中 policy step:
policy_model.rollout(...)
得到 action 后,又调用:
policy_model.get_actions_log_prob(actions)
计算:
log π θ o l d ( a t ∣ o t ) \log\pi_{\theta_{\mathrm{old}}}(a_t|o_t) logπθold(at∣ot)
然后一起放进 rollout storage。(GitHub)
所以一个 transition 实际上包含:
( o t , a t , r t , d t , log π θ o l d ( a t ∣ o t ) , V t , … ) (o_t,a_t,r_t,d_t,\log\pi_{\theta_{\rm old}}(a_t|o_t),V_t,\ldots) (ot,at,rt,dt,logπθold(at∣ot),Vt,…)
这正是后面 PPO update 所需要的数据。
8. 这里有一个非常重要的时间尺度
当前 PPO 配置:
num_steps_per_env = 32num_learning_epochs = 5num_mini_batches = 4gamma = 0.99lambda = 0.95clip_param = 0.2actor learning rate =2 × 10 − 5 2\times10^{-5} 2×10−5critic learning rate =10 − 3 10^{-3} 10−3initial noise std = 0.05。(GitHub)
假设 SONIC policy 的控制频率是 50 Hz,那么:
32 × 20 ms = 640 ms 32\times20\text{ms}=640\text{ms} 32×20ms=640ms
也就是说,每个 environment 在一次 PPO iteration 中先收集约 0.64 秒的 trajectory,然后利用这批数据做多轮更新。同时,官方训练 reference 给出的环境结构是 IsaacLab vectorized environments,并且默认训练规模是 4096 个并行环境。(GitHub)
因此单次 rollout 大致是: 4096 × 32 = 131072 4096\times32=131072 4096×32=131072 个 environment transitions。
这解释了为什么 SONIC 可以用 PPO 做大规模运动 tracking:单次 iteration 本身就能产生大量 on-policy samples。
9. Critic 在这里干什么?
Actor 得到 action。
Critic 则负责估计:
V ϕ ( s t ) = E [ ∑ k = 0 ∞ γ k r t + k ∣ s t ] V_\phi(s_t)= \mathbb E \left[ \sum_{k=0}^{\infty}\gamma^k r_{t+k} \mid s_t \right] Vϕ(st)=E[k=0∑∞γkrt+k∣st]
代码里的 Critic 是独立 backbone,并输出 value estimate。源码中 Critic.evaluate() 接收 critic_obs,经过 critic backbone 得到 value。(GitHub)
这里 SONIC 使用了一个很重要的 asymmetric actor-critic 思路:
Actor
使用 policy observation。
Critic
使用更丰富的 privileged observation。
训练代码文档明确区分了:
policycritictokenizer
三个 observation group;critic observation 包含更多 privileged physical information,例如 base velocity、body position、body orientation 等。(GitHub)
因此可以写成:
π θ ( a t ∣ o t p o l i c y , x t m o t i o n ) \pi_\theta(a_t|o_t^{policy},x_t^{motion}) πθ(at∣otpolicy,xtmotion)
而:
V ϕ ( o t c r i t i c , x t m o t i o n ) V_\phi(o_t^{critic},x_t^{motion}) Vϕ(otcritic,xtmotion)
critic 可以看到更完整的 simulation state,从而提供更稳定的 advantage estimate。
部署时只需要 actor。
10. Rollout 完以后,怎么得到 Advantage?
_rollout_step() 收集完 32 个 timestep 后,会把整条 trajectory 的 critic observations 拼起来,并额外加入最后一个 observation。
然后 critic 一次性计算:
V 0 , V 1 , … , V T V_0,V_1,\ldots,V_{T} V0,V1,…,VT
源码中这一过程可以直接看到:收集完 trajectory 后,构造 all_obs_dict,调用 _chunked_value_evaluate(),得到整个 trajectory 的 value predictions。(GitHub)
随后处理 timeout:
r t ← r t + γ ⋅ timeout t ⋅ V ( s t + 1 ) r_t \leftarrow r_t+\gamma \cdot \text{timeout}_t\cdot V(s_{t+1}) rt←rt+γ⋅timeoutt⋅V(st+1)
源码明确对 timeout transition 做了这种 bootstrap。(GitHub)
之后进入:
self._compute_returns(...)
计算 returns 和 advantages。(GitHub)
从算法层面就是标准 GAE:
δ t = r t + γ V ( s t + 1 ) − V ( s t ) A t = δ t + γ λ δ t + 1 + γ 2 λ 2 δ t + 2 + ⋯ \delta_t= r_t+\gamma V(s_{t+1})-V(s_t)\\ A_t= \delta_t+\gamma\lambda\delta_{t+1} +\gamma^2\lambda^2\delta_{t+2}+\cdots δt=rt+γV(st+1)−V(st)At=δt+γλδt+1+γ2λ2δt+2+⋯
当前配置使用: γ = 0.99 , λ = 0.95 \gamma=0.99,\lambda=0.95 γ=0.99,λ=0.95 (GitHub).
11. PPO 更新在优化什么?
这里需要把 PPO 和 SONIC auxiliary loss 分开理解。
标准 PPO 部分可以写成:
r t ( θ ) = π θ ( a t ∣ o t ) π θ o l d ( a t ∣ o t ) r_t(\theta)= \frac{ \pi_\theta(a_t|o_t) }{ \pi_{\theta_{\mathrm{old}}}(a_t|o_t) } rt(θ)=πθold(at∣ot)πθ(at∣ot)
然后:
L P P O = − E t [ min ( r t A t , clip ( r t , 1 − ϵ , 1 + ϵ ) A t ) ] L_{\mathrm{PPO}}= -\mathbb E_t \left[ \min \left( r_t A_t, \operatorname{clip}(r_t,1-\epsilon,1+\epsilon)A_t \right) \right] LPPO=−Et[min(rtAt,clip(rt,1−ϵ,1+ϵ)At)]
当前: ϵ = 0.2 \epsilon=0.2 ϵ=0.2。此外还有 critic value loss 和 entropy regularization。配置中 value_loss_coef = 1.0 , entropy_coef = 0.01 \text{value\_loss\_coef}=1.0,\text{entropy\_coef}=0.01 value_loss_coef=1.0,entropy_coef=0.01 (GitHub)。因此传统 PPO 部分可以概括成:
L R L = L p o l i c y + L v a l u e − β H ( π ) L_{\mathrm{RL}}= L_{\mathrm{policy}} + L_{\mathrm{value}}- \beta H(\pi) LRL=Lpolicy+Lvalue−βH(π)
12. PPO Loss 之外的Auxiliary Loss
现在来到整个阶段三最重要的一点。
TRLAuxLossPPOTrainer 继承 TRLPPOTrainer。
它在父类 PPO loss 的基础上,把 UniversalTokenModule 返回的 auxiliary losses 加进去。
源码明确写的是:
L = L P P O + aux_loss_scale ∑ i c i L i L= L_{\mathrm{PPO}} + \text{aux\_loss\_scale} \sum_i c_iL_i L=LPPO+aux_loss_scalei∑ciLi
(GitHub)
在 _compute_loss() 中,先执行:
super()._compute_loss(...)
得到标准 PPO loss,然后再执行 _compute_aux_loss(),最后:
loss_dict["loss"] += aux_loss_result["total_aux_loss"]
(GitHub)
所以现在整个 SONIC policy 的训练目标可以写成:
L S O N I C = L P P O + λ a u x ( ∑ i c i L i a u x ) \boxed{ L_{\mathrm{SONIC}}= L_{\mathrm{PPO}} + \lambda_{\mathrm{aux}} \left( \sum_i c_iL_i^{aux} \right) } LSONIC=LPPO+λaux(i∑ciLiaux)
这就是我们上一阶段看到的 latent alignment、reconstruction、cycle consistency 真正进入 RL training 的位置。
13. Auxiliary Loss 的梯度到底流到哪里?
这点非常关键。训练时 Actor 的 forward() 会设置:compute_aux_loss=True
然后 UniversalTokenModule 输出:
{ action_mean , aux_losses , aux_loss_coef } \{\text{action\_mean},\text{aux\_losses},\text{aux\_loss\_coef}\} {action_mean,aux_losses,aux_loss_coef}
Actor 把这些 loss 保存下来,再由 TRLAuxLossPPOTrainer 加到总 loss 中。(GitHub)
于是计算图实际上长这样:
Motion / Tokenizer Obs
│
▼
G1 / SMPL / Teleop Encoder
│
├──────────────┐
│ │
▼ ▼
pre-FSQ latent Auxiliary Losses
│
▼
FSQ
│
▼
Token
│
▼
G1 Dynamic Decoder
│
▼
action_mean
│
▼
PPO objective
因此 Encoder 和 Decoder 同时收到两类梯度:
∇ θ L P P O \nabla_\theta L_{\mathrm{PPO}} ∇θLPPO
以及:
∇ θ L a u x \nabla_\theta L_{\mathrm{aux}} ∇θLaux
最终:
∇ θ L S O N I C = ∇ θ L P P O + λ a u x ∇ θ L a u x \nabla_\theta L_{\mathrm{SONIC}}= \nabla_\theta L_{\mathrm{PPO}} + \lambda_{\mathrm{aux}} \nabla_\theta L_{\mathrm{aux}} ∇θLSONIC=∇θLPPO+λaux∇θLaux
这就是 SONIC 与普通 imitation RL controller 的一个重要区别:latent representation 本身也受到显式监督,同时参与最终控制策略学习。
14. 优化关系
考虑 G1 latent:
z g 1 = E g 1 ( x g 1 ) z_{g1}=E_{g1}(x_{g1}) zg1=Eg1(xg1)
SMPL latent:
z s m p l = E s m p l ( x s m p l ) z_{smpl}=E_{smpl}(x_{smpl}) zsmpl=Esmpl(xsmpl)
上一阶段我们看到:
L a l i g n = ∥ z g 1 − z s m p l ∥ 2 L_{\mathrm{align}}= \|z_{g1}-z_{smpl}\|^2 Lalign=∥zg1−zsmpl∥2
与此同时,RL 又希望:
D ( z , s t ) D(z,s_t) D(z,st)
产生高 reward 的 action。
所以 encoder 实际受到三个方向的约束:
第一层:运动重建
让 latent 保留 motion information。
L r e c o n L_{\mathrm{recon}} Lrecon
第二层:跨模态 alignment
让不同 motion source 映射到相近 latent。
L a l i g n L_{\mathrm{align}} Lalign
第三层:control utility
让 decoder 使用 latent 能产生高 reward action。
L P P O L_{\mathrm{PPO}} LPPO
于是 latent 的学习目标可以概念化为:
latent quality = information preservation + cross-modal consistency + control utility \boxed{ \text{latent quality}= \text{information preservation} + \text{cross-modal consistency} + \text{control utility} } latent quality=information preservation+cross-modal consistency+control utility
这是理解 SONIC 最关键的地方之一。它的 latent 并非只由 reconstruction objective 定义,而是在 representation learning + multimodal alignment + reinforcement learning 三个方向共同塑造。
15. 4096 并行环境
训练代码采用 vectorized IsaacLab environment。
所以实际上不是:
robot 1
↓
policy
↓
step
而是:
env 1 ─┐
env 2 ─┤
env 3 ─┤
... ├──→ Actor ──→ actions
env4096┘
│
▼
Isaac Lab
│
┌─────┴─────┐
▼ ▼
rewards next obs
因此 policy 输入的 batch 是:
B = 4096 B=4096 B=4096
每次 policy inference 同时处理 4096 个 humanoid simulation。
一次 rollout:
B × T = 4096 × 32 = 131072 B\times T= 4096\times32= 131072 B×T=4096×32=131072
然后 PPO epoch 对这些 samples 做 mini-batch update。配置里:
N e p o c h = 5 , N m i n i = 4 N_{\mathrm{epoch}}=5,\qquad N_{\mathrm{mini}}=4 Nepoch=5,Nmini=4
所以每轮 rollout 数据会被重复利用多次。(GitHub)
16. 一次 PPO iteration 的完整链路
现在可以把整个过程串起来:
┌───────────────────────────────────────────────┐
│ IsaacLab 4096 environments │
└──────────────────────┬────────────────────────┘
│
│ obs_dict
▼
┌──────────────┐
│ Actor │
└──────┬───────┘
│
▼
UniversalTokenModule
│
┌────────────┼────────────┐
▼ ▼ ▼
G1 Encoder SMPL Encoder Teleop Encoder
│
▼
pre-FSQ latent
│
▼
FSQ
│
▼
2 × 32 token
│
▼
token_flattened
│
├───────────────┐
│ │
▼ ▼
proprioception Dynamic Decoder
│
▼
μ(action)
│
▼
Gaussian π(a|o)
│
▼
sample a
│
▼
┌────────────────┐
│ IsaacLab.step │
└───────┬────────┘
│
┌────────┴────────┐
▼ ▼
reward next obs
│
▼
RolloutStorage
│
▼
Critic evaluation
│
▼
V(s_t)
│
▼
GAE
│
▼
advantages
│
▼
PPO mini-batch update
│
├───────────────┐
│ │
▼ ▼
PPO objective Auxiliary losses
│ │
└───────┬───────┘
▼
total loss
│
▼
gradient
│
▼
optimizer
这基本就是 SONIC training loop 的骨架。官方 training reference 也把整个训练系统明确拆成 environment、Actor/Critic、UniversalTokenModule、PPO trainer 和 auxiliary loss trainer 几个组件。(GitHub)
17. 注意:Rollout 时和 Update 时的行为不同
Rollout
代码使用:
torch.no_grad()
因此 rollout 主要负责收集:
( o t , a t , r t , log π θ o l d , V t ) (o_t,a_t,r_t,\log\pi_{\theta_{\rm old}},V_t) (ot,at,rt,logπθold,Vt)
不保留训练计算图。源码 _rollout_step() 明确把整个 environment interaction 放在 torch.no_grad() 下。(GitHub)
PPO Update
之后重新把 rollout data 喂给 Actor。
此时重新执行:
o t → E → F S Q → D → π θ o_t \rightarrow E \rightarrow FSQ \rightarrow D \rightarrow \pi_\theta ot→E→FSQ→D→πθ
并计算新的:
log π θ ( a t ∣ o t ) \log\pi_\theta(a_t|o_t) logπθ(at∣ot)
以及 auxiliary losses。
所以 rollout 中的 action mean / log probability 是 old policy 数据;update 阶段重新 forward 得到的是 current policy。
这正是 PPO ratio:
r t ( θ ) = exp ( log π θ ( a t ∣ o t ) − log π θ o l d ( a t ∣ o t ) ) r_t(\theta)= \exp \left( \log\pi_\theta(a_t|o_t)- \log\pi_{\theta_{\rm old}}(a_t|o_t) \right) rt(θ)=exp(logπθ(at∣ot)−logπθold(at∣ot))
能够成立的原因。
18. 抽象总结SONIC Actor
从代码结构看,答案非常明确:
SONIC Actor = UniversalTokenModule + Gaussian Policy \boxed{ \text{SONIC Actor}= \text{UniversalTokenModule} + \text{Gaussian Policy} } SONIC Actor=UniversalTokenModule+Gaussian Policy
而 UniversalTokenModule 自己又包含:
UniversalTokenModule = E multi-modal + Q FSQ + D motion/control \boxed{ \text{UniversalTokenModule}= E_{\text{multi-modal}} + Q_{\text{FSQ}} + D_{\text{motion/control}} } UniversalTokenModule=Emulti-modal+QFSQ+Dmotion/control
所以 SONIC 的完整 controller 可以抽象成:
π ( a t ∣ o t , x t ) = N ( D ( Q ( E ( x t ) ) , s t ) , σ 2 ) \boxed{ \pi(a_t|o_t,x_t)= \mathcal N \left( D( Q(E(x_t)), s_t ), \sigma^2 \right) } π(at∣ot,xt)=N(D(Q(E(xt)),st),σ2)
19. 4 个设计点
① Token 和 action 的职责被严格分开
x motion → z token → a joint x_{\text{motion}} \rightarrow z_{\text{token}} \rightarrow a_{\text{joint}} xmotion→ztoken→ajoint
这使 VLA、SMPL、teleoperation 等不同上游输入都可以共享同一个控制接口。
② Proprioception 被放在 decoder 侧
a t = D ( z t , s t ) a_t=D(z_t,s_t) at=D(zt,st)
这使 latent 更偏向描述 motion intent / reference,而具体动作由当前机器人状态决定。
这也是后面 VLA 接入 SONIC 时非常重要的接口基础。
③ Auxiliary loss 与 RL loss 共用同一套网络参数
L = L P P O + λ a u x L a u x L= L_{\mathrm{PPO}} + \lambda_{\mathrm{aux}}L_{\mathrm{aux}} L=LPPO+λauxLaux
因此 latent representation 同时受到 motion reconstruction、跨模态 alignment 和 control reward 的约束。
④ Critic 使用 privileged observation
Actor 学习:
π ( a ∣ o p o l i c y ) \pi(a|o_{\mathrm{policy}}) π(a∣opolicy)
Critic 学习:
V ( o p r i v i l e g e d ) V(o_{\mathrm{privileged}}) V(oprivileged)
这降低了 value estimation 难度,同时保持最终部署 policy 的 observation 要求相对有限。官方文档也明确将 policy / critic observation groups 分开设计。(GitHub)
20. 核心训练闭环
现在从最开始的 motion 到 PPO 更新,已经可以完整写成:
x t : t + 9 → E G1/SMPL/Teleop → z p r e → F S Q → z t ∈ R 64 → [ z t , s t ] → D G 1 → μ t → π θ ( a t ∣ o t ) → a t → IsaacLab → r t , s t + 1 → V ϕ → A t → L P P O + L r e c o n + L a l i g n + L c y c l e → ∇ θ L \boxed{ \begin{aligned} x_{t:t+9} &\rightarrow E_{\text{G1/SMPL/Teleop}} \\ &\rightarrow z^{pre} \\ &\rightarrow FSQ \\ &\rightarrow z_t\in\mathbb R^{64} \\ &\rightarrow [z_t,s_t] \\ &\rightarrow D_{\mathrm{G1}} \\ &\rightarrow \mu_t \\ &\rightarrow \pi_\theta(a_t|o_t) \\ &\rightarrow a_t \\ &\rightarrow \text{IsaacLab} \\ &\rightarrow r_t,s_{t+1} \\ &\rightarrow V_\phi \\ &\rightarrow A_t \\ &\rightarrow L_{\mathrm{PPO}} \\ &\quad +L_{\mathrm{recon}} +L_{\mathrm{align}} +L_{\mathrm{cycle}} \\ &\rightarrow \nabla_\theta L \end{aligned} } xt:t+9→EG1/SMPL/Teleop→zpre→FSQ→zt∈R64→[zt,st]→DG1→μt→πθ(at∣ot)→at→IsaacLab→rt,st+1→Vϕ→At→LPPO+Lrecon+Lalign+Lcycle→∇θL
到这里,SONIC 的“训练时内部机制”已经基本闭环。
下一阶段:阶段四:从训练模型到真正的 VLA / 外部 Token 输入。
那里我们会把现在的训练链:
motion reference → encoder → token → decoder \text{motion reference} \rightarrow \text{encoder} \rightarrow \text{token} \rightarrow \text{decoder} motion reference→encoder→token→decoder
改成:
VLA → 64-D Universal Token → SONIC Decoder → G1 action \text{VLA} \rightarrow \boxed{\text{64-D Universal Token}} \rightarrow \text{SONIC Decoder} \rightarrow \text{G1 action} VLA→64-D Universal Token→SONIC Decoder→G1 action
尤其值得追 gear_sonic_deploy 和 VLA tutorial 里的实际接口,因为源码里已经出现了一个非常有研究价值的能力:Actor.rollout_with_tokens() 可以绕过 encoder,直接接收外部 (B, 2, 32) FSQ tokens,再调用 decoder 生成 action。源码甚至明确把它描述为 external model 提供 pre-computed FSQ tokens 的路径。(GitHub)
这实际上已经把我们之前提出的 “Universal Token 作为 VLA → Humanoid Controller 标准接口” 从概念推进到了代码级接口。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)