news 2026/9/15 7:24:21

【SONIC源码阅读系列2】运动数据到观测

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【SONIC源码阅读系列2】运动数据到观测

现在开始整个系列的Phase 0 + Phase 1。这一部分会刻意放慢一点,因为后面 Universal Token、PPO 和 deployment 都建立在这里的数据定义之上。
这次直接以当前 main 分支代码为准,而不是只根据 SONIC report 复述。当前 repo 已经发展到包含 Default SONIC、Low-latency SONIC 和 v1.1,因此个别参数会明确区分“原始 SONIC”和“当前代码”。(GitHub)


Phase 0: VLA → SONIC 的边界

0.1 最终部署系统

当前 repo 给出的 VLA inference 是一个很好的入口:

Camera / Prompt │ ▼ ┌─────────────┐ │ VLA │ │ Isaac-GR00T │ └──────┬──────┘ │ action / motion │ ▼ ┌─────────────────┐ │ SONIC │ │ C++ WBC │ └────────┬────────┘ │ joint control │ ▼ G1

launch_inference.py实际上就是把这个系统串起来:它启动 C++ SONIC deployment,同时启动 Python VLA inference client、keyboard publisher,以及可选的数据 exporter。(GitHub)

所以这里首先要纠正一个非常容易产生的误解:SONIC 并不负责“理解 pick up the cup”。

例如:

"pick up the cup" │ ▼ VLA [task → action / motion intent] │ ▼ motion/action │ ▼ SONIC [motion intent / reference → physically executable whole-body behavior] │ ▼ G1 motion

这也是为什么 NVIDIA 可以把两者独立训练、独立部署。当前官方 VLA workflow 就是:

teleop data ↓ fine-tune Isaac-GR00T ↓ PolicyServer ↓ launch_inference.py ↓ SONIC ↓ G1

而不是重新训练一个端到端:image + language → motor torque的巨大模型。(GitHub)


0.2 重要发现:SONIC Action Space

这里开始出现一个非常有意思的接口。当前 VLA Inference 文档明确写:

unitree_g1_sonic的 action space 是78 dimensions:(GitHub)

64-dim motion token + 7-dim left hand joints + 7-dim right hand joints = 78

这意味着:

VLA │ ▼ ┌──────────────────┐ │ 78-D action │ ├──────────────────┤ │ 64-D motion token│ │ 7-D left hand │ │ 7-D right hand │ └────────┬─────────┘ │ ▼ SONIC

这说明一个非常重要的架构事实:SONIC 的 64-D token 并不只是内部 latent。至少在当前 VLA 接口中,它已经成为一个可以由上层 policy 操作的action-level interface

问题:

Universal Token 到底是 latent representation,还是 robot control interface?

初步判断是:

两者兼具。

但我们后面必须通过代码把它证明出来,而不是先下结论。


0.3 SONIC 并不是“VLA 后面的 PD Controller”

这一点也非常重要。传统机器人 pipeline:

VLA ↓ target pose ↓ IK ↓ joint target ↓ PD ↓ motor

SONIC 更像:

VLA / Motion Source ↓ motion representation ↓ ┌──────────────────────┐ │ SONIC │ │ │ │ representation │ │ ↓ │ │ whole-body policy │ │ ↓ │ │ robot action │ └──────────┬───────────┘ ↓ low-level

所以 SONIC 的贡献不是做了一个更好的 IK,而是把 whole-body coordination 本身学习成一个 generalist policy。这和 SONIC report 的定位一致:官方现在把 SONIC 定义为 humanoid behavior foundation model,通过 scalable motion tracking 从大规模 human motion 中学习统一的 whole-body behavior。(GitHub)


Phase 1:Motion Reference → Observation

现在正式进入源码。整个 SONIC 从这里开始,因为:

policy 学什么,首先由 observation 决定。


1.1 Training 的真正入口

当前训练命令:

python gear_sonic/train_agent_trl.py \ +exp=manager/universal_token/all_modes/sonic_release

(GitHub)

入口:gear_sonic/train_agent_trl.py

train_agent_trl.py │ ├── Hydra config │ ├── environment │ ├── actor │ ├── critic │ └── trainer

然后 Hydra 加载 architecture:

config/base.yaml │ ▼ exp/manager/universal_token/all_modes/sonic_release.yaml │ ├── algo ├── actor_critic ├── manager_env ├── observations ├── rewards ├── terminations ├── events └── aux_losses

官方 training-code 文档已经把这条 composition chain 明确列出来。(GitHub)


1.2 Configuration hierarchy

当前 release 的核心配置是:

sonic_release │ ├── algo │ └── ppo_im_phc │ ├── actor_critic │ └── universal_token/all_mlp_v1 │ ├── manager_env │ │ │ ├── observations │ │ ├── tokenizer │ │ ├── policy │ │ └── critic │ │ │ ├── rewards │ │ │ ├── terminations │ │ │ └── events │ ├── aux_losses │ └── trainer └── PPO + auxiliary loss

(GitHub)

这已经开始暴露出 SONIC 的核心结构:

Environment │ ┌────────────┼────────────┐ │ │ │ ▼ ▼ ▼ tokenizer policy critic │ │ │ ▼ ▼ ▼ Encoder Actor Value │ ▼ Universal Token │ ▼ Decoder

注意:tokenizer 和 policy 是两个不同的 observation group。


1.3 SONIC 的 Observation 并不是一个 vector

看当前:gear_sonic/envs/manager_env/mdp/observations.py。它定义了大量 observation terms。比如 policy group 里包括:

joint_pos joint_vel base_ang_vel gravity_dir actions

以及大量 motion-reference terms:

command_multi_future command_multi_future_joint_pos command_multi_future_joint_body_pos command_multi_future_joint_body_diff_pos ... smpl_joints_multi_future ... motion_anchor_ori_b_mf ...

同时还有 teleoperation:

vr_3point_target_multi_future vr_3point_orn_target_multi_future vr_wrists_local_pos_target vr_wrists_local_orn_target vr_head_local_orn_target

等等。(GitHub)

因此更准确的抽象应该是:

Observation │ ┌─────────────────────┼─────────────────────┐ │ │ │ ▼ ▼ ▼ Motion Reference Robot State Teleop / SMPL │ │ │ └─────────────────────┬─────────────────────┘ ▼ observation dict

然后再根据用途拆成:

obs_dict │ ├── tokenizer ├── policy ├── critic ├── ...

1.4 为什么要拆成 tokenizer / policy / critic?

这是第一个值得研究的设计。官方文档明确:

Policy

给 actor 的:

joint_pos joint_vel base_ang_vel gravity_dir last_actions ...

Critic

可以拿到 privileged information:

base_lin_vel body_pos body_ori ...

Tokenizer

给 UniversalTokenModule:

multi-future joint commands SMPL joints VR targets anchor orientations ...

(GitHub)

因此:

Environment │ ┌───────┴───────┐ │ │ motion ref robot state │ │ ▼ ▼ ┌──────────┐ ┌──────────┐ │ Tokenizer│ │ Policy │ │ Obs │ │ Obs │ └────┬─────┘ └────┬─────┘ │ │ ▼ │ Universal │ Token │ │ │ └──────┬────────┘ ▼ Actor

这个结构说明:

SONIC 的 motion representation learning 和 control policy 并不是完全相同的东西。

它们之间存在一个明确的 bottleneck:

motion observations ↓ tokenizer ↓ universal token ↓ actor

这就是下一阶段要重点扒开 Universal Token 的原因。


1.5 Motion Reference 来自哪里?

进入:

gear_sonic/envs/manager_env/mdp/commands.py

核心类:

TrackingCommand

当前代码对它的定义非常明确:

TrackingCommand是 SONIC-style motion tracking RL environment 的 primary command term。

它负责:

  • 加载 motion library
  • sample motion ID
  • sample episode start time
  • 每个 simulation step 推进 motion cursor
  • 提供 current reference
  • 提供 multi-future reference
  • 提供 joint/body/root/SMPL 等数据
  • 做 robot-local / egocentric / heading-canonicalized transformation。(GitHub)

所以整个 motion pipeline 实际上是:

Motion Dataset │ ▼ Motion Library │ ▼ TrackingCommand │ ┌────────┴────────┐ │ │ current frame future frames │ │ └────────┬────────┘ ▼ observations

1.6 Future Reference 是怎么生成的?

这里终于可以把 report 里的“lookahead”落实到代码。

当前配置:

target_fps = 50 Hz num_future_frames = 10 dt_future_ref_frames = 0.1 s

来自当前configuration.md。(GitHub)

但是这里有一个非常值得注意的细节

当前 training config 的dt_future_ref_frames0.1 s

也就是说训练配置中的未来 reference:

t t + 100ms t + 200ms ...

而模型 card 对 released deployment model 描述的是:

10 future frames 20 ms spacing ≈ 200 ms lookahead

(GitHub) 因此:不要简单把 README 的 release checkpoint observation protocol 和当前 training config 当成完全相同的东西。这是我们后面必须仔细追的一个版本差异。

TrackingCommand内部实际上按照:

frame_skips=dt_future_ref_frames*target_fps

计算未来帧间隔,然后:

future_time_steps_init=arange(num_future_frames)*frame_skips

也就是:

future_time_steps = [0, frame_skip, 2*frame_skip, ...]

(GitHub) 这才是实际 motion sampling mechanism。


1.7 所以一个 Motion Clip 到底发生了什么?

假设某条 motion:

M = {x0, x1, x2, ..., xN}

每个 motion frame 是:

x_t = { root pose body pose joint pose velocity orientation ... }

episode reset 时:

motion_id = k start_time = t0

然后第n个 RL step:

current_time = t0 + n * Δt

TrackingCommand 给出:

x(t) x(t + Δt_ref) x(t + 2Δt_ref) ... x(t + HΔt_ref)

因此 policy 看到的不是:

“机器人现在应该在哪里”

而更接近:

“机器人现在在哪里 + 未来一段时间 reference 会怎么变化”

这其实是 SONIC 的一个非常重要的 inductive bias:

control policy 是 trajectory-conditioned,而不是 single-pose-conditioned。


1.8 更重要:Reference 会被变换到机器人坐标系

observations.py里面大量出现:

*_b *_l *_w *_mf

这几个后缀非常值得我们在以后形成条件反射:

w = world frame b = body / local frame l = local / heading-related frame mf = multi-future

例如:

motion_anchor_ori_b_mf

可以理解成:

motion anchor orientation ↓ body/local frame ↓ multiple future frames

而:

smpl_joints_multi_future

就是:

SMPL joints ↓ multiple future frames

1.9 为什么不直接把 World-frame Pose 给 Policy?

这是 SONIC 非常典型的 robot learning design。假设:

reference: root = (10m, 5m)

policy 其实不应该关心:“机器人在世界坐标的 x=10, y=5。”

它应该关心: “reference 相对于我现在的位置在哪里?”

所以代码大量做:

world frame ↓ anchor frame ↓ robot local frame

例如motion_anchor_gravity_dir()

world gravity ↓ inverse(anchor quaternion) ↓ anchor-local gravity

代码直接实现了这个变换。(GitHub)

这说明 SONIC 的 observation design 在努力做到:

把 global trajectory 转换成 robot-centric control problem。


1.10 一个特别值得关注的变化:v1.1

这里和当前 repo 的 evolution 有关系。

最新 SONIC v1.1 使用:

robot-heading-normalized target orientation

并加入:

wrist-pose augmentation

官方说这是为了提高 whole-body teleoperation 和 VLA execution 的 heading stability。(GitHub)

这说明一个很有研究价值的事实:SONIC 的 performance 很大程度上取决于 reference coordinate representation。

不是:

network bigger → performance better

而可能是:

better reference parameterization ↓ easier learning problem ↓ better whole-body control

1.11 Phase-1 数据流

现在不要急着进入 network。先把我们已经追到的部分固定下来:

Motion Dataset │ ▼ Motion Library │ ▼ TrackingCommand │ ┌───────────┼────────────┐ │ │ │ current future SMPL frame frames frames │ │ │ └───────────┼────────────┘ │ ▼ coordinate transforms │ ┌──────────────┼──────────────┐ │ │ │ ▼ ▼ ▼ Tokenizer Policy Critic observations observations observations │ │ │ │ │ │ ▼ │ ▼ Encoder │ Value network │ │ ▼ │ Universal Token │ │ │ └──────┬───────┘ ▼ Actor [Decoder] │ ▼ Action

1.12 Decoder的输入

之前说:

Motion → Encoder → 64-D token → Policy [Decoder] → Action

这个说法作为高层理解没错,但现在看源码后需要精确化

当前UniversalTokenModule的代码 docstring 实际写的是:

tokenizer_obs ↓ encoder(s) ↓ latent ↓ FSQ ↓ token ↓ decoder(s) ↓ action_mean

而且 decoder 还可以同时接收:

token + proprioception

也就是说更加准确的是:

motion/reference │ ▼ tokenizer obs │ ▼ ┌──────────────────┐ │ Encoder(s) │ │ │ │ G1 │ │ SMPL │ │ Teleop │ └────────┬─────────┘ │ pre-quant latent │ ▼ FSQ │ universal token │ ▼ ┌──────────────────┐ │ Decoder(s) │ │ │ │ + proprioception │ └────────┬─────────┘ │ ▼ action_mean

当前源码明确把它实现成:

UniversalTokenModule

并且其forward()明确返回:

  • action_mean
  • aux_losses
  • decoded_outputs
  • tokenizer_obs
  • encoder_masks
  • encoded_tokens
  • encoded_latents

(GitHub)这其实比我们最开始画的架构更加有意思。


1.13 不止“一个 Encoder”

当前UniversalTokenModule明确是:

encoders = ModuleDict

可以有:

g1 smpl teleop soma ...

当前 release configuration 使用:

G1 Encoder SMPL Encoder Teleop Encoder

而 extended Bones-SEED configuration 还可以加入 SOMA encoder。(GitHub)

所以真正的 architecture 是:

G1 reference │ ▼ G1 Encoder ──┐ │ SMPL ────────┤ │ │ ▼ │ SMPL Encoder ──┼──→ FSQ → shared token │ Teleop ──────┤ │ │ ▼ │ Teleop Encoder ─┘

这才是 Universal Token 真正的含义。


1.14 那么“Universal”究竟是什么意思?

现在我们可以给出一个暂时性的、源码支持的定义

Universal 并不是说一个 encoder 能理解所有 motion。

恰恰相反:

G1 → G1 encoder SMPL → SMPL encoder Teleop → Teleop encoder

每一种输入都有自己的 encoder。

真正 universal 的地方是:

different modality ↓ different encoder ↓ same shared latent/token space ↓ common decoder/control interface

即:

modality-specific encoders │ ┌────────────┼────────────┐ │ │ │ G1 SMPL Teleop │ │ │ ▼ ▼ ▼ Enc-G1 Enc-SMPL Enc-Teleop │ │ │ └────────────┼────────────┘ ▼ FSQ │ ▼ Universal Motion Token │ ▼ G1 Decoder │ ▼ Robot Action

这其实是笔者认为整个 SONIC architecture 最漂亮的地方之一。


1.15 跨模态对齐机制

当前代码中明确出现了 encoder mask combinations:

g1 ↔ smpl teleop ↔ smpl g1 ↔ teleop g1 ↔ soma

以及 auxiliary losses:

G1-SMPL alignment cycle consistency ...

UniversalTokenModule的 forward 还专门保留:

encoded_latents encoded_tokens aux_losses encoder_masks

(GitHub)

这意味着 Universal Token 并不是:

三个 encoder ↓ 简单 concat ↓ decoder

而是在训练过程中存在一种:

让不同 encoder 产生相互兼容 latent 的机制。

这就是我们下一章必须重点拆开的地方。


1.16 最初的第一个研究问题

Q:SONIC 到底看到了什么?

答案现在已经相当清楚:

它同时拥有三类信息:

① Reference motion

current + future

可能来自:

G1 SMPL Teleop

② Robot proprioception

例如:

joint_pos joint_vel base_ang_vel gravity_dir last_actions

③ Privileged state(训练 critic)

例如:

base_lin_vel body_pos body_ori height map ...

(GitHub)

所以它是:

Motion Reference │ ▼ Token │ │ Robot State ───────────┤ ▼ Controller │ ▼ Action

也就是:

motion-conditioned + state-feedback control


1.17 第二个研究问题:为什么要 future reference?

现在也可以更准确地回答。如果只给:

x_ref(t)

policy 需要自己从:

x_ref(t)

推断:

dx_ref/dt d²x_ref/dt² future contact future balance

而现在直接给:

x_ref(t) x_ref(t+Δ) x_ref(t+2Δ) ...

于是 policy 实际上获得:

trajectory context

因此它学习的是:

π(a_t | s_t, R_{t:t+H})

这对 humanoid 尤其重要,因为当前 pose 通常不足以决定:

下一步脚该落在哪里 身体 momentum 怎么变化 手臂应该如何配合

所以 future reference 是一个非常合理的control inductive bias


1.18 一个小问题

既然 SONIC 给了 future trajectory:

为什么还需要一个 learned latent token?

理论上完全可以:

future reference + robot state ↓ MLP ↓ action

为什么还要:

reference ↓ encoder ↓ FSQ ↓ 64D token ↓ decoder ↓ action

判断是:Universal Token 并不主要是为了“压缩”。真正的意义更接近:

把不同 motion modalities 映射到一个统一的 control representation。

也就是:

SMPL G1 Teleop SOMA ... ↓ shared latent ↓ same controller

这比单纯 compression 更重要。下一阶段我们验证这个判断。


Phase 1 小结

┌──────────────┐ │ VLA / Teleop │ └──────┬───────┘ │ ▼ Motion Reference │ ┌────────────┼────────────┐ │ │ │ G1 SMPL Teleop │ │ │ └────────────┼────────────┘ ▼ TrackingCommand │ ▼ Current + Future Motion │ ▼ Coordinate Transform │ ┌─────────┴─────────┐ │ │ ▼ ▼ Tokenizer Policy │ │ ▼ │ Encoder(s) │ │ │ ▼ │ FSQ │ │ │ ▼ │ Universal Token │ │ │ └────────┬──────────┘ ▼ Decoder │ proprioception │ ▼ Action │ ▼ G1 / WBC

下一阶段:进入核心

Phase 2 — Universal Token

不再做高层概括,直接沿着当前代码:

gear_sonic/trl/modules/universal_token_modules.py

往下追。这个文件目前有1256 行,里面已经把 SONIC 的核心机制基本暴露出来了。(GitHub) 我们重点逐行搞清楚:

tokenizer_obs ↓ encoder_index ↓ encoder_masks ↓ G1 / SMPL / Teleop Encoder ↓ pre-quantization latent ↓ FSQ ↓ 64-D token ↓ latent residual ↓ G1 Dynamic Decoder ↓ action_mean

尤其重点帮解决三个问题:

64-D 到底是怎么来的?

不是只说“FSQ 输出 64-D”,而是把:

num_fsq_levels fsq_level_list max_num_tokens down_t

全部对应到数学上的 latent shape。

为什么 G1 / SMPL / Teleop 可以共用一个 token space?

我们会追:

encoder masks + encoder sampling + auxiliary losses + latent alignment + reconstruction

最后回答:

这个 shared latent space 到底是怎么被训练出来的。

为什么 Decoder 还要 proprioception?

这是一个非常关键的问题:

token + proprioception ↓ decoder ↓ action

为什么 token 本身不能决定 action?

这个问题实际上会把我们带进 SONIC 最核心的representation vs control分工。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 7:21:53

MicroPython与RP2040硬件深度协同原理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 7:17:07

AI通识基础Coze平台工作流搭建案例

扣子编程网站:www.coze.cn/store进入官网后点击资源库,再点击右上角资源,选中工作流进行创建。一、生成祝福图片案例需求:输入祝福主题 → 让大模型生成祝福语 → 根据祝福语生成图片描述 → 生成图片 → 输 出图片完整工作流1.开…

作者头像 李华
网站建设 2026/9/15 7:17:04

Web源码泄露防护:从攻击案例到防御体系

1. 从一次应急响应说起去年处理某金融企业安全事件时,发现攻击者仅用3小时就突破了多层防御。复盘发现,问题始于一个被忽视的.git目录泄露。开发团队在测试环境部署时,忘记删除源码目录中的版本控制文件夹,攻击者通过简单的目录扫…

作者头像 李华
网站建设 2026/9/15 7:16:23

外贸网络营销策划方案制定:告别模板丑站,3招搞定建站报价与转化

外贸网络营销策划方案制定:告别模板丑站,3招搞定建站报价与转化 做外贸独立站,最让人头疼的不是代码写不出来,而是做出来的东西“拿不出手”。很多老板拿着几千块做的模板站去谈客户,结果客户连点开的欲望都没有。那种千篇一律的布局、刺眼的配色,加上毫无逻辑的导航,直接暴露了企业的“草台班子”属性。这时候你再…

作者头像 李华
网站建设 2026/9/15 7:15:59

AI驱动的价值投资预警系统设计与实践

1. 项目概述:当价值投资遇上AI预警系统去年帮一家私募基金做技术咨询时,他们正为持仓的某制造业股票突然暴雷焦头烂额。这件事让我意识到,传统财务分析就像用望远镜观察企业——能看到宏观轮廓,却容易忽略近在咫尺的陨石坑。现在我…

作者头像 李华
网站建设 2026/9/15 7:15:36

AD7768多通道同步采样ADC的Verilog驱动设计与FPGA实现

简介:基于Xilinx FPGA的AD7768 Verilog驱动源码,面向高速数据采集系统开发者与FPGA进阶学习者,提供一套已仿真验证、可直接调用的完整控制方案。代码完整覆盖AD7768的上电时序、电源与增益控制、同步输入、工作模式配置以及数据读取链路&…

作者头像 李华