FF SDKMaster Docs v0.1.0a2
Humanoid Platform
人形平台

Master Humanoid Robot SDK

Master 人形机器人 SDK

Master (product codename X2) is FF's compact humanoid robot. This release centers on the motion package ecosystem: upload custom motion packages built in the motion studio to the robot and play them (dance / performance actions), paired with full battery and state telemetry. Basic posture and velocity control are opening up progressively on the roadmap.

Master(产品代号 X2)是 FF 紧凑型人形机器人。当前版本主打动作作品生态: 把动作工作室制作的自定义动作包上传到机器人并播放(舞蹈 / 表演动作), 配合电量、状态全套遥测。基础姿态与速度控制在路线图中逐步开放。

target: X2-<sn> Python ≥ 3.10 aarch64 / x86_64 Motion package ecosystem 动作作品生态

01Overview & Capability Matrix概览与能力矩阵

Capability domainMethodStatusDescription
motiondo_preset() play uploaded motion✅ livePlay an uploaded custom motion package (workflow)
do_preset_uploaded() upload & play🟡 partialUpload + play in one step — ⚠️ experimental: the multipart upload field name is not yet confirmed on a real robot
stop()✅ liveStop
stand() / damping() / cmd_vel()⏳ plannedBasic posture / velocity control (opening next stage)
statebattery() / status()🟡 partialBattery / state machine — ⚠️ battery BMS protobuf decode not yet implemented, currently returns a placeholder 0%; status data stream hz=0, verified from a single frame only
joint_states() / get_imu()✅ liveWhole-body joint telemetry — 5 segments aggregated (~31 DoF: name/position/velocity/effort) + torso IMU, via ROS2 (real-machine verified 2026-06-28; needs the ROS2 message typesupport env)
visionframe()🟡 partialFrame-grab interface maturing
audiosay() / play_wav()⏳ plannedTTS / playback
display⏳ plannedFace-screen expressions
state.pose / navigationNo SLAM pose on this platform
armArm control not exposed separately (via whole-body motion packages)
能力域方法状态说明
motiondo_preset() 播放上传动作✅ live播放已上传的自定义动作作品(工作流
do_preset_uploaded() 上传并播放🟡 部分一步完成上传 + 播放 —— ⚠️ 实验性:上传 multipart 字段名尚未真机确认
stop()✅ live停止
stand() / damping() / cmd_vel()⏳ 规划基础姿态 / 速度控制(下一阶段开放)
statebattery() / status()🟡 部分电量 / 状态机 —— ⚠️ battery BMS protobuf 解码待补、当前返回占位 0;status 数据流 hz=0 仅单帧验证
joint_states() / get_imu()✅ live全身关节遥测 —— 5 段聚合(~31 自由度:name/position/velocity/effort)+ 躯干 IMU,经 ROS2(2026-06-28 真机验证;需 ROS2 类型支持环境)
visionframe()🟡 部分取帧接口完善中
audiosay() / play_wav()⏳ 规划TTS / 播放
display⏳ 规划面屏表情
state.pose / navigation本平台无 SLAM 位姿
arm不单独暴露臂控(走全身动作作品)
Where Master fits Master's strongest capability in this release is whole-body motion package playback — paired with the motion-studio ecosystem (video → motion training → package), developers can make the robot perform any self-made dance / action, not just call fixed preset actions.
Master 的定位 Master 当前版本最强的能力是全身动作作品播放——配合动作工作室 (视频 → 动作训练 → 作品包)生态,开发者可以让机器人表演任意自制舞蹈 / 动作, 而不只是调用固定预置动作。

02Installation安装

shell — install
# On the robot (aarch64)
pip install wheels/ff_sdk-0.1.0a2-cp310-cp310-linux_aarch64.whl

# On a Linux dev machine (x86_64)
pip install wheels/ff_sdk-0.1.0a2-cp310-cp310-linux_x86_64.whl

python -c "import ff_sdk; print(ff_sdk.__version__)"
shell — 安装
# 在机器人上(aarch64)
pip install wheels/ff_sdk-0.1.0a2-cp310-cp310-linux_aarch64.whl

# 在 Linux 开发机上(x86_64)
pip install wheels/ff_sdk-0.1.0a2-cp310-cp310-linux_x86_64.whl

python -c "import ff_sdk; print(ff_sdk.__version__)"

03Quickstart快速开始

hello_master.py
import asyncio, ff_sdk

async def main():
    async with await ff_sdk.connect("X2-DEMO") as robot:
        print(robot.diagnose())                  # health check: is the motion channel online
        print(robot.capabilities())

        battery = await robot.state.battery()
        print(f"battery {battery.percent:.0%}")   # ⚠️ currently returns placeholder 0 — BMS decode pending on real robot

        status = await robot.state.status()
        print(f"status {status}")

        # play a previously uploaded motion package (name = <key>_<version>)
        res = await robot.motion.do_preset("mydance_v1")
        print(res)

asyncio.run(main())
hello_master.py
import asyncio, ff_sdk

async def main():
    async with await ff_sdk.connect("X2-DEMO") as robot:
        print(robot.diagnose())                  # 体检:动作通道是否在线
        print(robot.capabilities())

        battery = await robot.state.battery()
        print(f"电量 {battery.percent:.0%}")   # ⚠️ 当前返回占位 0 —— 真机 BMS 解码待补

        status = await robot.state.status()
        print(f"状态 {status}")

        # 播放一个之前上传过的动作作品(名字 = <key>_<version>)
        res = await robot.motion.do_preset("mydance_v1")
        print(res)

asyncio.run(main())
Safety notice Before playing a whole-body motion package, make sure the robot is standing on level ground with 2m of clear space all around. When a package's range of motion is unknown, validate on a slow / small-amplitude version first. Do not cut power during playback.
安全须知 播放全身动作作品前确认机器人站立在四周 2m 空旷的平整地面, 动作包幅度未知时先在低速 / 小幅版本上验证。播放过程不要打断供电。

04Connect & Config连接与配置

connect()

async ff_sdk.connect(target: str, *, config: Config | None = None, identity: Identity | None = None) -> Session

target uses the X2-<serial> form. On connect, the SDK auto-probes motion-channel reachability and reflects the result in diagnose().

targetX2-<序列号> 形式。连接时 SDK 会自动探测动作通道 可达性,并把结果反映在 diagnose() 里。

Environment variables环境变量

VariableDefaultDescription
FF_SDK_X2_MOTION_PROXY_URLauto-probeFull URL override for the motion channel (e.g. http://<robot-ip>:<port>, use across subnets)
FF_SDK_DRY_RUNoffSet 1 for dry-run mode
FF_SDK_TRANSPORT_TIMEOUT5.0Per-operation timeout (seconds)
FF_SDK_LOG_DIR/var/log/ff_sdkLog directory
变量默认说明
FF_SDK_X2_MOTION_PROXY_URL自动探测动作通道完整 URL 覆盖(如 http://<robot-ip>:<port>,跨网段时使用)
FF_SDK_DRY_RUN1 干跑模式
FF_SDK_TRANSPORT_TIMEOUT5.0单次操作超时(秒)
FF_SDK_LOG_DIR/var/log/ff_sdk日志目录
python — explicitly specify the motion channel
from ff_sdk import Config

cfg = Config.from_env()
cfg.extra["x2_motion_proxy_url"] = "http://192.168.1.50:5000"
robot = await ff_sdk.connect("X2-DEMO", config=cfg)
python — 显式指定动作通道
from ff_sdk import Config

cfg = Config.from_env()
cfg.extra["x2_motion_proxy_url"] = "http://192.168.1.50:5000"
robot = await ff_sdk.connect("X2-DEMO", config=cfg)
Where the program runs Recommended on the same LAN as the robot (or deployed directly on the robot). In dry-run mode it runs on any computer, and all motion calls return placeholder results.
程序跑在哪 推荐与机器人同一局域网(或直接部署到机器人上)。dry-run 模式下任意电脑可运行, 所有动作调用返回占位结果。

05Session & LifecycleSession 与生命周期

MemberTypeDescription
session.motion / .state / …Capability accessorRaises CapabilityNotSupported when unsupported
session.capabilities()set[str]Set of supported capability domains
session.diagnose()DiagnosticReportHealth check (motion-channel reachability, etc.)
await session.e_stop(reason)Emergency stop
await session.close()Disconnect
成员类型说明
session.motion / .state / …能力访问器不支持时 raise CapabilityNotSupported
session.capabilities()set[str]支持的能力域集合
session.diagnose()DiagnosticReport健康体检(动作通道可达性等)
await session.e_stop(reason)紧急停止
await session.close()断开连接
python
async with await ff_sdk.connect("X2-DEMO") as robot:
    ...
# auto close()
python
async with await ff_sdk.connect("X2-DEMO") as robot:
    ...
# 自动 close()

06motion · Motion Control运动控制

do_preset() — play an uploaded motion package—— 播放已上传的动作作品

async motion.do_preset(name: str, motion_type: str = "") -> MotionResult
ParameterDescription
nameMust be an uploaded package name in <key>_<version> form (e.g. mydance_v1). Names without an underscore version suffix are rejected — those belong to the built-in action catalog (see roadmap).
motion_typeEmpty = imitation-type action; non-empty = foundation-type action. Fill in per the type the package was made with.
参数说明
name必须是 <key>_<version> 形式的已上传作品名(如 mydance_v1)。 没有下划线版本后缀的名字会被拒绝 —— 那是内置动作目录的命名(见 路线图)。
motion_type留空 = 模仿型动作;非空 = 基座型动作。按作品制作时的类型填。

do_preset_uploaded() — upload + play in one step—— 上传 + 播放一步到位

async motion.do_preset_uploaded(key: str, version: str, file_path: str, motion_type: str = "foundation") -> MotionResult

Upload a local motion file to the robot and play it immediately. key must not contain underscores (the underscore separates key and version).

把本地动作文件上传到机器人并立即播放。key 不要含下划线 (下划线是 key 与 version 的分隔符)。

python — full motion playback flow
# Path A: package uploaded before → play directly by name
await robot.motion.do_preset("mydance_v1")

# Path B: new package → upload and play
# ⚠️ experimental: multipart upload field name not yet confirmed on a real robot
await robot.motion.do_preset_uploaded(
    key="mydance", version="v2",
    file_path="./works/mydance_v2.bin",
    motion_type="foundation",
)

# stop
await robot.motion.stop()
python — 完整动作播放流程
# 路线 A:作品之前传过 → 直接按名字播
await robot.motion.do_preset("mydance_v1")

# 路线 B:新作品 → 上传并播放
# ⚠️ 实验性:上传 multipart 字段名尚未真机确认
await robot.motion.do_preset_uploaded(
    key="mydance", version="v2",
    file_path="./works/mydance_v2.bin",
    motion_type="foundation",
)

# 停止
await robot.motion.stop()
Where do motion packages come from? Motion packages are produced by the motion-studio ecosystem: upload a dance video → cloud training → export a robot package. The SDK handles the last step, “package → robot”. See Motion Package Workflow.
动作作品从哪来? 动作作品由动作工作室生态产出:上传一段舞蹈视频 → 云端训练 → 导出机器人作品包。 SDK 负责“作品包 → 机器人”的最后一步。详见 动作作品工作流

07Motion Package Workflow ★动作作品工作流 ★

Master's motion ecosystem has three steps:

Master 的动作生态分三步:

StepWho does itOutput
1 · CreateMotion studio (web upload video / music → training)Motion package (action + soundtrack + expressions)
2 · UploadSDK do_preset_uploaded() or direct upload from the studioAdds <key>_<version> to the robot's local package library — ⚠️ experimental: the multipart upload field name is not yet confirmed on a real robot
3 · PlaySDK do_preset("<key>_<version>")Robot performs the whole-body action (with synced soundtrack / expressions)
步骤谁来做产物
1 · 制作动作工作室(网页上传视频 / 音乐 → 训练)动作作品包(动作 + 配乐 + 表情)
2 · 上传SDK do_preset_uploaded() 或工作室直传机器人本地作品库新增 <key>_<version> —— ⚠️ 实验性:上传 multipart 字段名尚未真机确认
3 · 播放SDK do_preset("<key>_<version>")机器人执行全身动作(含同步配乐 / 表情)

Naming rules命名规则

  • Package name = <key>_<version>, e.g. mydance_v1, kungfu_v3
  • Do not use underscores inside key (the underscore is reserved as a separator)
  • Multiple versions can coexist under the same key; choose which to play by version number
  • 作品名 = <key>_<version>,例如 mydance_v1kungfu_v3
  • key 内不要用下划线(下划线保留为分隔符)
  • 同 key 多版本并存,按版本号选择播放
python — package version management example
import ff_sdk
from ff_sdk.core.exceptions import CapabilityNotSupported

async def play_latest(robot, key: str, versions: list[str]):
    """Try playing from newest to oldest version; the first success wins."""
    for ver in versions:
        try:
            res = await robot.motion.do_preset(f"{key}_{ver}")
            if res.success:
                return res
        except CapabilityNotSupported:
            continue
    raise RuntimeError(f"package {key} has no playable version")
python — 作品版本管理示例
import ff_sdk
from ff_sdk.core.exceptions import CapabilityNotSupported

async def play_latest(robot, key: str, versions: list[str]):
    """按版本号从新到旧尝试播放,第一个成功的为准。"""
    for ver in versions:
        try:
            res = await robot.motion.do_preset(f"{key}_{ver}")
            if res.success:
                return res
        except CapabilityNotSupported:
            continue
    raise RuntimeError(f"作品 {key} 没有可播放的版本")

08state · State Telemetry状态遥测

MethodStatusDescription
await state.battery()BatteryState: percent(0–1) / voltage / is_charging
await state.status()RobotStatus state machine
await state.joint_states()Joint telemetry (planned)
await state.pose()No SLAM on this platform, raises CapabilityNotSupported
方法状态说明
await state.battery()BatteryStatepercent(0–1) / voltage / is_charging
await state.status()RobotStatus 状态机
await state.joint_states()关节遥测(规划中)
await state.pose()本平台无 SLAM,raise CapabilityNotSupported
python — check battery before playback
battery = await robot.state.battery()
if battery.percent < 0.3:
    print("Battery below 30%; charge before whole-body actions")
else:
    await robot.motion.do_preset("mydance_v1")
python — 播放前检查电量
battery = await robot.state.battery()
if battery.percent < 0.3:
    print("电量低于 30%,全身动作建议先充电")
else:
    await robot.motion.do_preset("mydance_v1")

09Other Capabilities其他能力

vision (🟡 partial)(🟡 部分)

async vision.frame(source: str = "default") -> CameraFrame

The frame-grab interface is maturing; availability follows what capabilities() returns at runtime.

取帧接口完善中,可用性以 capabilities() 运行时返回为准。

audio / display (⏳ planned)(⏳ 规划)

TTS announcement and face-screen expression interfaces are defined and will open in later releases. Calling them now raise CapabilityNotSupported. Note: a motion package's built-in soundtrack and expressions are unaffected — they play on the robot itself along with the package and don't need the audio/display capability.

TTS 播报与面屏表情接口已定义,将在后续版本开放。当前调用会 raise CapabilityNotSupported。注意:动作作品包自带的配乐与表情不受影响 —— 它们随作品在机器人本体播放,不需要 audio/display 能力。

10Cross-platform Skills跨平台 Skills

python
from ff_sdk import skills

# skills auto-selects an available implementation path on Master,
# returning {"ok": False, "message": ...} instead of crashing when unavailable
result = await skills.wave(robot)
print(result)   # {"ok": ..., "platform": "x2", ...}
python
from ff_sdk import skills

# skills 在 Master 上自动选择可用的实现路径,
# 不可用时返回 {"ok": False, "message": ...} 而不是崩溃
result = await skills.wave(robot)
print(result)   # {"ok": ..., "platform": "x2", ...}

Prefer skills when writing cross-robot code — the same line runs on Aegis / Futurist / Navi.

写跨机器人代码时优先用 skills —— 同一行代码在 Aegis / Futurist / Navi 上都能跑。

11Capability Roadmap能力路线图

CapabilityCurrentPlan
Uploaded motion package playback✅ live
Built-in action catalog (wave / bow / salute, etc.)Opening next stage via the realtime motion channel for direct do_preset("wave") calls
stand() / damping() / cmd_vel()Opening with the realtime control channel
Joint telemetry / audio / expressionsOpening progressively
能力当前计划
上传动作作品播放✅ live
内置动作目录(招手 / 鞠躬 / 敬礼等)下一阶段经实时动作通道开放 do_preset("wave") 直调
stand() / damping() / cmd_vel()随实时控制通道开放
关节遥测 / 音频 / 表情逐步开放
Why can't some preset actions be called yet? This release's motion channel only serves user-uploaded custom packages. The built-in action catalog runs on a separate realtime control path and will open in a later SDK release — until then, calling it returns an explanatory CapabilityNotSupported rather than failing silently.
为什么有的预置动作现在调不了? 当前版本的动作通道只服务用户上传的自定义作品。内置动作目录走另一条 实时控制链路,将在后续 SDK 版本开放 —— 在那之前调用会得到带说明的 CapabilityNotSupported,不会静默失败。

12Exceptions & Diagnostics异常与诊断

ExceptionWhen it's raised
FfSdkErrorRoot class of all SDK exceptions
ConfigErrorInvalid config
ConnectionError / TransportError / TimeoutErrorConnection / transport / timeout
PlatformErrorLow-level error translated by the platform adapter layer
CapabilityNotSupportedCapability not open / package name invalid (the error message explains why)
EStopActiveError / StateErrorE-stop active / state not permitted
异常什么时候抛
FfSdkError所有 SDK 异常的根类
ConfigError配置无效
ConnectionError / TransportError / TimeoutError连接 / 线路 / 超时
PlatformError平台适配层翻译的底层错误
CapabilityNotSupported能力未开放 / 作品名不合规(错误信息会说明原因)
EStopActiveError / StateError急停激活 / 状态不允许
python — MotionResult check
res = await robot.motion.do_preset("mydance_v1")
if not res.success:
    print(f"playback failed: {res.message}")
    print(res.details)        # server-returned details, for troubleshooting
python — MotionResult 检查
res = await robot.motion.do_preset("mydance_v1")
if not res.success:
    print(f"播放失败: {res.message}")
    print(res.details)        # 服务端返回的细节,排错用

13Examples Index示例索引

ExampleContent
01_hello_connect.pyFirst connection + diagnostics + emergency stop
02_diagnose.pyHealth report explained
03_estop.pyEmergency stop + callback + reset
cookbook/context_manager.pyasync with context management
cookbook/graceful_shutdown.pyGraceful shutdown
cookbook/safety_watchdog.pySafety watchdog
cookbook/diagnose_report.pyDiagnostic report export
示例内容
01_hello_connect.py第一次连接 + 诊断 + 紧急停止
02_diagnose.py体检报告详解
03_estop.py紧急停止 + 回调 + 重置
cookbook/context_manager.pyasync with 上下文管理
cookbook/graceful_shutdown.py优雅关闭
cookbook/safety_watchdog.py安全看门狗
cookbook/diagnose_report.py诊断报告导出

14FAQ / TroubleshootingFAQ / 排错

SymptomPossible causeFix
do_preset("wave") rejectedBuilt-in action catalog not open yet (see roadmap)Use an uploaded package for now (<key>_<version>), or skills.wave()
Package playback returns failurePackage name misspelled / that version not uploadedCheck <key>_<version>; re-upload with do_preset_uploaded()
Connected but actions don't respondMotion channel unreachable (across subnets)Check diagnose(); set FF_SDK_X2_MOTION_PROXY_URL to point at the robot
state.pose() errorsNo SLAM on this platformExpected behavior, catch CapabilityNotSupported
现象可能原因解决
do_preset("wave") 被拒绝内置动作目录尚未开放(见路线图当前用上传作品(<key>_<version>),或用 skills.wave()
作品播放返回失败作品名拼错 / 该版本未上传核对 <key>_<version>;用 do_preset_uploaded() 重传
连接成功但动作不响应动作通道不可达(跨网段)diagnose();设 FF_SDK_X2_MOTION_PROXY_URL 指向机器人
state.pose() 报错本平台无 SLAM预期行为,捕获 CapabilityNotSupported