Skip to content

YAML Reference

konodoki edited this page Aug 2, 2026 · 26 revisions

YAML 字段参考

项目存在三类 YAML:遥控器映射、状态机系统配置和 Mod 清单。

1. 遥控器 YAML

主文件:

src/remote_controller/config/xbox_default.yaml

顶层结构:

inputs:
  selection: {}
sources: {}
curves: {}
controls: {}
outputs: {}
system: {}
system_mutexes: {}
system_reset_motion_after: []

inputs.selection

字段 含义
scan_interval_ms 扫描候选设备的周期
promote_stable_ms 高优先级设备持续可用多久后允许抢占

sources

每个 source group 声明一个候选设备:

sources:
  gamepad:
    type: joystick
    device: /dev/input/jsBattleDragon
    priority: 50
    ready_timeout_ms: 1000
    loss_timeout_ms: 300
    cooldown_ms: 1000
    signals:
      gamepad.left_y:
        from: js.axis.3

内置 type 包含 joystickkeyboardcrsf。自定义类型需注册输入驱动工厂。

通用字段:typepriorityready_timeout_msloss_timeout_mscooldown_mssignals。设备专有字段由对应 driver 解析。

curves

curves:
  stick:
    type: expo
    deadzone: 0.03
    expo: 0.0
    limit: [-1.0, 1.0]
    calibration:
      input: [-1.0, 0.0, 1.0]
      output: [-1.0, 0.0, 1.0]

controls

支持 analogboolenum。control 把多个设备输入统一成业务控制量,输入规则可带 sourcedirectioncurvewhenvalue 等字段。条件支持 pressedreleasedequals、范围以及 all/any 组合。

完整语法和实例见遥控器输入映射配置

outputs

outputs:
  conflict_policy: first_wins
  publish_on_change: true
  analog:
    vx: move.vx
  edge:
    - output: btn_10=11
      when:
        any:
          - - keyboard.wave_event
          - - gamepad.a_event

conflict_policy 可为 first_winslast_winserrorpublish_on_change 控制是否仅在消息变化时发布。analog 连续写值,level 保持条件结果,edge 只在进入条件时产生脉冲;最终字段写入 MotionCommands。 示例值 11 是客户扩展示意,不是默认绑定;实际使用前需与所有已启用 Mod 的事件一起 检查,避免同一来源状态下出现相同槽位和值的多个目标。

2. 状态机系统 YAML

主文件:

src/bxi_example_py_elf3/config/elf3_state_machine.yaml
字段 必需 含义
initial_state 完整初始状态名
mod_paths 额外 Mod 搜索根列表
graph.validate 是否验证状态图
graph.export.dot DOT 导出路径
graph.export.mermaid Mermaid 导出路径
default_transition 未指定边的默认过渡
transition_profiles 系统共享过渡 profile
logging Framework、Mod、State、Node 日志等级和子进程输出限制
control_runtime 控制周期、计算预算、维护频率、绑核和实时优先级

statesremote_eventsspeed_profiles 由 Mod 合成,不应写入此文件。

control_runtime 的完整字段和实时权限要求见框架控制调度。这里的 周期配置是所有平台适配器共享的运行时设置,不应复制进单个 Mod。

logging

logging:
  default_level: info
  levels:
    framework.scheduler: warning
    mod.com.bxi.normal_depth: debug
  subprocess:
    max_line_bytes: 16384
    max_lines_per_sec: 200

levels 按最长 scope 前缀匹配,可用等级为 debug/info/warning/error/fatal。 常用 scope 为 frameworkmod.<mod-id>mod.<mod-id>.state.<state>mod.<mod-id>.node.<node>。子进程每行字节上限和每秒行数上限由 subprocess 配置。详见日志系统

内置过渡类型

Transition 是源状态与目标状态之间的切换策略,决定切换期间怎样生成电机 qpos/kp/kd。它的主要作用是处理两边输出不连续的问题,避免直接换帧造成突跳;instant 表示明确选择不做中间处理。

  • instant
  • hold
  • entry_gain_ramp
  • running_blend
  • sequence

各字段见 自定义过渡双状态运行混合

3. mod.yaml

顶层字段

字段 必需 含义
schema 当前固定为 1
id 全局唯一、带命名空间的 Mod id
name 面向用户的非空 Mod 名称
version 数字点版本,如 1.0.0
api 兼容的框架 Mod API 版本范围,如 ">=4,<5"
enable 是否启用此 Mod,必须是布尔值
entrypoint 高级插件的 module:function;约定式加载显式写 null
visibility public 或发布工具识别的 protected
requires Mod 依赖及版本约束;没有依赖时写 []
conflicts 不能同时启用的 Mod id 列表;没有冲突时写 []
python_exports 暂时导出的顶层 Python 包;没有时写 []
runtime_requirements Python、ROS 和系统动态库依赖;三类均须显式填写
runtime_profiles 节点可选的 Host、Vendor 或 Portable 进程运行环境
events 遥控槽位到事件的映射
speed_profiles Mod 私有速度 profile
transition_profiles Mod 私有过渡 profile
states 本地状态声明;资源 Mod 可省略或使用 {}
routes 状态图中的状态切换边
actions 不切换状态的事件 action
nodes Mod 内置的 Python ROS 节点

描述 Mod 身份和装载行为的以上 12 个字段必须全部显式填写,加载器也会拒绝未知顶层字段。例如约定式公共 Mod 的头部为:

schema: 1
id: com.example.wave
name: 挥手示例
version: 1.0.0
api: ">=4,<5"
enable: true
entrypoint: null
visibility: public
requires: []
conflicts: []
python_exports: []
runtime_requirements:
  python: []
  ros: []
  system: []

requires

requires:
  - com.example.shared
  - id: com.bxi.basic_actions
    version: ">=1,<2"

apirequires[].version 使用相同的逗号分隔约束语法,支持 ==!=>=<=><。当前框架公开的 Mod API 版本是 4.0.0;加载器会在导入任何 Mod 代码前检查兼容性。

conflicts

conflicts:
  - com.example.legacy_wave

冲突声明只需由一方给出;如果两个 Mod 都处于启用状态,加载器会在执行任何 Mod 代码前拒绝启动。未安装或已禁用的冲突对象不影响加载。Mod 不能声明与自身冲突。

python_exports

python_exports:
  - customer_common

Mod 根目录必须存在 customer_common/__init__.py。导出名必须是合法且全局唯一的 Python 包名。

runtime_requirements

requires 只表示 Mod 之间的依赖;Python 模块、ROS 包和系统动态库写入 runtime_requirements

runtime_requirements:
  python:
    - import: numpy
  ros:
    - package: sensor_msgs
  system:
    - library: realsense2

当前版本不比较依赖版本,也不会自动执行 pipaptrosdep。Python 依赖会在隔离子进程中执行真实 import;ROS 包和系统动态库检查可发现性。检查顺序为当前平台 vendor 优先、跨平台纯 Python vendor 其次、宿主环境最后:

mods/com.example.camera/
  vendor/
    python/
      linux-x86_64-cpython-310/  # OS + CPU + Python ABI
      linux-aarch64-cpython-310/
      common/                    # 仅放跨平台纯 Python 包
    lib/
      linux-x86_64/              # OS + CPU
      linux-aarch64/

目录标签由运行时自动生成。不匹配当前 OS、CPU 或 Python ABI 的目录不会加入 PYTHONPATH/LD_LIBRARY_PATH。若只有其他平台的 vendor,框架会显式警告并尝试宿主依赖;当前平台 vendor 存在但真实 import 失败时会直接标记为 unavailable,不会静默回退并掩盖损坏的 bundle。旧的 vendor/python/<package>vendor/lib/<library> 平铺格式不会加载。

缺少顶层依赖时,整个 Mod 以 unavailable 状态发布且不执行其 Python entrypoint。其他不依赖它的 Mod 继续加载;启用的 Mod 若通过 requires 强依赖它,框架会用完整依赖链拒绝启动。

构建机应使用非 --symlink-install 的发布安装树。部署该 install/ 后,可以直接将完整 Mod 目录复制到 share/bxi_example_py_elf3/mods/,重启进程即可发现,无需在目标机器重新执行 colcon build。一个 Mod 可以同时携带多个目标目录;目标机只会选择匹配项。更新时应完整替换 Mod 目录,不能覆盖合并,否则旧平台文件可能残留。

普通非 symlink 安装会保留 vendor/ 内的相对符号链接,避免同一版本化动态库因 libname.so、SONAME 和完整版本名被展开成多份二进制。vendor bundle 应附带许可证、来源版本和目标 Python ABI/CPU 架构说明;com.bxi.normal_depth/vendor/README.md 是完整示例。

资源准备策略不属于 YAML。插件注册资源时使用 context.register_resource(..., policy="startup")policy="on_demand";这样资源 工厂与准备策略保持在同一处代码中。默认是 startup。使用 on_demand 的状态必须把 对应 handle 放入 RobotControlState(..., resources=(handle,)),由状态机在切换前异步准备。

runtime_profiles

runtime_profiles 是可选能力;没有该字段的 Mod 继续使用宿主环境。Profile 可写成单个 candidate,或显式给出有序 fallback:

runtime_profiles:
  host_only:
    mode: host

  bundled_sdk:
    mode: vendor

  portable_sdk:
    candidates:
      - mode: portable
        root: runtime/{platform}
        python: python/bin/python3
        executable_paths: [bin, python/bin]
        library_paths: [lib, python/lib]
        isolated: true
      - mode: host
  • host 不要求 Mod 携带环境,由用户自行安装 pip/apt/ROS 依赖。
  • vendor 使用现有 vendor/python/<平台-Python ABI>vendor/python/commonvendor/lib/<平台>
  • portable 从 Mod 内相对 root 原地运行;python 可选,C++ 等原生进程可以只使用 executable_pathslibrary_paths
  • {platform}{python_tag} 由框架展开。绝对路径、.. 和逃出 Mod/runtime 根的 符号链接会被拒绝。
  • Portable 默认 isolated: true,且只允许 execution: process
  • fallback 只跳过完全不存在的 candidate;已存在但损坏的 Portable 环境不会降级。

节点通过 runtime_profile: portable_sdk 引用。command 脚本可使用 interpreter: bundled-python 直接选择 Portable profile 声明的解释器。节点状态快照会 发布 runtime_profileruntime_mode 和实际 runtime_root,方便确认最终选择。

events

events:
  activate:
    slot: btn_10
    value: 11
  any_change:
    slot: btn_9

slot 必须是合法标识符,value 若存在必须是整数。事件必须至少被一条 route 或 action 使用。

states

states:
  wave:
    factory: state:WaveState
    id: 12345
    label: 示例
    priority: 100
    group: Customer
    icon: waves
    confirm: true
    confirm_message: 请确认安全
    speed_profile: walk
    params:
      amplitude: 0.4

factory: module:Class 用于 entrypoint: null 的约定加载。状态 id 是可选 int32。priority 是界面顺序优先级,必须是整数,数值越大越靠前,默认 0;相同 priority 按完整状态名升序排列。加载器据此自动生成非负、唯一的 manifest.index

index 仍可作为高级固定位置选项显式配置;两个显式 index 重复会报错退出。label/priority/index/group/icon/confirm/confirm_message 均可直接写,也可放在 manifest 中,但不能同时给出冲突值。params 的类型和允许字段由类的 dataclass Params 或显式插件工厂决定。

routes

routes:
  - from: com.bxi.basic_actions/normal
    event: activate
    to: wave
    transition: soft_switch

  - from: wave
    event: stop
    to: com.bxi.basic_actions/zero_torque
    transition:
      profile: safe_exit
      duration: 0.5
    delay: 0.2

字段:fromeventtotransitiondelay。每条 route 都必须包含 to,不能包含 action

actions

actions:
  - from: wave
    event: toggle_pause
    action: toggle_pause
    manifest:
      label: 暂停/继续

字段:fromeventactionmanifestmanifest.label 是必需的非空字符串;action 只调用当前状态的 action handler,不发生状态切换,也不能配置 to/transition/delay

加载器会规范化 from/event,并把 action 元数据发布到 state_machine_info.graph.actions[]。与状态快照相同,manifest 字段会在发布对象中展开,例如上例发布为 {from: ..., event: ..., action: toggle_pause, label: 暂停/继续}

nodes

nodes:
  detector:
    entrypoint: detector_node:create_node
    execution: process
    scheduling:
      cpu_affinity: shared
    lifecycle: state
    states:
      - avoidance
    manifest:
      label: 障碍物检测器
    runtime_requirements:
      python:
        - import: onnxruntime
      ros:
        - package: sensor_msgs
      system: []
    params:
      output_topic: /perception/obstacles
    restart:
      max_attempts: 3
      delay: 1.0
      non_retryable_exit_codes: [78]

entrypoint 必须指向 Mod 目录中的 Python module:function。工厂接收公共 API NodeBuildContext 并返回 rclpy.node.Node

from bxi_example_py_elf3.framework.mod_api import NodeBuildContext


def create_node(context: NodeBuildContext):
    return DetectorNode(
        context.node_name,
        output_topic=str(context.params["output_topic"]),
    )
  • execution 可为 in_process(默认,加入控制器的 MultiThreadedExecutor)或 process(独立 Python 子进程)。
  • scheduling.cpu_affinity 仅用于 execution: process,接受统一角色 controlcomputebackgroundsharedallinherit;默认 shared。兼容数字和数字 列表,但跨平台 Mod 不应依赖具体核号。in_process 声明该字段会被拒绝。
  • lifecycle 可为 mod(默认,Mod 启用期间常驻)或 state
  • lifecycle: state 必须给出非空 states;框架在目标状态 on_prepare() 前启动节点,在过渡取消或离开最后一个关联状态后停止。多个状态共享一个节点时使用引用计数。
  • manifest.label 必须是非空字符串;params 原样传给 NodeBuildContext
  • 节点级 runtime_requirements 可选;依赖缺失时节点状态为 unavailable,模块不会被导入,也不会进入重启循环,Mod 的 route、action、state 和其他节点仍可使用。
  • restart 控制独立进程异常退出后的有限重启,默认最多 3 次、间隔 1.0 秒。 non_retryable_exit_codes 是可选的 1–255 整数列表;命中时直接标记故障且不再重启。 它只应用于确定性配置错误,不应包含一般运行时崩溃使用的退出码。
  • runtime 支持 python(默认)、executableroscommandcommand 是不 自动追加 --ros-args 的普通 Mod 内命令,必须使用 execution: process
  • runtime_profile 可选引用顶层运行环境;Portable 必须使用 execution: process
  • depends_on 声明节点依赖;框架按依赖顺序启动并逆序关闭。局部名称自动限定到当前 Mod,未知依赖和依赖环会在加载阶段报错。
  • shutdown 可配置 signalterminate_afterkill_after。默认先发 SIGTERM,3 秒后发 SIGKILL;也可配置成 SIGINT → SIGTERM → SIGKILL

节点状态发布在 state_machine_info.nodes[],Mod 状态发布在 state_machine_info.mods[]。依赖缺失使用 unavailable;依赖满足但节点运行后异常退出使用 faulted。Mod 生命周期节点的普通启动异常仍会使框架启动失败并回滚;State 生命周期节点的普通启动异常会取消本次状态切换并保留当前状态。

独立进程会依次使用当前 Mod 的 vendor/python/<平台-Python ABI>vendor/python/common 和宿主 Python,并只把 vendor/lib/<平台> 加入动态库路径。in_process 也支持匹配平台的 vendor,但框架会显式警告:导入模块和原生符号属于整个控制进程,不能可靠卸载,并可能与其他 Mod 发生版本或符号冲突;需要原生依赖隔离时应优先使用 execution: process

普通命令节点还支持以下专用字段:

nodes:
  bridge:
    runtime: command
    entrypoint: scripts/bridge.py
    interpreter: "${BRIDGE_PYTHON:-python3}"
    lifecycle: state
    states: [run]
    arguments: [--port, "${BRIDGE_PORT:-5557}"]
    cwd: .
    environment:
      PYTHONUNBUFFERED: "1"
      LD_LIBRARY_PATH:
        prepend: ["${SDK_ROOT:-/opt/sdk}/lib"]
        existing_only: true
    depends_on: [manager]
    shutdown:
      signal: SIGINT
      terminate_after: 3.0
      kill_after: 5.0
    manifest:
      label: 普通命令桥

entrypointcwd 只能位于 Mod 内。interpreterarguments 和环境值支持 $NAME${NAME}${NAME:-default},但不会通过 shell 执行。框架注入 BXI_MOD_ROOTenvironment 的字符串形式是直接赋值;映射形式支持 setunsetprependappendseparatorexisting_onlyunset: true 会从 子进程环境中删除该变量,且不能与赋值或拼接同时使用。command 不允许非空 paramsnamespaceremappings

nodes 适合由 Mod 自己拥有生命周期的感知、通信或设备辅助进程。当前 com.bxi.normal_depth 不再通过该字段启动 RealSense;真机相机由独立 bxi_depth_camera 包管理,深度状态订阅标准的 /<simulation|hardware>/body_depth_camera/depth/image_rect_rawcamera_info

speed_profiles

speed_profiles:
  walk:
    vx_scale: 1.0
    vx_min: -1.0
    vx_max: 1.0
    vy_scale: 0.5
    yaw_scale: 1.5

transition_profiles

与系统 profile 结构相同,但运行时自动命名为 mod-id/local-name

4. 名称限定规则

  • 不含 / 的 state、event、node state reference、speed profile 和 Mod transition profile 名称属于当前 Mod。
  • 包含 / 的值视为完整名称。
  • 资源键始终必须显式写完整命名空间。
  • 跨 Mod 引用应同时出现在 requires 中。

完整加载和校验行为见 Mod 系统

Clone this wiki locally