EmbodiFlow文档
开始使用数据工作流与算法训练与部署开发者参考
  • 工作流与算法

工作流

  • 认识工作流
  • 搭建自己的工作流
  • 运行与日志

算法

  • 算法目录
  • 编写自定义算法

算法开发指南

  • 第一个算法
    • 标准化
    • 质检
    • 变换
    • 增强
    • 切分
    • 整理
    • 导出
  • 镜像算法
  • 在工作流中使用算法
  • 接口参考
  • SDK 参考
  • 命令行
  • MCP 工具
  • 问题排查

© 2026 EmbodiFlow

llms.txtOpenAPI JSON
  1. 工作流与算法
  2. 算法开发指南
  3. 按类别
  4. 质检

质检

加入你自己的质检规则:质检算法判断数据,为每条结果给出状态,从不修改录制。

每个团队都有内置质检不知道的规则:某个话题必须存在、丢帧比例的上限、传感器数值的合理范围、任务片段的最短时长。质检算法就是把这样的规则写成代码。它查看数据、报告发现,从不修改任何内容,所以你可以放心地加很多个。

测试阶段。自定义算法和工作流已经在产品里,可以直接试用,但还没有在真实数据上完成完整验收。可能会遇到不完善的地方,依赖结果之前请先核对。

质检默认每条录制运行一次,读取标准化后的 recording;设置 reads="raw" 后读取上传的原始文件。写 level="episode" 后改为每个任务片段运行一次,读取 recording 和 episodes。无论哪种,都通过 out(一个 CheckWriter)写出 checks。

状态

每条结果都有六种状态之一:pass、warn、fail、error、not_evaluated 和 not_applicable。只有 pass 显示为绿色,其余五种都不会显示为通过。这样,一个没能检查数据的质检,永远不会看起来像是认可了数据。

你要写什么

方法作用
out.result(id, status, value=None, threshold=None, message="", evidence=None, at=None)报告一条结果。
out.evidence_path(name)获得一个写证据文件(例如图表)的路径。

每个结果 id 都要先在装饰器里用 checks=[Check(id, title)] 声明,写出未声明 id 的结果会被 CHECK_ID_UNDECLARED 拒绝。结果可以用 at 指明发生的位置,查看器就能直接跳到那一刻。

示例:录制质检

这个质检报告相机出现间隙的帧所占的比例,超过你设定的上限就给出警告。文件是 packages/sdk-python/examples/v1/check-frame-gaps/main.py。

"""Check (recording): the share of frames where a camera had a gap.

In:  the canonical recording (the frames of every camera and which of them are valid).
Out: one check result, "pass" or "warn", with the worst share and where the first gap is.
Why: a camera that drops frames (a USB hiccup on a head-mounted rig, a stalled robot camera) breaks
     video-based training; the check tells you before the recording reaches a dataset version.
"""

from dataclasses import dataclass

from embodiflow import Check, CheckWriter, Context, Recording, algorithm, param


@dataclass
class Params:
    max_ratio: float = param(0.01, minimum=0.0, maximum=1.0, title="Largest accepted gap share")


@algorithm.check(
    name="frame-gaps",
    title="Frame gaps",
    checks=[Check("frame_gap_ratio", "Share of frames missing from a camera")],
    params=Params,
)
def run(recording: Recording, out: CheckWriter, ctx: Context[Params]) -> None:
    worst, worst_key, first_gap = 0.0, "", None
    for camera in recording.cameras:
        valid = camera.valid()
        ratio = float(1.0 - valid.mean()) if len(valid) else 0.0
        if ratio > worst:
            worst, worst_key = ratio, camera.key
            first_gap = int((~valid).argmax())
    status = "pass" if worst <= ctx.params.max_ratio else "warn"
    at = {"startS": first_gap / recording.fps, "endS": (first_gap + 1) / recording.fps, "camera": worst_key} if first_gap is not None else None
    out.result("frame_gap_ratio", status, value=worst, threshold=ctx.params.max_ratio, message=f"{worst:.1%} of {worst_key or 'camera'} frames are gaps", at=at)

它的测试是 packages/sdk-python/examples/v1/check-frame-gaps/test_main.py:用一份很小的测试数据,通过真实的运行器运行这段代码,并检查写出的值。想从它开始,运行 embodiflow algorithm init my-algorithm --category check,它会写出 main.py 和 test_main.py;再运行 embodiflow algorithm test my-algorithm(需要装有 pytest 和 SDK 的 Python)。

示例:检查上传的原始文件

reads="raw" 的质检在标准化之前运行,可以在任何繁重的处理开始之前拦下有问题的上传。这个例子检查 MCAP 文件里是否有标准化算法需要的话题,required 是一个列表参数。文件是 packages/sdk-python/examples/v1/check-raw-topics/main.py。

"""Check (raw): the MCAP file has the topics a standardizer needs, at a sane rate.

In:  the uploaded files (raw), before any standardizing: an MCAP file recorded from ROS 2.
Out: two check results: all required topics are there, and each publishes fast enough.
Why: a missing or slow camera topic is cheapest to find right after the upload, not after a long workflow.
     A missing topic fails the check but not the step; an upload without an MCAP is "not applicable".
"""

from dataclasses import dataclass

from embodiflow import Check, CheckWriter, Context, RawFiles, algorithm, param


@dataclass
class Params:
    required: list[str] = param(["/camera/image"], widget="tags", min_items=1, title="Required topics")
    min_rate_hz: float = param(5.0, minimum=0.0, title="Lowest accepted rate (Hz)")


@algorithm.check(
    name="raw-topics",
    title="Required MCAP topics",
    reads="raw",
    checks=[Check("topics_present", "Required topics are present"), Check("topic_rate", "Required topics publish fast enough")],
    params=Params,
)
def run(raw: RawFiles, out: CheckWriter, ctx: Context[Params]) -> None:
    wanted = ctx.params.required
    if not raw.find("**/*.mcap"):
        ctx.not_applicable("NO_MCAP", "no .mcap file in this upload")
    topics = {t.name: t for t in raw.mcap().topics}
    missing = [name for name in wanted if name not in topics]
    out.result("topics_present", "fail" if missing else "pass", message=f"missing: {', '.join(missing)}" if missing else "all required topics found")
    slow = [name for name in wanted if name in topics and topics[name].rate < ctx.params.min_rate_hz]
    out.result("topic_rate", "warn" if slow else "pass", threshold=ctx.params.min_rate_hz, message=f"slow: {', '.join(slow)}" if slow else "rates are fine")

它的测试是 packages/sdk-python/examples/v1/check-raw-topics/test_main.py:用一份很小的测试数据,通过真实的运行器运行这段代码,并检查写出的值。想从它开始,运行 embodiflow algorithm init my-algorithm --category check --reads raw,它会写出 main.py 和 test_main.py;再运行 embodiflow algorithm test my-algorithm(需要装有 pytest 和 SDK 的 Python)。

示例:任务片段质检

写 level="episode" 后,质检会得到一个 episode 参数,里面有该任务片段的时间窗口和任务文字。这个例子把短到无法训练的任务片段判为 fail,缺少任务文字时给出警告。文件是 packages/sdk-python/examples/v1/check-episode-duration/main.py。

"""Check (episode level): is each episode long enough to train on, and does it say what the robot or person does?

In:  one recording and one episode of it (a start and an end on the recording's timeline, plus a task text).
Out: two check results for that episode, "pass", "warn" or "fail". The step itself still succeeds.
Why: very short episodes and episodes without a task text are the usual reasons a training run learns nothing,
     so this check flags them before the episode reaches a dataset version.
"""

from dataclasses import dataclass

from embodiflow import Check, CheckWriter, Context, Episode, Recording, algorithm, param


@dataclass
class Params:
    min_s: float = param(2.0, minimum=0.0, title="Shortest accepted episode (s)")


@algorithm.check(
    name="episode-duration",
    title="Episode duration and task",
    level="episode",
    checks=[
        Check("episode_duration", "The episode is long enough"),
        Check("episode_task", "The episode has a task text"),
    ],
    params=Params,
)
def run(recording: Recording, episode: Episode, out: CheckWriter, ctx: Context[Params]) -> None:
    # `episode.start_s` and `episode.end_s` are seconds on the recording's timeline.
    long_enough = episode.duration_s >= ctx.params.min_s
    out.result(
        "episode_duration",
        "pass" if long_enough else "fail",
        value=episode.duration_s,
        threshold=ctx.params.min_s,
        message=f"{episode.duration_s:.2f} s",
        at={"startS": episode.start_s, "endS": episode.end_s},
    )
    has_task = bool(episode.task and episode.task.strip())
    out.result("episode_task", "pass" if has_task else "warn", message=episode.task or "no task text")

它的测试是 packages/sdk-python/examples/v1/check-episode-duration/test_main.py:用一份很小的测试数据,通过真实的运行器运行这段代码,并检查写出的值。想从它开始,运行 embodiflow algorithm init my-algorithm --category check --level episode,它会写出 main.py 和 test_main.py;再运行 embodiflow algorithm test my-algorithm(需要装有 pytest 和 SDK 的 Python)。

值得知道

想让质检停止这条录制,就在工作流里给它的步骤设置 onFail: stop。任何一条结果为 fail 时,这条录制后续都不再运行。原始文件质检最适合这样用。见在工作流中使用算法。

在任务片段质检里,recording 参数是该任务片段的时间窗口,episode 参数带有它的任务和标签。

团队质检的结果 id 保存时会加上算法名称作前缀,不会和内置 id 冲突。另外,当数据根本不归你的质检处理时,例如录制里没有 MCAP 文件,请调用 ctx.not_applicable(code, message),而不是报告失败。

下一步

  • 变换算法
  • 接口参考
上一页

标准化

下一页

变换