Skip to content

Architecture

konodoki edited this page Jul 29, 2026 · 21 revisions

架构总览

当前架构把平台适配、控制框架、动作实现和模型资产分层。BxiExample 只处理 ROS 和硬件边界;RobotControlFramework 拥有 Mod、状态、Transition、Context 和状态机。状态侧的部署单元是 Mod,不再依赖中央状态类文件。

当前 bxi_example_demo.py 是 ELF3 的一个平台适配实现,不是框架本体。需要接入其他 机器人消息、SDK 或硬件总线时,请按机器人平台适配指南 实现新的 ControlPlatformAdapter,不要把硬件差异写进状态机或策略公共运行时。

总数据流

手柄 / 键盘 / CRSF / 自定义设备
  -> InputDeviceManager
  -> InputDriver
  -> sources -> controls -> outputs
  -> communication/msg/MotionCommands
  -> BxiExample 的 ROS subscription
  -> BxiExample 按消息关节名采集 RobotObservation 并排队 raw events
  -> RobotControlRuntime 的独立控制线程
       -> BxiExample.snapshot_control_inputs()
       -> 首帧建立完整 Robot Layout
       -> RobotControlFramework 内的 RemoteEventAdapter / RobotStateMachine
       -> 可选 JointCommandComposer 合并模型、话题、IK、轨迹等命令源
       -> RobotControlState 输出自己的 29/31/N 自然 MotorFrame
       -> JointCommandResolver 补齐完整 Robot Layout
       -> Transition 在完整布局上插值
       -> BxiExample.publish_motor_frame()
  -> BxiExample 将 MotorFrame 转换为 ActuatorCmds
  -> 机器人硬件

这条边界的核心约定很简单:平台每周期给框架一份完整观测和事件,框架返回最终电机帧。框架不创建硬件消息,不发布电机话题,也不负责机器人 reset。因此更换消息类型、总线或硬件平台时,状态图和 Mod 不需要跟着改。

当 route 要求切换状态时,RobotStateMachine 会在源状态和目标状态之间执行 Transition。它负责切换期间的电机帧,避免两边 qpos/kp/kd 不连续时直接换帧产生突跳;完成后目标 RobotControlState 才接管正常更新。

配置和代码的加载流:

elf3_state_machine.yaml(系统配置)
  + 内置 mods/
  + mod_paths 中的客户 Mod
  -> 发现 mod.yaml
  -> 校验依赖并排序
  -> 加载 entrypoint
  -> 注册 eager/lazy 资源、状态工厂和 Python 节点
  -> 合成 states/events/routes/actions/nodes/profiles
  -> 构建 RobotStateMachine
  -> 按 Mod/State 生命周期管理节点

文件分层

src/remote_controller/
  config/xbox_default.yaml
  include/remote_controller/
  src/

src/bxi_example_py_elf3/
  config/elf3_state_machine.yaml       # 系统级配置
  mods/
    com.bxi.basic_actions/
      mod.yaml                         # 状态图贡献
      plugin.py                        # 工厂和资源注册
      *_state.py                       # 状态实现
      assets/                          # 该 Mod 私有资产
    com.bxi.back_flip/
    com.bxi.forward_flip/
    com.bxi.ballet/
  bxi_example_py_elf3/
    framework/                       # 与 ELF3 部署代码分离的可移植运控框架
      inference/                     # 后端、历史帧、策略契约和推理运行时
      joints/                        # 具名布局、默认目标、解析器、映射与标定
      platform/                      # 机器人接入协议、关节 I/O 和控制 Runtime
      mod_api/                       # 面向 Mod 作者的公共 API
      runtime/                       # 状态机、加载器、调度器和控制内核
      transitions/                   # 通用 Transition
    policies/                        # ELF3 内置推理策略
      joints.py                     # 模型关节契约与策略内部顺序
      normal.py
      amp.py
      beyondmimic.py
      depth.py
    bxi_example_demo.py             # ELF3 ROS 适配层及平台默认目标
    bxi_example_mjlab.py            # 原 MJLab 示例入口

运行时对象

对象 责任
RobotControlRuntime 持有控制调度器、Framework、维护线程和平台适配边界
RobotControlFramework 接收 RobotObservation/events,推进状态机并返回 MotorFrame
ModRuntime 保存合成配置、状态工厂、资源管理器、已加载 Mod 和动态模块
ResourceManager 注册、按 eager/lazy 策略创建、缓存和关闭资源
StateBuildContext 为状态工厂提供名称、稳定 state id 和强类型参数读取
RobotStateMachine 处理事件、延迟、过渡、状态生命周期和图导出
RemoteEventAdapter MotionCommands 槽位变化转成 Mod 事件
JointCommandComposer 在状态内部合并多个具名命令源,检查覆盖所有权并复用输出帧
JointCommandResolver 将状态自然输出按名称解析成完整 Robot Layout;映射仅编译一次

Robot Layout 不再由固定的 29 关节常量传给 Runtime。具名消息的首个合法完整快照决定当前 机器人是 29、31 还是 N 个关节;策略类自己的 PolicyJointContract 仍固定模型输入输出, 二者互不改写。旧模型缺失的新增关节由平台 JointCommandDefaults 显式提供目标。 模型输出维度也不等于状态输出维度:状态可用 JointCommandComposer 把模型目标与话题、 IK、轨迹或其他控制器合成,再把自然输出交给 Resolver 适配实际机器人。 完整的输入选择、输出补齐/裁剪、Transition 和固定顺序硬件规则见 关节布局与映射

Mod 只能依赖 bxi_example_py_elf3.framework.mod_apiBxiExample 通过 framework.platform.RobotControlRuntime 使用运控内核,不再自己组装调度器、loader、 状态和状态机。项目不保留旧路径的兼容转发模块,错误依赖会在开发阶段直接暴露。

命名空间

Mod 清单中的本地名称会自动限定:

Mod id:       com.example.wave
state:        wave
event:        activate
resource:     com.example.wave/policy

运行时状态:  com.example.wave/wave
运行时事件:  com.example.wave/activate

同一 Mod 内的 from: waveto: waveevent: activate 会自动限定。跨 Mod 引用必须写完整名称,并通过 requires 声明依赖。

修改边界

新增动作:

  • 新建或扩展一个 Mod。
  • mod.yaml 声明状态、事件、路由和 action。
  • 简单状态用 factory: module:Class;需要资源和扩展注册时再由 plugin.py 返回工厂。
  • 模型放进该 Mod 的 assets/ 并注册资源。
  • 在遥控器 YAML 中输出清单所使用的 btn_N=value

新增过渡:

  • 框架通用过渡放入 framework/transitions/
  • 业务专用过渡放在对应 Mod 中,并由 ModDefinition.transition_plugins 显式注册。
  • profile 可放系统 YAML;Mod 私有 profile 放 mod.yaml,会自动加命名空间。

新增输入协议:

  • 先判断是否仅靠遥控器 YAML 能完成。
  • 必须读新设备时再实现 InputDriverBase 和工厂。
  • Driver 只产生 raw signal,不直接写状态名。

公开发布:

  • 在要整体移除的 Mod 清单中设置 visibility: protected
  • tools/sanitize_release.py 删除整个目录并验证保留 Mod 的依赖闭包。

下一步

Clone this wiki locally