悟界·RoboBrain Orca 表征输入准备——多模态数据采样与对齐检查

文章导读
表征输入准备翻车,多数不是模型的问题,而是采样和检查混在一段脚本里:一旦某个样本对不齐,既不知道是采样丢了帧,还是索引写错了时间戳。建议把这件事拆成两条并行线——采样策略负责“每条模态取哪些点”,对齐检查负责“取出来的点能不能拼回同一个时间轴”。两条线各自留下可回溯的记录,出问题时才能定位到具体样本、具体模态、具体字段。
📋 目录
  1. 壹 按任务频率给每路模态定采样策略
  2. 贰 把采样结果写入中间目录并保留原始索引
  3. 叁 用检查脚本逐样本核对各模态时间戳与长度
  4. 肆 对异常样本分类,决定丢弃、补齐还是重采
  5. 伍 固定一整批样本跑通输入准备流程并留存日志
A A

表征输入准备翻车,多数不是模型的问题,而是采样和检查混在一段脚本里:一旦某个样本对不齐,既不知道是采样丢了帧,还是索引写错了时间戳。建议把这件事拆成两条并行线——采样策略负责“每条模态取哪些点”,对齐检查负责“取出来的点能不能拼回同一个时间轴”。两条线各自留下可回溯的记录,出问题时才能定位到具体样本、具体模态、具体字段。

可先判断:只要多模态数据里存在帧率差异、事件型输入和缺失字段,采样与对齐就必须分开做。操作动作是——先按任务频率给每路模态写采样率与缺失值规则,把结果落到独立中间目录并保留原始索引,再用脚本逐样本核对时间戳与长度,异常样本分类处理后再固定一整批复跑。验证方式是比对索引字段、异常记录和复跑哈希。边界是:采样率、阈值、批大小都需结合具体数据频率和机器资源确认,不套用固定值。

按任务频率给每路模态定采样策略

采样策略的输入不是“数据总量”,而是每路模态在任务中的出现频率。高频流(关节状态、力矩、IMU 这类)通常按固定时间基准取点;图像、点云按帧率;语言指令、任务文本、事件标签这类低频数据按事件触发,不要硬拉到统一帧率上,否则会被静默复制成大量重复行。

建议先落一张三列表:模态 → 采样率或触发条件 → 下采样/上采样方式与缺失值处理。下面是一份可替换的配置骨架,字段名按你实际的数据组织改:

modalities:
  rgb:
    trigger: frame
    target_hz: 10
    resample: downsample_nearest
    missing: drop_and_flag
  joint_state:
    trigger: time
    target_hz: 50
    resample: linear
    missing: hold_last_and_flag
  language:
    trigger: event
    resample: none
    missing: empty_with_flag

高频模态避免主导的做法是限制它的目标采样率,而不是让低频模态去补;低频模态避免被丢空的做法是给它单独的触发规则,并在对齐检查时按事件时间戳匹配最近的邻帧。缺失值处理建议保守:能标 flag 就先标 flag,不要默认插值,插值过的点要在索引里留下 resample_op 字段。

把采样结果写入中间目录并保留原始索引

采样结果必须是可重建的中间产物,而不是覆盖原始数据。目录上把 raw 设为只读,sampled 可以整个删除重跑,index 单独存放:

悟界·RoboBrain Orca 表征输入准备——多模态数据采样与对齐检查
dataset/
  raw/task_0001/...            # 只读,不修改
  sampled/task_0001/
    rgb/000123.jpg
    joint_state/000123.npy
  index/task_0001.jsonl        # 一行一个采样点

索引字段建议至少包含:sample_id、task_id、modal、timestamp_ns、frame_id、src_path、src_offset、sample_path、resample_op、missing_flag。原始文件映射方式用“相对路径 + 帧号或字节偏移”,不要只写文件名——同名文件在不同任务下很容易混。这样排查时可以从 sample_id 直接跳回原始帧,确认是采样阶段丢的,还是检查阶段判错的。

用检查脚本逐样本核对各模态时间戳与长度

检查脚本放在采样之后、进入模型之前,输入是 index 目录,输出是每个样本一行结果。检查项建议固定这几条:各模态在索引中是否都存在;单模态时间戳是否单调不减;各模态覆盖的时间区间是否包住任务区间;帧数或长度与索引记录是否一致;数值 shape、dtype 是否与配置一致。

def check_sample(rec, cfg):
    issues = []
    for modal in cfg["modalities"]:
        if modal not in rec["modals"]:
            issues.append((modal, "missing_modal"))
            continue
        ts = rec["modals"][modal]["timestamps_ns"]
        if any(b < a for a, b in zip(ts, ts[1:])):
            issues.append((modal, "non_monotonic_ts"))
        expected_n = rec["modals"][modal]["expected_len"]
        if len(ts) != expected_n:
            issues.append((modal, "len_mismatch"))
    return {"sample_id": rec["sample_id"], "issues": issues}

期望输出是 pass / fail 两种状态;fail 的样本写入 anomalies.jsonl,字段包括 sample_id、modal、reason、ts_span、expected、actual、src_path。异常样本单独记录而不是直接中断整批任务,这样一批跑完能看到全貌。

对异常样本分类,决定丢弃、补齐还是重采

分类标准建议按“能不能重建”来分,而不是按错误码分:整段模态缺失且原始数据也没有,直接丢弃;时间戳有短暂间隙但两端都有有效帧,按配置补齐并打 flag;不同模态起始错位超出容忍窗口,回到原始数据重采或重新对齐;长度不一致但内容可用,截断到公共时间区间;shape、dtype 与配置不符,丢弃并记录配置版本。

悟界·RoboBrain Orca 表征输入准备——多模态数据采样与对齐检查
{"sample_id": "task_0001/000123", "action": "resample",
 "reason": "ts_offset_exceeded", "before_count": 1, "after_count": 1}
{"sample_id": "task_0007/000044", "action": "drop",
 "reason": "modal_absent_in_raw", "before_count": 1, "after_count": 0}

每个处理动作都写回同一份 decisions.jsonl,并统计处理前后的样本与帧数,按 action 分组。处理前后数量记录的意义在于:如果丢弃量集中在某几个任务,通常说明是那批数据的采集方式有问题,而不是清理规则写得太严。

固定一整批样本跑通输入准备流程并留存日志

流程改完必须固定一批样本复跑。批大小建议先取一次运行能跑完、能装进内存的范围,例如按任务数取 8 到 32 个任务,具体数值结合数据频率和磁盘读速确认,不要一上来就扫全量。

日志字段建议包含:run_id、config_hash、batch_id、sample_id、modal、src_path、sample_path、check_status、action、elapsed_ms、tool_version。config_hash 用于确认两次跑的是同一套采样与检查配置。

复跑一致性检查的做法是:同一 config_hash、同一批 sample_id 跑两遍,把 index 文件按 sample_id 排序后取哈希对比,再对比 decisions.jsonl 的计数。不一致时先看是哪一类字段变了——时间戳变化通常指向原始数据被改动,sample_path 变化通常指向目录或命名规则不稳定。日志留存在每次 run_id 对应目录下,不要覆盖,方便对照。