安装

运动学求解器不是 FastSim 的基础依赖。包索引已经提供这些发行包时,可以安装:

bash
python -m pip install 'fastsim[kinematics]'

从源码安装时,先安装 FastSim-Plugins 中的 SPI 和 Portable Provider,再安装 FastSim:

bash
python -m pip install ./FastSim-Plugins/packages/fastsim-kinematics-solver-spi
python -m pip install ./FastSim-Plugins/packages/fastsim-kinematics-solver-portable
python -m pip install ./FastSim

没有安装这些可选包时,import fastsim 和普通 Run 仍能正常工作。只有第一次查询 Descriptor 或调用求解时,才会返回带安装提示的 KinematicsSolveErrorCode.SERVICE_UNAVAILABLE

准备机器人组件

FastSim 只能从不可变 ExecutionPlan 中已经编译的机器人实体选择模型,调用方不能传入 主机文件路径。组件需要包含一个 URDF 资源。存在 role: kinematics 时优先选它;否则 组件内必须恰好只有一个 URDF 资源。

因此,Isaac Lab 组件可以用 USD 运行仿真,同时用同一机器人的 URDF 做可移植求解:

yaml
schema: fastsim-component/1
id: robot://example/arm
version: 1.0.0
kind: robot

semantics:
  joints: [shoulder, elbow]
  joint_units:
    shoulder: rad
    elbow: rad
  groups:
    arm: [shoulder, elbow]
  frames: [base, tool]

variants:
  isaaclab:
    resources:
      simulation:
        uri: arm.usd
        format: model/vnd.usd
        role: simulation
      kinematics:
        uri: arm.urdf
        format: model/vnd.urdf+xml
        role: kinematics

第一次使用时,Core 会打开 ExecutionPlan 指定的精确文件,不跟随符号链接;随后检查 它是大小受限的普通文件,并核对锁定的 SHA-256,最后才把 URDF 文本交给 Provider。 公开请求中没有文件路径或仿真器原生句柄。

初学者 API

python
import asyncio

import fastsim


async def main() -> None:
    async with fastsim.app("run.yaml") as simulation:
        result = await simulation.kinematics.solve(
            robot="arm",              # Scenario 别名或完整 robots.* ID
            base_frame="base",
            tip_frame="tool",
            xyz_m=(0.55, 0.10, 0.42),
            quat_xyzw=(0.0, 1.0, 0.0, 0.0),
            group="arm",              # 多 Group 机器人建议明确填写
            timeout_s=1.0,
        )

        if result.status.value != "success":
            print(result.status.value, result.failure_code, result.message)
            return

        joints = result.solutions[0].joints
        operation = await simulation.control.submit_joints(
            actor="arm",
            group="arm",
            path=[joints.positions],
            dt=1.0 / 60.0,
            timeout=10.0,
        )
        print((await operation.result()).status)
        await operation.release()


asyncio.run(main())

不填写 start 时,Core 会从当前 Run Generation 最新一次已提交 WorldState 中原子 读取当前关节位置。关节 ID、顺序和逐轴单位来自已编译组件语义。双臂或移动操作机器人 应填写 group,使当前状态与指定 URDF 运动链精确一致。

默认 Provider 提供确定性的可移植 URDF 运动学。首版只支持 collision_mode="none"。请求世界或自碰撞时会返回明确的 unsupported 响应, FastSim 不会静默降级为无碰撞求解。

高级 API

高级用户可以从 fastsim.api.kinematics 按需导入不可变类型,并提交完整 IKRequest

python
from fastsim.api.kinematics import IKRequest, Pose

state = await simulation.state()
request = IKRequest(
    request_id="layout-check-0042",
    run_id=simulation.run_id,
    generation=state.world.generation,
    robot_entity_id="robots.arm",
    base_frame_id="base",
    tip_frame_id="tool",
    target=Pose("base", (0.55, 0.10, 0.42), (0.0, 1.0, 0.0, 0.0)),
    start=None,
    timeout_s=1.0,
)
result = await simulation.kinematics.solve_request(request)

同一个 Generation 内,request_id 具有幂等语义。完全相同的请求会返回缓存的不可变 响应;用同一个 ID 提交不同输入会被拒绝。

创建 Application 时可以显式选择其他已安装 Provider:

python
simulation = fastsim.app(
    "run.yaml",
    kinematics_solver="kinematics-solver://vendor/gpu-ik",
    kinematics_config={"device": "cuda:0"},
)

Provider 发现要求 fastsim.kinematics_solvers 下恰好有一个匹配的 Entry Point,并在 使用前核对 Distribution、版本、Entry Point 名称和值、服务 API 与 Descriptor 标识。

生命周期与错误

只有显式查询 Descriptor 或调用求解时,才开始 Solver 发现、模型读取、Executor 创建 和 Provider 工作。未使用时不导入 Solver 包、不创建线程,也不注册 Tick Hook。

每个请求在 Provider 工作前与结果发布前各检查一次 Generation。Reset 会取消旧 Generation 的在途任务;即使 Provider 之后返回,旧解也会被丢弃。Provider 在 Application Owner Loop 外执行。超时与调用方取消会设置线程安全取消标志;不配合取消的 工作也只能留在有界 Orphan 配额中。

Core 边界错误统一抛出 KinematicsSolveError,其中包含稳定 code 以及请求、Run、 Generation 和实体上下文。目标不可达、关节限位不可行、不支持碰撞模式或不收敛等正常 数学结果仍是有类型的 IKSolveResponse,不是异常崩溃。

常见 Core 错误码如下:

错误码 含义
service_unavailable 可选 SPI 缺失、不兼容、重复或无效
solver_not_found 没有安装显式选择的 Provider
entity_not_found / entity_not_robot 编译实体不存在或不是机器人
model_unavailable / model_unsupported 没有唯一且校验通过的 URDF,或 Provider 拒绝模型
state_unavailable 无法取得新鲜、有序的当前关节状态
stale_generation Reset 或 Run 替换使请求失效
request_conflict 同一个请求 ID 被用于不同输入
capacity_exhausted 有界 Active/Orphan 配额已满
timeout / cancelled Core 截止时间或调用方取消结束了请求
provider_failure Provider 在正常响应契约之外失败或违反契约