-
Notifications
You must be signed in to change notification settings - Fork 12
YAML Reference
项目存在三类 YAML:遥控器映射、状态机系统配置和 Mod 清单。
主文件:
src/remote_controller/config/xbox_default.yaml
顶层结构:
inputs:
selection: {}
sources: {}
curves: {}
controls: {}
outputs: {}
system: {}
system_mutexes: {}
system_reset_motion_after: []| 字段 | 含义 |
|---|---|
scan_interval_ms |
扫描候选设备的周期 |
promote_stable_ms |
高优先级设备持续可用多久后允许抢占 |
每个 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 包含 joystick、keyboard 和 crsf。自定义类型需注册输入驱动工厂。
通用字段:type、priority、ready_timeout_ms、loss_timeout_ms、cooldown_ms、signals。设备专有字段由对应 driver 解析。
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]支持 analog、bool 和 enum。control 把多个设备输入统一成业务控制量,输入规则可带 source、direction、curve、when、value 等字段。条件支持 pressed、released、equals、范围以及 all/any 组合。
完整语法和实例见遥控器输入映射配置。
outputs:
conflict_policy: first_wins
publish_on_change: true
analog:
vx: move.vx
edge:
- output: btn_10=8
when:
any:
- [keyboard.wave_event]
- [gamepad.a_event]conflict_policy 可为 first_wins、last_wins 或 error。publish_on_change 控制是否仅在消息变化时发布。analog 连续写值,level 保持条件结果,edge 只在进入条件时产生脉冲;最终字段写入 MotionCommands。
主文件:
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 |
states、remote_events 和 speed_profiles 由 Mod 合成,不应写入此文件。
Transition 是源状态与目标状态之间的切换策略,决定切换期间怎样生成电机 qpos/kp/kd。它的主要作用是处理两边输出不连续的问题,避免直接换帧造成突跳;instant 表示明确选择不做中间处理。
instantholdentry_gain_ramprunning_blendsequence
| 字段 | 必需 | 含义 |
|---|---|---|
schema |
是 | 当前固定为 1
|
id |
是 | 全局唯一、带命名空间的 Mod id |
name |
是 | 面向用户的非空 Mod 名称 |
version |
是 | 数字点版本,如 1.0.0
|
api |
是 | 兼容的框架 Mod API 版本范围,如 ">=2,<3"
|
enable |
是 | 是否启用此 Mod,必须是布尔值 |
entrypoint |
是 | 高级插件的 module:function;约定式加载显式写 null
|
visibility |
是 |
public 或发布工具识别的 protected
|
requires |
是 | Mod 依赖及版本约束;没有依赖时写 []
|
conflicts |
是 | 不能同时启用的 Mod id 列表;没有冲突时写 []
|
python_exports |
是 | 暂时导出的顶层 Python 包;没有时写 []
|
runtime_requirements |
是 | Python、ROS 和系统动态库依赖;三类均须显式填写 |
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: ">=2,<3"
enable: true
entrypoint: null
visibility: public
requires: []
conflicts: []
python_exports: []
runtime_requirements:
python: []
ros: []
system: []requires:
- com.example.shared
- id: com.bxi.basic_actions
version: ">=1,<2"api 和 requires[].version 使用相同的逗号分隔约束语法,支持
==、!=、>=、<=、> 和 <。当前框架公开的 Mod API
版本是 1.1.0;加载器会在导入任何 Mod 代码前检查兼容性。
conflicts:
- com.example.legacy_wave冲突声明只需由一方给出;如果两个 Mod 都处于启用状态,加载器会在执行任何 Mod 代码前拒绝启动。未安装或已禁用的冲突对象不影响加载。Mod 不能声明与自身冲突。
python_exports:
- customer_commonMod 根目录必须存在 customer_common/__init__.py。导出名必须是合法且全局唯一的 Python 包名。
requires 只表示 Mod 之间的依赖;Python 模块、ROS 包和系统动态库写入 runtime_requirements:
runtime_requirements:
python:
- import: numpy
ros:
- package: sensor_msgs
system:
- library: realsense2当前版本不比较依赖版本,也不会自动执行 pip、apt 或 rosdep。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(..., loading="eager") 或 loading="lazy";这样资源
工厂与加载策略保持在同一处代码中。
events:
activate: {slot: btn_10, value: 8}
any_change: {slot: btn_9}slot 必须是合法标识符,value 若存在必须是整数。事件必须至少被一条 route 或 action 使用。
states:
wave:
factory: state:WaveState
id: 12345
label: 示例
priority: 100
group: Customer
icon: waves
confirm: true
confirm_message: 请确认安全
speed_profile: walk
params:
amplitude: 0.4factory: 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:
- 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字段:from、event、to、transition、delay。每条 route 都必须包含 to,不能包含 action。
actions:
- from: wave
event: toggle_pause
action: toggle_pause
manifest:
label: 暂停/继续字段:from、event、action、manifest。manifest.label 是必需的非空字符串;action 只调用当前状态的 action handler,不发生状态切换,也不能配置 to/transition/delay。
加载器会规范化 from/event,并把 action 元数据发布到 state_machine_info.graph.actions[]。与状态快照相同,manifest 字段会在发布对象中展开,例如上例发布为 {from: ..., event: ..., action: toggle_pause, label: 暂停/继续}。
nodes:
depth_camera:
entrypoint: camera_node:create_node
execution: process
lifecycle: state
states: [normal_depth]
manifest:
label: 深度相机发布器
runtime_requirements:
python:
- import: pyrealsense2
ros:
- package: sensor_msgs
system:
- library: realsense2
params:
output_topic: /camera/depth/image_36x48
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 DepthCameraNode(
context.node_name,
output_topic=str(context.params["output_topic"]),
)-
execution可为in_process(默认,加入控制器的MultiThreadedExecutor)或process(独立 Python 子进程)。 -
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(默认)、executable、ros和command。command是不 自动追加--ros-args的普通 Mod 内命令,必须使用execution: process。 -
depends_on声明节点依赖;框架按依赖顺序启动并逆序关闭。局部名称自动限定到当前 Mod,未知依赖和依赖环会在加载阶段报错。 -
shutdown可配置signal、terminate_after和kill_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: 普通命令桥entrypoint 和 cwd 只能位于 Mod 内。interpreter、arguments 和环境值支持
$NAME、${NAME}、${NAME:-default},但不会通过 shell 执行。框架注入
BXI_MOD_ROOT。environment 的字符串形式是直接赋值;映射形式支持 set、
unset、prepend、append、separator 和 existing_only。unset: true 会从
子进程环境中删除该变量,且不能与赋值或拼接同时使用。command 不允许非空
params、namespace、remappings。
内置 com.bxi.normal_depth 使用 execution: process、lifecycle: mod 提供 Python RealSense 发布节点。它随 Mod 常驻,默认发布 /camera/depth/image_raw、/camera/depth/image_64x36 和 /camera/depth/image_36x48,并保留可选 color、双 IR 和 IMU 输出。
speed_profiles:
walk:
vx_scale: 1.0
vx_min: -1.0
vx_max: 1.0
vy_scale: 0.5
yaw_scale: 1.5与系统 profile 结构相同,但运行时自动命名为 mod-id/local-name。
- 不含
/的 state、event、node state reference、speed profile 和 Mod transition profile 名称属于当前 Mod。 - 包含
/的值视为完整名称。 - 资源键始终必须显式写完整命名空间。
- 跨 Mod 引用应同时出现在
requires中。
完整加载和校验行为见 Mod 系统。