先分清是感知出错还是动作出错——UnifoLM-WLA-1.0 的失败归因顺序

文章导读
任务失败时,改提示词、换数据集通常是成本最高、信息量最低的两个动作。更省时间的顺序是:先把同一次失败任务的指令、画面、本体状态、输出动作对齐到同一条时间轴,再在输入侧确认模态是否齐全、时间戳是否错位,然后在输出侧确认动作数值是否合法,最后用同一份输入跑两次,看分叉落在哪一段。这条顺序不预设模型能力不行,只依赖日志、配置和可重复的推理入口。
📋 目录
  1. A 把一次失败任务的状态、输入画面、指令和输出动作按时间戳对齐
  2. B 在输入侧检查模态是否齐全、时间戳是否错位
  3. C 在输出侧检查动作序列是否越界或抖动
  4. D 用同一份输入重复跑两次
  5. E 按段位给失败贴标签并留档
A A

任务失败时,改提示词、换数据集通常是成本最高、信息量最低的两个动作。更省时间的顺序是:先把同一次失败任务的指令、画面、本体状态、输出动作对齐到同一条时间轴,再在输入侧确认模态是否齐全、时间戳是否错位,然后在输出侧确认动作数值是否合法,最后用同一份输入跑两次,看分叉落在哪一段。这条顺序不预设模型能力不行,只依赖日志、配置和可重复的推理入口。

判断方向:先归因到“输入没喂进去或喂晚了”,再归因到“动作数值不合法或抖动”,最后才讨论模型本身。适用场景是保留了完整日志的单次失败任务;操作动作是四路时间戳对齐、输入侧模态清单、输出侧越界检查、同输入重跑对照;验证方式是越界帧位置和重跑分叉区段能被他人复现;边界是时钟源不统一或采样未固定的任务,重跑对照的结论不成立,需要先固定随机性再判断。

把一次失败任务的状态、输入画面、指令和输出动作按时间戳对齐

对齐的目的是让后续讨论有共同对象。建议选一个时间基准:优先用机器人控制器的单调时钟(如推理服务里记录的控制 tick 或 time.monotonic()),图像采集和控制日志都换算到它上面。容差通常取一个控制周期或一帧间隔,具体数值需要结合实际的采集频率和控制频率确认,不要凭感觉设。凡是换算后仍落不进容差的片段,先单独标成“无法对齐”,不要混入后面的判断。

时间戳(统一到控制时钟)画面帧指令本体状态输出动作备注
t0frame_id / 有无丢帧指令文本或指令 ID关节角度 / 夹爪宽度动作块起止帧—
t1frame_id同上同上逐帧动作越界或抖动标记
t2时间戳缺失———无法对齐,单独归档

常见无法对齐的片段有三类:日志里压根没有时间戳,图像管线用墙钟而控制用单调时钟,以及采集进程重启导致的时钟跳变。这三类都要在表里明确标注,否则后面的越界检查和重跑对照会被污染。

在输入侧检查模态是否齐全、时间戳是否错位

这一步只排除低级原因:数据根本没送到,或者送到的是上一帧。检查项按模态拆开写:图像是否每帧都有、指令字段是否为空或只含空白、本体状态的维度是否与训练配置一致、每一路的时间戳相对控制时钟的偏移是否稳定。

  • 图像:帧数、分辨率、通道顺序、是否有整帧重复
  • 指令:非空、长度、是否被截断
  • 本体状态:维度、单位、是否用了过期值
  • 时间戳:每路的偏移均值与抖动范围

看日志时可以先用一个通用关键字过滤,字段名按实际日志替换:

先分清是感知出错还是动作出错——UnifoLM-WLA-1.0 的失败归因顺序
# 用途:快速看出哪一路模态缺失或延迟
# 关键字需替换成项目里真实的字段名
grep -nE 'modality|missing|drop|stale|empty|ts_delta' run.log | head -50

如果日志不便加字段,也可以在推理入口做一次轻量断言,把缺失和延迟直接暴露出来:

# 通用接入骨架,字段名与方法名按实际 SDK 替换
def check_inputs(obs, now):
    assert obs.get('image') is not None, 'image missing'
    assert obs.get('instruction', '').strip(), 'instruction empty'
    assert len(obs.get('state', [])) == EXPECTED_STATE_DIM, 'state dim mismatch'
    for name, ts in obs.get('ts', {}).items():
        if abs(now - ts) > MAX_SKEW:
            print('stale or lagging modality:', name, now - ts)

在输出侧检查动作序列是否越界或抖动

输入齐全不代表输出合法。允许的数值范围要有明确来源,通常来自三处:机器人 URDF 或控制器里的关节限位,夹爪等执行器的硬件行程规格,以及模型训练时使用的动作归一化配置。这三处不一致时,以控制器限位为准,归一化范围只用来判断是否发生了缩放错配。

检查时记录越界位置和对应帧,而不是只报一个“有越界”:

# 记录越界帧号、通道与数值,便于回到对齐表定位
for i, a in enumerate(action_seq):
    for j, v in enumerate(a):
        if v < low[j] or v > high[j]:
            print('out_of_range', 'frame=', i, 'joint=', j,
                  'value=', v, 'limit=', (low[j], high[j]))

抖动则看相邻帧的差分是否远超正常运动速度,或同一关节在几帧内来回换向。差分阈值同样要结合控制周期和关节速度上限确认,不能直接套用别的本体。越界帧如果是连续一段,更可能是归一化或单位问题;如果是零星几帧,更可能是数值噪声或推理输入被污染。

用同一份输入重复跑两次

重跑前先固定随机性:把采样温度设为零或等价配置,固定随机种子,关闭会引入随机的数据增强。没有这一步,两次分叉无法区分是模型随机性还是环境问题。

先分清是感知出错还是动作出错——UnifoLM-WLA-1.0 的失败归因顺序

把两次输出并排对齐,按区段标注一致或分叉:

# 同输入重跑对照,输出按帧列出
diff -u run_a.action run_b.action
# 或按区段统计:哪里一致、哪里开始分叉
python compare_runs.py `--a` run_a.npz `--b` run_b.npz `--start-frame` 0

如果两次在开头一段一致、中段开始分叉,重点看分叉点之前的输入是否已经变化,常见原因是本体状态或图像帧在那几帧被替换。如果两次从头就不一致,先确认随机性是否真的固定,再考虑实现侧的不确定性。

按段位给失败贴标签并留档

标签只用三类,避免把问题描述成一团:感知、动作、接口。感知指输入模态缺失、错位、内容错误;动作指越界、抖动、单位或归一化错配;接口指调用失败、超时、字段名不一致、版本不匹配。每条标签都要附上证据文件路径,下一次复现可以直接比对,而不是从零开始猜。

task_id: demo-001
label: 感知
frame_range: 12-15
evidence:
  - logs/infer_run.log        # 含 stale modality 行
  - traces/align_table.csv    # 无法对齐的片段
action_on_next_run: 固定图像管线时钟源后重跑同一输入

留档时把对齐表、输入侧检查输出、越界记录、两次重跑的动作文件放在同一目录,标签写在该目录的说明文件里。这样下一次遇到相似失败,先看标签再决定是修输入、修动作映射还是修接口,不必重新改提示词试一轮。