-
Notifications
You must be signed in to change notification settings - Fork 12
Framework Internals
本页按启动和每帧执行顺序描述当前实现。
读取 YAML
-> 校验 sources/controls/outputs
-> 注册并创建候选 InputDriver
-> InputDeviceManager 周期探测
-> 选择唯一活动设备
-> InputMapper 计算 controls
-> MotionCommandsAdapter 写消息
-> 发布 motion_commands
设备选择考虑 priority、ready_timeout_ms、loss_timeout_ms、cooldown_ms 以及全局扫描/稳定时间。Driver 只生产 raw signal,业务组合留在 YAML。
edge 只在条件由 false 变 true 时触发;level 持续反映条件;analog 每帧写连续值。
BxiExample
-> 读取 ROS 参数和系统状态机 YAML
-> 创建 ROS subscription / publisher / service client
-> 实现 ControlPlatformAdapter 的三个平台方法
-> 创建 RobotControlRuntime
RobotControlRuntime
-> 创建 RobotControlFramework
-> load_mod_runtime()
-> build_robot_states()
-> 绑定状态并创建 RobotStateMachine
-> 创建 RemoteEventAdapter
-> 创建 ControlScheduler 和低频维护线程
BxiExample 不再持有 ModRuntime、状态实例或状态机,也不再代理 set_motor_target()、request_state() 和模型预热等框架能力。这些由 RobotControlFramework 作为 RobotControlContext 的实现统一提供。BxiExample 只实现启动检查、观测快照和电机帧发送,具体迁移方法见机器人平台适配指南。
RobotControlRuntime 通过 ControlPlatformAdapter 隔离传感器和电机总线,同时保留一条直接的每周期调用路径。控制时钟由独立的 ControlScheduler 管理,不由 ROS Timer 驱动。
每个清单先校验 schema、id、版本和 API。依赖拓扑排序后,入口模块或 factory: module:Class 引用以由 Mod id 确定的私有包名导入,使相对 import 可用并隔离不同 Mod 的模块命名空间。
加载失败会关闭新资源、移除动态模块、恢复过渡插件注册,并撤销 Python exports 和对应的 sys.path 修改。
系统 YAML 先复制为 base config,mod_paths 不进入最终状态机配置。随后按依赖顺序合成:
statesremote_eventsspeed_profiles- Mod
transition_profiles -
routes/actions生成的states.*.transitions.on_event -
nodes生成的 Python 节点规格
所有本地名称在这一阶段限定为 mod-id/local-name。输入冲突、未使用事件、未知目标、priority/index、profile 和 initial state 都在返回运行时前校验。
构建器不再扫描类。它遍历合成状态:
- 使用显式
id,或对完整名称计算 CRC32 int31。 - 创建
StateBuildContext。 - 调用显式 Mod factory,或由类的 dataclass
Params生成约定 factory。 - 拒绝未知参数。
- 设置 speed profile 和 manifest。
注册时只保存 key、owner、Mod 根和 factory。ResourceHandle.get() 第一次调用才构造实例;context.asset() 负责限定资产必须位于本 Mod 的 assets/。关闭运行时会逆序调用实例的 close()。
RobotControlRuntime 到达绝对时间释放点
-> 调用 BxiExample.snapshot_control_inputs()
-> 在同一把观测锁内更新长期复用的 JointStateBuffer 和传感器缓冲
-> 返回稳定 RobotObservation 并取出已排队 events
-> framework.update(observation, events, dt)
-> 更新 RobotControlContext 的当前周期快照
-> state_machine.update(dt, events)
-> Transition 或当前状态生成最终 MotorFrame
-> 调用 BxiExample.publish_motor_frame()
-> 把 MotorFrame 写入 ActuatorCmds 并发布
-> BxiExample 按频率发布 state_machine_info
RobotObservation.joints 是带 JointLayout 的 JointStateView。平台适配层长期持有观测和
数组,每周期只原地更新;它们不能引用生命周期不确定的 ROS 消息内存。Framework 首次看到
输入布局时编译名称映射,之后只执行数组映射。框架返回 MotorFrame | None,其中
MotorFrame.layout 明确输出关节语义;有输出时由平台适配层决定如何发送,没有输出时不发
新的电机命令。
Transition 是状态机在源状态和目标状态之间创建的一次切换 Session。它在目标状态正式进入前暂时负责每周期的电机输出,用来处理两边 qpos/kp/kd 不连续时的保持、增益渐变或帧混合;instant Session 则立即完成。
plan.validate_states(source, target)
target.on_prepare(ctx, source)
session = plan.create_session(...)
每帧:session.update(ctx, dt)
完成:
source.on_exit(ctx)
current = target
target.on_enter(ctx)
活动过渡被中断或 Session 抛错时调用目标 on_prepare_cancel(),当前状态仍是源状态。
mod.yaml 顶层 actions 生成只含 action 的事件规则,不切换状态。每条 action 必须声明非空的 manifest.label。状态机先查全局 handler,再调用当前状态的 on_action(ctx, name);都未处理会报错。
状态机验证未知目标、未声明事件、过渡能力、不可达状态和无出边状态。DOT/Mermaid 导出来自同一合成配置。
状态信息话题包含当前状态、pending/active transition、本周期事件以及 states、actions、profiles 和 remote events 快照。graph.actions[] 中的 manifest 会像 state manifest 一样展开,label 可直接用于界面显示。
Mod 节点状态位于顶层 nodes[]。进程内节点加入主 MultiThreadedExecutor;独立进程节点由 runner 加载同一个 Mod entrypoint。State 生命周期节点在目标状态准备前启动,过渡取消时回收,切换完成后释放源状态不再需要的节点。
Mod、资源、动态模块和状态图只在节点启动时构建。启动失败会清理已经创建的资源、模块和注册项;启动成功后运行时保持不变。修改系统 YAML、Mod 或资产后必须重启节点,控制循环不扫描文件,也不动态替换状态机。
- 新动作:Mod 状态 + 工厂 + 清单。
- 新模型:Mod 资源工厂。
- 新业务过渡:Mod 的
ModDefinition.transition_plugins显式注册。 - 新后台 ROS 节点:Mod
nodes+ PythonNodeBuildContext工厂。 - 新通用过渡:
framework/transitions/。 - 新设备:
InputDriverBase工厂。 - 新消息适配:
MotionCommandsAdapter。
客户 Mod 只导入 bxi_example_py_elf3.framework.mod_api。framework/runtime 保存
RobotControlFramework、Mod loader、ResourceManager、状态构建器、状态机和 Transition
注册/编译逻辑。公开生命周期使用 RobotControlContext Protocol,不直接绑定
BxiExample。旧包路径已移除,不提供兼容转发层。
ctx.ros_node 是为必须创建 ROS subscription、service 或 timer 的高阶 Mod 保留的显式逃生口。普通状态不应使用它;一旦使用,该 Mod 就是主动选择 ROS 集成,而不是框架的主控制链路依赖 ROS。