多步骤装配场景用 GO-3 Preview / 卡点在步骤衔接而非单步动作

文章导读
单步动作单独跑都能过,一连起来就在步骤切换处失败,这类现象通常说明模型对单个动作的拟合不是主要矛盾,问题出在两次调用之间那段没有被人看着的空档:上一步的结束状态没有被确认,下一步的输入已经进来了。处理方向不是继续调单步参数,而是把切换时刻变成可记录的观察对象,先分清是输入抖动还是误差累积,再在衔接处补一条约束做对照复测。GO-3 Preview 这类模型本身的输出好坏,只有把衔接段的输入和状态固定
📋 目录
  1. Ⅰ 把装配流程按步骤边界切开并单独记录切换时刻
  2. Ⅱ 在切换时刻检查输入是否发生突变
  3. Ⅲ 对比连续执行与每步重置两种跑法
  4. Ⅳ 在衔接处补一条约束再复测
A A

单步动作单独跑都能过,一连起来就在步骤切换处失败,这类现象通常说明模型对单个动作的拟合不是主要矛盾,问题出在两次调用之间那段没有被人看着的空档:上一步的结束状态没有被确认,下一步的输入已经进来了。处理方向不是继续调单步参数,而是把切换时刻变成可记录的观察对象,先分清是输入抖动还是误差累积,再在衔接处补一条约束做对照复测。GO-3 Preview 这类模型本身的输出好坏,只有把衔接段的输入和状态固定住之后才谈得上评估。

单步正常而串联失败,先把步骤切换时刻当成独立对象记录,再判断是输入抖动还是误差累积。做法是按步骤边界切分日志、在切换窗口对比工件位置与光照遮挡变化、用每步重置跑法做对照,最后在衔接处加一条等待稳定或条件重拍的约束复测。约束只补在衔接段,单步的判据不要放宽,否则归因会被掩盖。

把装配流程按步骤边界切开并单独记录切换时刻

切换时刻要先有明确定义,否则日志里全是“大概那会儿”。常见的三种判定方式,选一种并在整个工位统一:以上位机下发下一条指令的时间戳为准;以末端执行器由运动转为静止的那一帧为准;以视觉重拍的触发时刻为准。三种时间点在日志里通常会差几十到上百毫秒,混用会让后续对比失去意义,建议固定用下一条指令下发时刻作为 t_switch,其余两个作为辅助字段一起记。

记录字段建议至少包含下面这些,不同接入方式的字段名不一样,下面是通用骨架,字段按实际 SDK 映射替换,代码放在控制循环里两次技能调用之间执行:

def log_switch(prev_step, next_step, ctx):
    t_switch = now()
    return {
        "step_id": next_step.id,
        "switch_window": f"{prev_step.id}->{next_step.id}",
        "t_switch": t_switch,
        "tcp_pose": robot.get_tcp_pose(),          # 位置 + 姿态
        "tcp_speed": robot.get_tcp_speed(),        # 判断是否还在动
        "gripper_state": robot.get_gripper_state(),
        "prev_return_code": ctx.prev_return_code,  # 上一步是怎么算完成的
        "wait_ms": t_switch - ctx.prev_end_ts,     # 两步之间空了多久
        "reframe": False,                          # 本步开始前是否重拍
        "frame_id": camera.last_frame_id(),
        "action_chunk_start": ctx.next_action_index
    }

这一步的验证方式很直接:跑一遍完整装配,看日志里每个 switch_window 是否都有记录、wait_ms 是否为负、prev_return_code 是否出现异常值。如果某个衔接段的 wait_ms 明显偏短,说明上一步还没落稳就开始下一步,这就是后续要补约束的位置。

多步骤装配场景用 GO-3 Preview / 卡点在步骤衔接而非单步动作

在切换时刻检查输入是否发生突变

失败是否由输入抖动引起,看切换窗口前后的输入差就够了。在 t_switch 前后各取一帧图像和一次位姿读数,做同名字段对比。三类变化的记录项可以这样列:

  • 工件位置:位姿平移差(毫米)、旋转差(度)、是否仍在视野内、相对标定基准的偏移方向。
  • 光照:整帧亮度均值差、曝光时间变化、是否发生自动曝光跳档、高光区域面积变化。
  • 遮挡:目标工件的可见像素占比变化、分割掩码面积差、被夹爪或机械臂自身遮挡的比例、检测置信度变化。
{
  "switch_window": "pick_01->insert_02",
  "pose_delta_mm": 1.8,
  "pose_delta_deg": 3.2,
  "brightness_mean_delta": 14,
  "visibility_ratio_delta": 0.09,
  "occluded_by_gripper": true,
  "flag": "input_shift"
}

标记阈值不要照抄别人的数,先按工位本身的夹持容差取:位姿偏移超过夹持容差的一半就标 input_shift,可见像素占比变化大到让检测框明显收缩就标遮挡。阈值属于需要结合环境确认的项,换镜头、换光源后要重新取值。当失败的衔接段集中在 flag=input_shift 的那些记录上,输入抖动这个归因才站得住。

多步骤装配场景用 GO-3 Preview / 卡点在步骤衔接而非单步动作

对比连续执行与每步重置两种跑法

两种跑法的操作差异要控制到只差一处,否则对比没有意义。连续执行是一个回合从头跑到尾,模型保留上一步的动作上下文,机械臂不回原点。每步重置是每步结束后回到固定初始位姿、重新拍一次图、清空上一步的动作历史、重新下发步骤,其他参数完全一致。

观察点主要有四个:失败落在第几步、失败发生在步骤内的第几帧、失败是否落在切换窗口内、该步开始前是否发生过重拍。结果记录建议用固定用例序号逐次记录,不要只记“成功率”:

case_id | 跑法 | 失败步骤 | 失败帧序 | 是否在切换窗口 | 是否重拍 | 备注
A-01    | 连续 | insert_02 | 3      | 是             | 否       | 位姿偏移偏大
A-02    | 每步重置 | —     | —      | —              | 是       | 通过

判读方式比较朴素:每步重置后失败显著减少,且失败点从切换窗口移出,倾向累积误差和状态没交接干净;两种跑法在同一衔接段以同样方式失败,倾向该处输入条件本身不稳定,回到上一节去查抖动。样本量小的时候不要急着下结论,同一用例至少重复几次再看分布。

多步骤装配场景用 GO-3 Preview / 卡点在步骤衔接而非单步动作

在衔接处补一条约束再复测

补约束是为了验证归因,不是为了提指标。一次只补一条,补完立刻用同一组用例复测,否则分不清是哪条起了作用。常用写法有三种,按工位条件挑一种:

# 写法一:等待稳定(速度连续若干帧低于阈值再进入下一步)
wait_until(lambda: robot.tcp_speed() < SPEED_TOL, hold_frames=3, timeout_ms=800)

# 写法二:条件重拍(切换窗口内图像差异过大就重拍一次再送模型)
if frame_diff(camera.frame(-1), camera.frame(-2)) > SHIFT_TOL:
    camera.capture()
    assert workpiece_in_view()

# 写法三:进入下一步前回读上一步的完成判据
tcp_ok = within(robot.get_tcp_pose(), target_pose, tol=GRASP_TOL)
grip_ok = robot.get_gripper_state().closed
assert tcp_ok and grip_ok, "上一步未真正完成"

复测通过的条件建议写成四条,同时满足才算归因成立:同一组用例在加了约束后,失败点从切换窗口移出或该窗口不再复现;把约束关掉,原失败能再次出现;单步动作的成功判据没有放宽,单步指标与加约束前基本一致;日志里 wait_ms 和重拍标记能对应上每一次干预。若关掉约束后失败不再出现,说明前面观察到的只是偶发,需要加长用例或换工位条件重新采一遍,不要直接把约束固化进流程。