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 domain | Method | Status | Description |
motion | do_preset() play uploaded motion | ✅ live | Play an uploaded custom motion package (workflow) |
do_preset_uploaded() upload & play | 🟡 partial | Upload + play in one step — ⚠️ experimental: the multipart upload field name is not yet confirmed on a real robot |
stop() | ✅ live | Stop |
stand() / damping() / cmd_vel() | ⏳ planned | Basic posture / velocity control (opening next stage) |
state | battery() / status() | 🟡 partial | Battery / 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() | ✅ live | Whole-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) |
vision | frame() | 🟡 partial | Frame-grab interface maturing |
audio | say() / play_wav() | ⏳ planned | TTS / playback |
display | — | ⏳ planned | Face-screen expressions |
state.pose / navigation | — | ❌ | No SLAM pose on this platform |
arm | — | ❌ | Arm control not exposed separately (via whole-body motion packages) |
| 能力域 | 方法 | 状态 | 说明 |
motion | do_preset() 播放上传动作 | ✅ live | 播放已上传的自定义动作作品(工作流) |
do_preset_uploaded() 上传并播放 | 🟡 部分 | 一步完成上传 + 播放 —— ⚠️ 实验性:上传 multipart 字段名尚未真机确认 |
stop() | ✅ live | 停止 |
stand() / damping() / cmd_vel() | ⏳ 规划 | 基础姿态 / 速度控制(下一阶段开放) |
state | battery() / 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 类型支持环境) |
vision | frame() | 🟡 部分 | 取帧接口完善中 |
audio | say() / 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().
target 用 X2-<序列号> 形式。连接时 SDK 会自动探测动作通道
可达性,并把结果反映在 diagnose() 里。
Environment variables环境变量
| Variable | Default | Description |
FF_SDK_X2_MOTION_PROXY_URL | auto-probe | Full URL override for the motion channel (e.g. http://<robot-ip>:<port>, use across subnets) |
FF_SDK_DRY_RUN | off | Set 1 for dry-run mode |
FF_SDK_TRANSPORT_TIMEOUT | 5.0 | Per-operation timeout (seconds) |
FF_SDK_LOG_DIR | /var/log/ff_sdk | Log directory |
| 变量 | 默认 | 说明 |
FF_SDK_X2_MOTION_PROXY_URL | 自动探测 | 动作通道完整 URL 覆盖(如 http://<robot-ip>:<port>,跨网段时使用) |
FF_SDK_DRY_RUN | 关 | 设 1 干跑模式 |
FF_SDK_TRANSPORT_TIMEOUT | 5.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 与生命周期
| Member | Type | Description |
session.motion / .state / … | Capability accessor | Raises CapabilityNotSupported when unsupported |
session.capabilities() | set[str] | Set of supported capability domains |
session.diagnose() | DiagnosticReport | Health 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
| Parameter | Description |
name | Must 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_type | Empty = 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 的动作生态分三步:
| Step | Who does it | Output |
| 1 · Create | Motion studio (web upload video / music → training) | Motion package (action + soundtrack + expressions) |
| 2 · Upload | SDK do_preset_uploaded() or direct upload from the studio | Adds <key>_<version> to the robot's local package library — ⚠️ experimental: the multipart upload field name is not yet confirmed on a real robot |
| 3 · Play | SDK 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_v1、kungfu_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状态遥测
| Method | Status | Description |
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() | ✅ | BatteryState:percent(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能力路线图
| Capability | Current | Plan |
| 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 / expressions | ⏳ | Opening 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异常与诊断
| Exception | When it's raised |
FfSdkError | Root class of all SDK exceptions |
ConfigError | Invalid config |
ConnectionError / TransportError / TimeoutError | Connection / transport / timeout |
PlatformError | Low-level error translated by the platform adapter layer |
CapabilityNotSupported | Capability not open / package name invalid (the error message explains why) |
EStopActiveError / StateError | E-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示例索引
| Example | Content |
01_hello_connect.py | First connection + diagnostics + emergency stop |
02_diagnose.py | Health report explained |
03_estop.py | Emergency stop + callback + reset |
cookbook/context_manager.py | async with context management |
cookbook/graceful_shutdown.py | Graceful shutdown |
cookbook/safety_watchdog.py | Safety watchdog |
cookbook/diagnose_report.py | Diagnostic report export |
| 示例 | 内容 |
01_hello_connect.py | 第一次连接 + 诊断 + 紧急停止 |
02_diagnose.py | 体检报告详解 |
03_estop.py | 紧急停止 + 回调 + 重置 |
cookbook/context_manager.py | async with 上下文管理 |
cookbook/graceful_shutdown.py | 优雅关闭 |
cookbook/safety_watchdog.py | 安全看门狗 |
cookbook/diagnose_report.py | 诊断报告导出 |
14FAQ / TroubleshootingFAQ / 排错
| Symptom | Possible cause | Fix |
do_preset("wave") rejected | Built-in action catalog not open yet (see roadmap) | Use an uploaded package for now (<key>_<version>), or skills.wave() |
| Package playback returns failure | Package name misspelled / that version not uploaded | Check <key>_<version>; re-upload with do_preset_uploaded() |
| Connected but actions don't respond | Motion channel unreachable (across subnets) | Check diagnose(); set FF_SDK_X2_MOTION_PROXY_URL to point at the robot |
state.pose() errors | No SLAM on this platform | Expected 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 |