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

工作流

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

算法

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

算法开发指南

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

© 2026 EmbodiFlow

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

增强

给录制增加新的信息,比如位姿、标签或嵌入向量,而不改动录制本身。

增强大概是你最常用的类别。它给录制增加信息:手部关键点、头部位姿、场景标签、嵌入向量、每个任务片段是否成功。录制本身完全不变,平台会把你的特征合并进保存的结果,查看器、整理和导出都能用上它们。

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

增强算法默认每条录制运行一次,写 level="episode" 后每个任务片段运行一次。它读取 recording(任务片段级还会读取 episodes)以及你声明的额外 features 输入,并通过 out(一个 FeatureWriter)写出 features。

声明特征

在装饰器里列出你要写的每个特征:Feature(name, type, per, dtype=…, shape=…, unit=…, frame=…, names=…, source=…, describe=…)。type 是接口参考注册表里的语义类型,工作流编辑器就按它检查连接。per 决定了写法:

per方法保存为
frameout.per_frame(name, values)每帧一行的一列。行数不对会报 FEATURE_ROW_COUNT_MISMATCH。
recordingout.per_recording(name, value)一个小的 JSON 值。
episodeout.per_episode(name, value)每个任务片段一个值。
fileout.file(name, path)一个文件,例如深度视频。

示例:逐帧的运动强度

就是第一个算法里的那个。文件是 packages/sdk-python/examples/v1/enrich-motion-energy/main.py。

"""Enrich: the mean absolute difference between consecutive frames of the first camera, per frame.

In:  the canonical recording.
Out: one new per-frame feature "motion_energy"; the recording itself is not changed.
Why: idle stretches (the arm waits, the person stands still) show as low motion, so a later step can
     segment or drop them. A feature is how an algorithm adds knowledge without rewriting the recording.
"""

from dataclasses import dataclass

import numpy as np

from embodiflow import Context, Feature, FeatureWriter, Recording, algorithm, param


@dataclass
class Params:
    step: int = param(1, minimum=1, title="Frame step")


@algorithm.enrich(
    name="motion-energy",
    title="Motion energy",
    description="How much the first camera's picture changes from one frame to the next.",
    features=[Feature("motion_energy", type="scalar", per="frame", dtype="float32", unit="none")],
    params=Params,
)
def run(recording: Recording, out: FeatureWriter, ctx: Context[Params]) -> None:
    cam = recording.cameras[0]
    energy = np.zeros((recording.num_frames, 1), np.float32)
    prev = None
    for frame in cam.frames(step=ctx.params.step, gray=True):
        current = frame.image.astype(np.float32)
        if prev is not None:
            energy[frame.index, 0] = np.abs(current - prev).mean()
        prev = current
    out.per_frame("motion_energy", energy)

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

示例:每个任务片段一个值

写 level="episode" 后,函数每个任务片段运行一次。它的 recording 参数是该任务片段的时间窗口,所以帧数和时间对每个任务片段都从 0 开始。文件是 packages/sdk-python/examples/v1/enrich-episode-length/main.py。

"""Enrich (episode level): the length of each episode and whether it is long enough to train on.

In:  the canonical recording and one episode of it, the step runs once per episode.
Out: two per-episode features: "length_s" and "is_long".
Why: shows the episode level: the step gets the episode window, not the whole recording.
"""

from dataclasses import dataclass

from embodiflow import Context, Episode, Feature, FeatureWriter, Recording, algorithm, param


@dataclass
class Params:
    long_s: float = param(1.0, minimum=0.0, title="Seconds an episode needs to count as long")


@algorithm.enrich(
    name="episode-length",
    title="Episode length",
    level="episode",
    features=[
        Feature("length_s", type="scalar", per="episode", dtype="float64", unit="s"),
        Feature("is_long", type="label.bool", per="episode"),
    ],
    params=Params,
)
def run(recording: Recording, episode: Episode, out: FeatureWriter, ctx: Context[Params]) -> None:
    length = episode.duration_s  # seconds, on the recording's timeline
    out.per_episode("length_s", length)
    out.per_episode("is_long", length >= ctx.params.long_s)

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

值得知道

名称。 你写 motion_energy 这样的短名称,平台会以团队专属的名称保存。和内置特征重名会被 FEATURE_NAMESPACE_VIOLATION 拒绝。

几何类型。 几何类型(pose.se3、keypoints3d.hand21、keypoints2d、深度)需要 unit 和 frame,按 file 保存的除外。逐帧估计值还应该写一列置信度。缺失值用 NaN 或 -1 标记,不要悄悄插值。

额外输入。 用 inputs={"hands": Input(requires=["keypoints3d.hand21"])} 声明。工作流编辑器只会连接声明了所需类型的输出。输入可能缺席时加 optional=True,此时参数为 None。

重定向动作。 类型为 action.joint 或 action.ee_pose、名为 action 的特征,可以用 source="retargeted" 和 describe={"targetRig": …} 写出。人类录制只能通过这种方式带上动作,控制台会把它标为重定向。

模型。 定义 setup(ctx) 来只加载一次模型,在 run 里用 state 接收它的返回值。不要在全局变量里保存随条目变化的状态。

任务片段级步骤。 任务片段级步骤对 episodes 输入里的每个任务片段运行一次,最多同时运行 4 个,最多处理 100 个。一个任务片段失败不会连累其他任务片段:只要至少有一个成功,步骤就算成功,运行会标为部分成功,并列出失败的任务片段。没有任务片段时步骤被跳过,按「不适用」处理。

合并。 你不需要自己合并特征。特征连到输出节点后,工作流会自动加一个步骤,把它们写进保存的录制,运行视图里显示为「合并特征」。

下一步

  • 切分算法
  • 接口参考
上一页

变换

下一页

切分