北京人形机器人创新中心 + 百度智能云办的「具身智能机器人黑客松」。我单人参赛,报了两个赛道:运控·复杂地形(基于开源 TienKung-Lab,天工人形)和 VLA·操作(五个 ACT 任务)。动作模仿子赛道最终没做。
先把结论摆在前面:两条腿都在截止前提交了,但都没有任何官方分数或名次。 运控是邮件提交、无反馈;VLA 的分数只在官方评测平台上,本机网络连不到那个平台,我从头到尾没亲眼看到过数字。自评是名次弱。这页有价值的不是成绩,是过程里的两起险情和一次 reward 假象。
时间线:一份手册推翻了三条继承认知
6-10 开始预研。7-17 17:00 官方 GPU 机开通,7-24 17:00 到点自动释放——这是个硬窗口,不是软 deadline。
7-21 才拿到官方手册,一读推翻了三条一直当前提在用的认知:截止是 07-31 不是 07-24;VLA 赛道只给评测算力、不给训练算力;赛题是 TianYi2.0 + xMimic 而不是 RoboMIND,前期情报押的基线全部作废。7-27 评测平台开放,7-30~7-31 正式评测,7-31 24:00 两赛道均已提交。
两起工程险情
这两件事比任何指标都值得写,因为它们各自差一点就让整条腿归零。
一、限时算力到点销毁,唯一成果靠抢救
现象:官方机是容器,对外网络几乎全封——只有几个镜像域可达,github / pytorch / 微软全部 timeout。进出数据只能靠本机中转 + tar-over-ssh 流式推拉。7-23 起原本走的网盘备份链路又出现账号级 403,等于断了一条腿。而机器 7-24 17:00 按时释放,上面的东西全部消失。
根因:不是网络,是我一直在手动挑文件的低效模式里,根本没有一个在跑的自动备份——没 cron、没进程。用户当场点破的那句是”为什么不设置自动全部传输”。这句话的分量在于:手动挑的前提是”我知道哪些重要”,而限时资源销毁时,你恰恰来不及重新判断什么重要。
解法:7-24 上午把 8 个实验 run(8.4 G)推到网盘,把唯一成果(冠军 run 全量 + 5 个 ckpt + 提交包 + 部署代码)按整目录拉回本机。核心成果 6 件(5 个 agent_best.ckpt + model_21299.pt)逐件 md5 硬验证——不是”看着文件在那儿”就算数:model_21299.pt 22708831、5 个 ckpt 各 336005993,全部对上;运控提交包那 10 件物料另走逐项对照验收(含 onnx 2206520)。17:00 机器按时释放。
判断机器到底有没有被回收,有个反直觉的细节:SSH 和裸 TCP 全不可达、但 ICMP 还通。这不是网络抖动,是实例已经被回收、地址落到了边缘网关上——ping 得通只说明有东西在应答,不说明你的机器还在。
当天没做到的那一半,我当场就标了:抢救完成时,验证强度只到”本机 md5 硬验 + 远端服务端尺寸与 manifest 佐证”,云端那份没有下载回验、没有逐字节硬核对,这一点在当时的记录里如实写了。真正的 6/6 唯一成果各 ≥2 份独立硬验证副本(本机 md5 + 云端逐字节 md5 往返)是赛后 8 月初做备份基建时才补齐的,不是抢救当天的功劳。把这两件事分开记,比合并成一句漂亮结论有用得多。
顺带记下一个静默的洞:某网盘的 CLI 在后台 / cron 环境下会失败(它要 tty)。所以自动备份改用另一家,那家只留作手动首选——否则就会变成”以为在自动备、其实那个 cron 从来没跑成过”。
教训:限时算力是不可再生资源;备份必须是在跑的自动化,不是手动挑;“传完了”和”验过了”是两件事,服务端尺寸对上不等于下载回来逐字节一致。
二、1292 字节差点报废整条 VLA 腿
现象:本机盘用到 93%,为了腾空间删掉一个已经打包过的归档。删之前我核对了——那个 336 MB 的 agent_best.ckpt 在别处确实有一份,判定冗余,放行。漏掉的是同目录里 1292 字节的 dataset_stats.pkl(训练时的归一化统计)。ACT 推理要求这个文件与 ckpt 同目录,缺了直接 FileNotFoundError。07-27 正式评测前准备部署包时才发现五个任务的 stats 全丢了。
代价评估:如果找不回来,要么重下 57 G 数据集重算归一化,要么冒归一化漂移的风险硬跑。在评测开始前几天,这等于整条 VLA 腿报废。
解法:4 个从另一个进程 07-24 保命时留在临时目录里的完整副本中捞回,剩下的 lab_task_03 从某网盘 22 G 全量备份里的 lab_task_03.tar 中流式取出(不落全量、只取那一个成员)。5/5 全部找回并逐件校验。
回头看,救回那 4 个的其实是三天前那个”低效”的动作:抢救时是按整目录拷而不是挑文件拷,所以 1292 字节的 stats 顺带被带了出来。当时嫌整目录笨重,结果它成了三天后的保险——这不是台账里写好的因果,是复盘时才看出来的。
根因:我验错了完整性维度。删 bundle 时只枚举了最大最显眼的文件,没有 diff 全部内容。
教训:冗余 < 错误 < 丢失。盘紧的正确动作是加盘或 offload,不是删成果。同目录的小文件(stats / config)最容易被”只拷大件”漏掉,而它们往往最致命——尺寸和重要性没有相关性。
运控赛道
最终提交 model_21299(变体 vC_fwd,iter 21299,22,708,831 B)。观测 750 维(75 维单帧 × 10 帧 history)→ 动作 20 维(12 腿 + 8 臂,不含腰),actor 是 MLP 750-512-256-128-20,551,316 参数。8192 个并行环境,约 4.56 s/iter。
客观指标不看 reward,用自写的运动学 probe 测(净前进速度 + 直立占比,对 reset 免疫):净前进 0.209 m/s、直立 71.6%。训练日志 iter21299 侧:mean_reward 42.77 / ep_len 817 / terrain_levels 2.09 / track_lin_vel_xy_exp 1.204。
变体谱系(负结果是这张表的主要内容)
| 变体 | 净前进 m/s | 直立 | 结论 |
|---|---|---|---|
| vA_rough | 0.085 | — | 只证明移植跑通,砍 |
| vB_comp6 | −0.010 | — | reward 26→52 但实测原地踏步,否决,留作负结果基线 |
| vC_fwd | 0.209 | 71.6% | 采纳,最终提交 |
| vD_push | 0.210 | — | plateau,“加速”这根杠杆到顶 |
| vR_recovery | 0.223 | 66.6% | 更快但更爱摔,否决作主策略 |
| vS_stable | 0.069 | — | 摔倒惩罚 −200→−350 反而更慢,backfire |
| vP_clean | 0.189 | — | 清理抢梯度项,无改善 |
配置上用 walk_cfg.py 的 env-gate 开关,一份安装并行跑多路,只加门控不删原配置——这样任何一路失败都不会污染基线。
提交包
10 件:policy.onnx(2,206,520 B 完整格式)、policy.pt、35 秒 xSim_mujoco 演示视频、剥离 ROS2 的 Deploy 3.0 sim2sim 代码、xsim_direct_drive.py、agent.yaml / env.yaml、onnx_verification.txt、eval_model_21299.md、SUBMIT_README.md。
onnx 数值一致性:5 次随机 obs 前向,onnx vs jit .pt 最大绝对误差 2.146e-06(门槛 1e-5)。这一步是在训练刚开始的时候就做掉的,理由很简单:导出这关过不了,后面全是白训。
接口核对上有个挺舒服的发现:官方 Deploy 3.0 的 walk_amp 是 obs 840 / action 23,与我方 750 / 20 的差值恰好是 3 个腰关节(84−75 = 3×3、23−20 = 3),且单帧组成两边同序。所以我方策略是状态机的 drop-in 替换。
VLA 赛道
数据是 X-Humanoid VLA-Challenge-Dataset,56.93 GiB / 1014 条 hdf5 / 5 个子集,state-action 26 维(左臂 7、左手 6、右臂 7、右手 6)。五个任务:齿轮入托盘、开关入篮、传开关到托盘、开电子天平门、关电子天平门。
模型用官方 ACT baseline(纯 torch,不需要 Isaac),ResNet18 backbone,83.92M 参数,chunk 50。五个 agent_best.ckpt 各 336,005,993 B。val_loss 分别是 0.109 / 0.298 / 0.182 / 0.243 / 0.276。评测走 ZMQ 对接组委会仿真,五个任务各约 25 次执行(官方要求 20+),全部 task-ok。
实测闭环成功率约 0。
把低分诊断做到底
val_loss 0.1 和成功率 0 放在一起,第一反应一定是”哪里有 bug”。我要求把外因一条条排干净,最后拿到三条决定性证据:
- 对齐数据与原始关节数据逐字节相同(仅采样率 2 倍差异)——排除系统性关节偏移。
- 离线 replay:模型预测的 action 与 hdf5 里记录的 action 吻合,整段 RMS 0.1~0.23 rad,正好对应 val_loss 的量级——说明推理链路忠实,没有隐藏 bug。
- 亲眼比对训练帧与仿真评测帧:同为 Isaac 渲染、同视觉域、同场景(训练数据本身就是仿真遥操作采集的)——排除 sim-to-sim 域差。
结论:val_loss 0.1~0.3 与成功率约 0 并不矛盾。 这是行为克隆开环训练、闭环 rollout 时误差累积走出分布(OOD)的结构性天花板,不是 bug、不是域差。开环每一步都对着专家轨迹上的状态预测,闭环里第一步的小偏差把机器人带到训练分布外,之后每步都在没见过的状态上预测,误差自乘。这个诊断的价值在于它划清了边界:想提成功率不是去调 lr,是要上 chunk 聚合 / 更强 backbone / 交互式数据(DAgger 类)或者干脆换范式。
其他工程坑
- reward 假象(最值钱的一条):vB 的 reward 从 26 涨到 52,看起来一路向好。probe 一测,净前进速度六千迭代死卡在 0——机器人找到了”原地踏步刷步态奖励 + 存活奖励”的局部最优。识破它靠的是自写运动学 probe,不是看 reward 曲线;而触发我去写 probe 的是用户一句”这么顺利?你确定吗”。locomotion 必须量净前进速度,训练指标不是比赛分数。 补一句不太光彩但必须写的:这个 probe 自己也先错过一次。早期它报”70% 摔倒”,查下去是工具里的 sticky-flag——姿态标志一旦置位就不再清除,于是一次瞬时倾斜被算成了后面全程摔倒。修掉之后实测 vA 直立 97.3%、vB 98.9%。所以”别信 reward、信我的 probe”这句话本身也不成立,正确的是每一层测量都要被质疑一次,包括你为了质疑别人而造的那个工具。
- 杠杆到顶就要换杠杆:vC 靠加大前进奖励破了原地最优(0 → 0.209),vD 继续加只到 0.210。换的两条新杠杆(降摔倒惩罚 vS、清理抢梯度项 vP)也都失败——三条路一起证明 vC 就是当前配置下的天花板,再调是浪费窗口。
torch.cuda.is_available()是 True 但跑不了任何 kernel:预装的 torch 只编到 sm_90,卡是 sm_120,一律 no kernel image。要装对 CUDA 组合、并且实测一次真卷积跑通才算数,is_available()什么都不证明。- 离线机上唯一的真 blocker:某可视化配置的
debug_vis=True会去拉远端资产服务器上的 USD,离线环境下无限等待。通用死法:凡 debug_vis / marker / GroundPlane 这类东西,先查它是否走远端资产源。 - 日志有结果但进程是死的:训到 max 之后进程不退出、空转了 8 小时,而我读的是 stale 日志、当成实时进度。判断”还在跑”要验新鲜度(日志 mtime、两次读 iter 是否变、GPU util、进程 etimes),后来上了 watchdog。另一个连带坑:warm-start 的 iter 是全局累加的(1300 + 20000 → 21300),拿”相对 max”判完成会把卡死误判成训完。
- 两个日志源打架该信谁:查进程自己打开的文件描述符(
/proc/<pid>/fd),找到真正在被写的那个 log 来裁决。这比看时间戳可靠。 - 评测会话一次性、掉线不可重连:握手只在会话起点发一次,驱动端中途死掉的话,仿真会带着一个冻住的机器人跑完剩余全部场景、全判失败,整场报废。我曾经因为给 runner 套了一个超时上限,把一场正在计分的活会话杀掉。之后的铁律:脱离 SSH 挂着跑、绝不套超时上限、一口气驱动到自然结束。
- 配置错被过度理论化:评测第一次卡死,根因只是端口用了默认值而实际不同——一个参数。但我从那个现象推断出一整套”计分必须由平台侧发起”的架构理论,越走越远。用户一句”别瞎测赶紧看权威文档”把我拉回来,官方群里一条消息秒解。教训:握手不通,先穷尽最笨的端口 / host / 默认值,再上升到协议层假设。
被否决的方案
- 自费租消费级显卡训练:否决。在什么环境比就在什么环境训,CUDA 版本不同会导致 sim2sim 错配。
- 选动作模仿子赛道:完成确定性最高,但被用户以”高风险高收益 / 场子太薄”否决,改选复杂地形。
- 直接上大模型策略:否决,算力训不动,先把 ACT 基线复现出来。
诚实的边界
- 两赛道都没有已知成绩或名次,VLA 的分数我从未亲眼看到。
- 0.209 m/s 是在指令速度 1.0 m/s 下的实测,约 21% 达成;直立 71.6% 意味着在难地形上约有 1/4 的时间姿态不达标。总训练量约 21k 迭代 / 1.5 天,而仓库参考的收敛量级是 5 万迭代。自评中等偏弱。
- 演示视频是平地行走,不是复杂地形穿越——复杂地形只体现在训练侧的 terrain_levels 等指标上。
- 训练本体 20 DOF 与出片 / 部署本体 29 DOF 是同族不同模型,多出来的关节按默认固定。终版策略在后者上的行走质量没有做独立评估。
- 官方要求的 ROS2 双进程部署路径从未真正跑通(离线环境装不了),提交的是剥离 ROS2 的等价直驱脚本,提交说明里已如实标注。
- 风格多样性与摔倒自恢复这两个评分维度基本没拿下,最终提交的是纯通过性策略。
- VLA 成功率约 0,已诊断到结构性天花板,剩下只有廉价调参的小 upside。
- 动作模仿子赛道没做。
- 96G 卡的窗口大约浪费了一半:单变体独占空转 24 小时 + 训完卡死 8 小时 + probe 静默失败一下午。根因是同一条”看到症状不追到底”的病根多次复发。
- 全程代码由 AI 编写执行,我做需求、赛道取舍、路线拍板、质疑与验收。