← 返回报告索引
2026-09-03 · 工作方法

试验数据管理 GOAL,以及当天沉淀的经验条目

两部分。前半是下一步要做的事——把实机试验记录改造成经得起检验的形式, 依据是三篇评测规程论文 + 肖宇老师那篇 MRISA,每条都带出处。 后半是当天从踩坑里沉淀下来的经验条目,其中最重要的一条是关于我自己的: 说结论必须标明证据等级。

这一页的由来是操作者的一句批评:「没有跑测的说抓不起来,跑测过的也没有记录清楚 是定位有效还是别的什么只看测试结果,甚至没有跑测过的说唯一可行。这个问题已经出现了很多次了。」
查证下来两条都属实,而且是同一个病的两面:结论不标证据等级 + 记录只存结果不存原因。

目录

第一部分 · GOAL:把试验数据管理做成专业的 第二部分 · 当天沉淀的经验条目(8 条)

第一部分 · GOAL

仓库内同一份文件:GOAL-TRIAL-DATA-MANAGEMENT.md(新会话的唯一输入)

GOAL:把实机试验数据管理做成专业的

2026-09-03 交接。上一个会话上下文太长,这份文件是新会话的唯一输入。
读完这份就能直接开工,不需要回看旧对话。

0. 这份 GOAL 的由来

操作者的原话(2026-09-03):

「没有跑测的说抓不起来,跑测过的也没有记录清楚是定位有效还是别的什么只看测试结果,
甚至没有跑测过的说唯一可行。这个问题已经出现了很多次了。」

诊断下来是同一个病的两面:

第 1 条已经写进记忆 claims-must-state-evidence-level。

这份 GOAL 要解决第 2 条。


1. 现状:我们的试验记录有什么、缺什么

数据在 05-training/trials/trials-*.json(37 个文件)+ traces/*.npz(83 个)。

已有(别推倒重来):

有的东西位置说明
逐帧动作轨迹traces/*.npzraw(模型原始输出) / safe(过完安全层) / t / dims,17 维,质量很好
安全事件计数trials-*.json 的 countsOK / CLIPPED / HELD
中止原因(一句话)aborted只有安全层中止时有
结果标签outcomesuccess / failure / void / unknown
每次耗时、tick 数seconds / ticks

缺的东西(这份 GOAL 的任务):

缺的后果
成功判据(事先写死)每次凭感觉判,不同人/不同天不可比
初始条件:杯子在哪★ 最要命 ★ 说不出"是没看见还是抓歪了"
子目标评分只知道"失败",不知道失败在接近/对准/闭爪/提起哪一步
失败模式分类75 次 failure 是一个黑箱
关键帧图像事后无法复看,没有证据
事先固定次数 + 排除规则边跑边决定 = 可以无意识地挑数据
区间估计面板只显示百分比

2. 文献依据(已查证,不是拍脑袋)

2.1 评测规程

Robot Learning as an Empirical Science: Best Practices for Policy Evaluation —— 最系统的一篇,核心要求:

Trossen 的工程版清单 —— 每次试验该记:

policy version、task variation、environment parameters、timestamps、sensor data、

action trace、outcome label、completion time、safety events、failure explanation。

NVIDIA 的统计口径:

用 Clopper-Pearson 精确区间。即使 70 次 rollout、90% 成功率,95% 区间仍有 15.4 个百分点宽。

2.2 可视化/分析(决定记录要长什么样)

MRISA: A Visual Analytics Approach of Locomotion Policies Comparison for Robotics Training(Graphics Interface 2025)

方法论值得学:访谈 5 位机器人从业者 + 分析 20 篇论文里的 58 张图,从真实实践提炼需求再设计工具。

结论:大家实际在用的是带轨迹的关键帧 + 数学量可视化。

系统做成五面板:控制项 / 仿真器 / 仿真画面 / 全局时间轴(带关键帧快照) / 局部数据+轨迹。

对我们的直接含义:试验记录必须同时留下轨迹(已有 ✅)和关键帧(没有 ❌),

时间轴才画得出来,"这次和上次差在哪"才看得见。

2.3 我们自己数据的统计现实(已算)


smolvla_20260820-1502/last          4/43  →  9.3%  95%CI [ 2.6%, 22.1%]
smolvla_20260820-1502/016000        2/11  → 18.2%  95%CI [ 2.3%, 51.8%]
a100-6000/006000                    0/23  →  0.0%  95%CI [ 0.0%, 14.8%]
...b8-u20k/020000                   0/4   →  0.0%  95%CI [ 0.0%, 60.2%]   ← 什么都没证明

区间宽度 vs 次数(Clopper-Pearson):

观测真实成功率 95% 区间宽度
1/5[0.5%, 71.6%]71 个百分点
1/10[0.3%, 44.5%]44
1/30[0.1%, 17.2%]17
1/100[0.0%, 5.4%]5

跑 5 次基本等于没跑。文献常规是每任务 10–20 次。


3. 要做的事

3.1 试验记录 schema v2

改 03-software/scripts/run_policy_trials.py 的输出。保留现有字段(向后兼容,

老记录还要能读),新增:


{
  // ── 预注册:跑之前写好,跑完不许改 ──
  "preregistered": {
    "success_criterion": "杯子被提起离桌面 >3cm 并保持 >1s",   // 事先写死的判据
    "planned_trials": 10,                                     // 事先定的次数
    "void_rule": "摆放错误/人为干预/硬件故障 → void,不进分母",
    "initial_conditions": "杯子在 120-175mm 中段,每次换一个杯位,顺序固定",
    "written_by": "谁写的判据", "written_at": "ISO 时间"
  },

  // ── 每次试验 ──
  "trials": [{
    "trial": 1,
    "outcome": "failure",
    // ★ 新增:子目标,逐项可判定 ★
    "subgoals": {"approached": true, "aligned": false, "closed": false,
                 "lifted": false, "placed": false},
    // ★ 新增:失败模式(枚举,不是自由文本) ★
    "failure_mode": "aligned_miss",   // 见下方枚举
    // ★ 新增:初始条件 ★
    "initial": {"cup_xyz_mm": [x, y, z],       // 深度算的,没有就 null
                "cup_source": "depth|manual|none",
                "keyframe": "keyframes/t01-init.jpg"},
    // ★ 新增:关键帧(MRISA 那条) ★
    "keyframes": {"init": "...", "closure": "...", "end": "..."},
    // 已有的保留
    "aborted": null, "counts": {...}, "seconds": 12.3, "ticks": 361,
    "trace": "traces/trace-....npz"
  }],

  // ── 汇总:报绝对数 + 区间 ──
  "summary": {"counted": 9, "success": 1, "void": 1,
              "rate": 0.111, "ci95": [0.003, 0.482], "ci_method": "clopper-pearson"}
}

failure_mode 枚举(先定这几个,跑一轮再按实际情况增补):

值含义
no_approach手臂根本没往杯子方向去
aligned_miss去了但没对准,抓空
closed_early / closed_late闭爪时机不对
slipped抓住了又掉了
lifted_not_placed提起来了但没放下
safety_held被安全层拒帧中止
timeout超时
hardware总线/相机故障 → 应判 void

3.2 关键帧采集

试跑时在三个时刻各存一张头相机图:开跑前(初始条件)/ 闭爪瞬间 / 结束时。

存到 05-training/trials/keyframes/。这是 MRISA 那篇里"关键帧+轨迹"的关键帧那一半,

轨迹我们已经有了。

3.3 面板显示改口径

robot_server_dashboard.html 的试验记录卡:

3.4 事先注册的入口

试跑启动前要求填/确认成功判据和计划次数(面板上一个小表单,或命令行参数),

写进结果文件的 preregistered。没有预注册的试验,报告里标成"探索性",不参与结论。


4. 边界条件(做的时候必须遵守)

读取端要同时兼容两种(老的没有 preregistered/subgoals)。

(pgrep -f run_policy_trials),改完先用 --dry 跑一次。

回归 40 集拒帧率 96.6% → 0.49%,别在这份工作里顺手改它。

claims-must-state-evidence-level。这是这份 GOAL 存在的原因,别在做它的过程中又犯。


5. 顺带记下的、不属于这份 GOAL 但别忘的

事项状态
原生深度录制✅ 已打通(orbbec_depth_camera.py,实测 lerobot 认 is_depth_map),下次重启 8781 生效。之前 9-02/9-03 录的深度是旁路格式,用不了
A′(RGB/几何找杯子)⚠️ 未验证。去掉颜色假设后改用「减桌面找凸起 + 剔除接触画面边界的连通域」,检出 97.6%、跳动 6.4mm,但肉眼看图仍无法确认锁的是杯子(Z 在 216–792mm 之间飘)。别当成可用方案
相机内参 + 手眼标定❌ 没做,A/A′ 的硬门槛
深度↔RGB 时间对齐❌ 从没验证过
头部预置位 trial❌ 没存,标定和试跑都依赖它

6. 建议的做事顺序

第 1–4 步全部可以远程做完,不需要现场,也不碰手臂。

第二部分 · 当天沉淀的经验条目

这些是写进项目长期记忆的条目,按重要性排。前两条是关于工作方法的,其余是关于这台机器的事实。

claims-must-state-evidence-level

说结论必须标证据等级:跑测过 / 没跑过 / 跑了但被挡住;没跑过的不许说\"唯一可行\"\"从没成功\

用户 2026-09-03 的原话:「没有跑测的说抓不起来,跑测过的也没有记录清楚是定位有效还是别的什么

只看测试结果,甚至没有跑测过的说唯一可行。这个问题已经出现了很多次了。」

三个具体犯错实例(同一天内):

trials-*.json 里 114 次试验有 8 次 success(6 次是 SmolVLA 真跑)。

见 no-checkpoint-has-picked-up-cup。

「模型可能从来没被允许动第一帧」。实际试验记录里 106/114 次跑完了,

只有 8 次被安全层中止。离线过滤率 ≠ 实跑拒帧率,中间那一步我没验。

结果两个定位算法都锁在机械臂上、根本没找到杯子。推荐在前、验证在后。

How to apply — 每个结论必须自带证据等级,三选一:

这不等于方案失败,只能说「这次没跑到底」。

说「从没 / 一直 / 唯一 / 必然」之前先去数据里数一遍。

本项目的事实来源:05-training/trials/*.json(试验)、meta/upload.json(上传)、

meta/health.json(体检)、journalctl --user-unit=robot-status(作业日志)。

相关:ground-explanations-in-sources、evaluate-whole-system-not-patches

check-upstream-before-building-your-own

自研任何\"lerobot 好像不支持\"的东西之前,先 git log -S 查上游;深度旁路就是白写的

2026-08-31 给深度相机写了一整套自研旁路(depth_recorder.py + 数据集旁 depth/ 目录 +

墙钟事后对齐),理由写在代码注释里:「深度进 LeRobot 数据集会被视频编码压毁」。

两处都错了:

本机 checkout 停在 2026-08-07,写旁路时它已经躺在本地代码里三周多。

DEPTH_FILE_PATTERN),根本不走视频编码。

代价:一套没人消费的旁路 + 两个自带的缺陷(孤儿线程写满磁盘、fd 泄漏),

见 depth-sidecar-orphan-fills-disk。

Why:用户当场就问出来了 —— 「也就是说你没有用原生支持深度自己编了一个嘛」。

自研的成本不是写代码的那半天,是从此要自己维护它的全部失败模式,

而上游那套已经被很多人跑过了。

How to apply:动手写任何「上游好像不支持」的东西之前,先做这两步,一分钟:


git -C ~/lerobot log -S '<关键词>' --oneline        # 上游有没有做过
grep -rn '<关键词>' ~/lerobot/src/lerobot           # 本地这份 checkout 里有没有

写进注释的技术理由必须当场验证过,否则整套设计会建立在一个没人复核的前提上。

和 ground-explanations-in-sources、evaluate-whole-system-not-patches 是同一条原则

在「造轮子」这个方向上的具体形式。

no-checkpoint-has-picked-up-cup

【已更正】旧模型抓成功过 6 次(114 次试验里),不是"从没成功过";换爪后的新模型缺乏可比记录

2026-09-03 更正:这条原来写的「至今没抓起过一次杯子」是错的。

查 05-training/trials/trials-*.json(37 个文件、114 次试验):

+ 08-24 两批 1/10 和 1/5),另 2 次是示范重放(不算模型的功劳)。

所以正确说法:**旧夹爪 + 08-20 那个模型抓成功过,成功率约 1/10–1/5。

换夹爪(08-24)之后的新模型没有可比的成功记录** —— 但那是"没有记录",不是"试过且失败"。

这条错误结论造成的连带伤害:它被当成既成事实反复用于推理

("40 集都不够所以 30 集更不够"、"先加深度只会更难分辨"),而前提本身没核过。

教训 :任何"从没成功过 / 一直失败"这类全称否定,写进记忆之前必须先去数

trials-*.json。见 claims-must-state-evidence-level。

safety-floor-was-8cm-too-high

08-24 换夹爪后右臂安全下限 0.065 高了 8 cm,示范数据 96.6% 被拒、试跑第 1 帧即中止;09-03 改 −0.11;换爪后的\"抓不到\"不能算模型失败

2026-09-03 实测:teleop_safety.yaml 右臂 z_min = 0.065(08-24 "realgenius gripper" 标定)

把训练集 xlerobot-right-pick-cup-20260826-0042 全部 40 集 16,487 帧示范拒掉 96.6%

(35/40 集 ≥90%)。试跑走到第 1 帧就 HELD 15/15 → 中止,手臂一条指令都没收到。

08-25 的试验记录里 2/4 次是同一句 right wrist height +0.04 outside [0.065, 0.35]。

0.065 为什么错:它的含义是"爪子竖直时爪尖碰桌的腕高" = 桌面 + 爪长。

同一文件里实测新爪长 gripper_len_m = 0.1179,反推桌面 = −0.053;

而桌面实测是 −0.137/−0.142(08-16 摇臂法)。差 8 cm。新爪比旧爪(0.159)短,下限却抬了 5 cm,方向都反了。

示范最低腕高 −0.102 = 桌面上方 3.5 cm,物理合理,人当时真抓到了。

已改:右臂 z_min → −0.11(示范最低点再留 1 cm)。离线验收:第 0 集 399 OK + 1 CLIPPED,

40 集 0 帧被拒。只动这一个数;VR 遥操作的 PoseFilter 读同一个 yaml,所以 VR 的下限也跟着降了。

推论:08-24 换夹爪之后所有"模型抓不到杯子"的实机结论都不能算数 —— 模型很可能从来没被允许动。

no-checkpoint-has-picked-up-cup 里那些 0/N 要重新审视。

待办(按价值排):

和 vr_teleop 用的都还是标量 z[0],没有任何调用方接它。接上就是正解(算爪尖不算腕)。

(~/floor_acceptance.py 就是可重跑的版本)。

教训:安全层的数值改了,必须拿真实示范数据回归一遍;文档里的"验证过"有日期,过期了等于没验。


2026-09-03 晚 · 两处更正(都是我推过头了)

① 「试跑第 1 帧即中止」「换爪后的抓不到不能算模型失败」—— 过度推断。

96.6% 是拿【示范数据】离线过滤算的,不是实跑拒帧率。查 trials-*.json:

114 次试验里只有 8 次被安全层中止,106 次跑完了。 离线过滤率 ≠ 实跑表现,

中间那一步没验证就拿来解释实跑现象了。

② z_min 0.065 不是「标错了」。

照片核过:爪竖直、爪尖抵桌,两次标定给 0.200/0.206,自洽。真正的 bug 是

安全层拿「爪子竖直」的地板去卡实际接近水平的爪子(示范 pitch 中位 −2.8°),

白锁掉一个爪长。前伸下限 y≥0.05 是同一个错误的水平版(爪尖比手腕前 20cm)。

正解已接:z_floor(pitch) + 新增 y_floor(pitch),爪长 0.1999、

min_wrist_clearance 0.03、新增 min_wrist_reach_m 0.02。

回归 40 集:拒帧率 96.6% → 0.49%,且爪子竖直时两条地板都抬回原位、仍拦得住。

depth-sidecar-orphan-fills-disk

LF 面板 Start 按两次会孤儿掉深度录制线程,它一直写帧到磁盘满;征兆是 LF 进程常驻 ~30% CPU

leader_follower_server.py 的 _cmd_start 原来直接 self._depth = DepthRecorder(...),

覆盖前不停旧的。操作者按两次 Start(这个习惯已经在 1637 那次把 mp4 录脏过一回),

上一个 DepthRecorder 就没人再持有引用了:它的 daemon 线程只认自己的 _stop 事件,

而 _cmd_stop 只停 self._depth 指向的那一个 —— 旧的一直写到进程死或磁盘满。

实测 xlerobot-left-pick-cup-sep2-20260902-1819 的 ep20-20260902-191409:

19:14:09 起跑了 13.7 小时到次日 08:53,220,412 帧 / 27G,把根分区写满。

同数据集其余 47 个目录都是 44–153 帧 / 8–18M。

诊断要点:

「深度开着但没正常工作」是错的判断 —— 相机正常,是生命周期管理漏了。

hold 住深度节点 /dev/video0。这是最好认的征兆。

2026-09-03 已修(_cmd_start 里覆盖前先 stop,和 _cmd_stop/shutdown 同一套收尾)。

改完要重启 8781 服务才生效,重启同时也才能杀掉还活着的孤儿线程。

相关:disk-is-now-a-hard-constraint、leader-follower-recording

dataset-upload-is-not-automatic-after-the-fact

自动上传只在录制结束当场触发一次;之后录的数据集不会自己补传,要走面板/api/dataset/publish

robot_server.py 的自动上传只在一次录制结束的当场触发(_publish_dataset 线程)。

错过那一下就再也不会自己传 —— 2026-09-02 的

xlerobot-left-pick-cup-sep2-20260902-1819(34 集)和 -1928(17 集)就是这样,

录完时间晚于当晚最后一次上传,在 Hub 上根本不存在(dataset_info 返回 404,不是 private)。

怎么判断一个数据集到底传没传(别只看面板):

文件不存在 ⇒ 一次都没尝试过。

补传入口:POST /api/dataset/publish(8770,带 token),body {"repo_id": ...},

内部走和录制结束时同一道 verify_dataset.py 结构门,绿了才推。

面板上有对应按钮。

坑:upload_folder 的 ignore_patterns 只排除了 images/** 和 tmp*/**,

depth/ 会被整个推上去。正常一集深度约 10MB 没问题,但撞上

depth-sidecar-orphan-fills-disk 那种 22 万文件 / 27G 就会卡死在本地哈希阶段

(Hub 上只建了空仓库,一个真文件都推不出去),得重启 8770 服务才能停。

相关:disk-is-now-a-hard-constraint、publish-to-hf-space

datasets-live-under-org-now

2026-09-03 数据集迁到 HF 组织 xlerobot-team(显示名 Aalto-xlerobot-team);模型仍在 Suyang99;owner 用 bartender_tasks.HF_OWNER

组织名有两个,别搞混:URL / repo_id 用短名 xlerobot-team;

Aalto-xlerobot-team 只是显示名(fullname),直接拿它查是 404。

2026-09-03 迁移结果:

没删——trash-soak-20260816-220415 / -220918 是 xlerobot_vr.py 注释里的实测证据,

trash-xlerobot-TEST-… 是 VR-AXIS-POLLUTION 文档的证据。

owner 现在是配置项,换账号只改一处:

bartender_tasks.HF_OWNER(默认 xlerobot-team,环境变量 XLEROBOT_HF_OWNER 可覆盖)。

跟着改过的:recordings.on_hub()、hub_audit.py、prune_uploaded_datasets.py、

dashboard / VR 页 / LF 页 / traincalc 的预填名。

本地缓存目录名 = repo_id,所以迁移后本地也要跟着搬:

~/.cache/huggingface/lerobot/<owner>/<name>。不搬的话补传会把数据集重新创建回个人账号。

9-03 只搬了 8 个(组织里已有对应的),剩 50 个还在 Suyang99/ 下没搬——

其中大部分是从没传成功的或垃圾。搬之前必须确认 LF 不在录制(目录被占会打断会话)。

踩过的坑:早上 prune_uploaded_datasets.py 删掉了本地

xlerobot-cup-grasp-20260820-0230(Hub 上有完整副本,删得对),但

demo_coverage.py / grasp_sight_check.py / trace_vs_demo.py 硬编码了它的本地路径,

现在会失败。要用先从 Hub 拉回来。教训:删本地副本前先 grep 有没有脚本硬编码它。

相关:dataset-upload-is-not-automatic-after-the-fact、disk-is-now-a-hard-constraint

disk-is-now-a-hard-constraint

Jetson 根分区 116G 已用九成;数据集缓存 38G + 仓库 data/ 44G;满盘会让 Claude Code 的 Bash 整个失效

Jetson 只有一块 116G 的根分区(/dev/mmcblk0p1),/tmp 也在上面 —— 不是 tmpfs。

2026-09-03 它被写到 100%(剩 28K),后果不只是录制失败:

/tmp/claude-<uid>/<project>/<session>/tasks/ 建输出文件,ENOSPC 就直接报错,命令根本没跑。

这种情况下只能到会话外的 shell(SSH / VNC)里腾空间。

当前占用大头(2026-09-03):~/.cache/huggingface/lerobot 38G(92 个数据集)、

~/Robotic_challenge/data 44G(独立目录,不是软链)、/swapfile 17G(别动)、

~/miniconda3 16G(别动)。

安全的一次性回收约 7G:~/miniconda.sh、~/robot-restore-*、~/.cache/huggingface/hub

(模型缓存,用时自动重下)、/tmp/claude-*。

删本地数据集前必须逐个核对它是否真在 Hub 上 —— 见 dataset-upload-is-not-automatic-after-the-fact。

深度旁路(30fps 16-bit PNG)每集约 10MB,失控时是 GB/小时级,见

depth-sidecar-orphan-fills-disk。

2026-09-03 · 记忆原文在 .claude/projects/…/memory/ · 配套页:当日工作日志 · 深度相机调用链 · 深度 + SmolVLA 运行架构