Skip to content

State Machine Config

konodoki edited this page May 14, 2026 · 14 revisions

状态机 YAML 配置

状态机配置文件:

src/bxi_example_py_elf3/config/elf3_state_machine.yaml

状态机配置定义:

  • 程序初始状态。
  • 遥控器消息如何变成状态机 event。
  • 有哪些状态。
  • 状态之间如何切换。
  • 切换时使用什么过渡行为。
  • 当前状态下速度输入如何缩放。
  • 是否导出状态图。

1. 顶层结构

initial_state: zero_torque

graph:
  validate: true
  export:
    dot: /tmp/elf3_state_machine.dot
    mermaid: /tmp/elf3_state_machine.mmd

remote_events:
  normal:
    slot: btn_1
    value: 1

transition_profiles:
  soft_switch:
    duration: 0.02
    exit_behavior: hold_last_motor
    enter_behavior: hold_last_motor

speed_profiles:
  normal:
    vx_scale: 1.0

states:
  normal:
    behavior: NormalState

2. initial_state

initial_state: zero_torque

启动后进入的状态。必须存在于 states

如果不写,状态机会使用 states 中第一个状态,但不推荐依赖这个行为。

3. graph:状态图自检和导出

graph:
  validate: true
  export:
    dot: /tmp/elf3_state_machine.dot
    mermaid: /tmp/elf3_state_machine.mmd

字段:

  • validate:是否启动时检查状态图。
  • export.dot:导出 Graphviz dot 文件。
  • export.mermaid:导出 Mermaid 文件。

自检会报告:

  • transition 目标状态不存在。
  • transition profile 不存在。
  • 某个状态监听了未声明的 remote event。
  • 从 initial_state 不可达的状态。
  • 没有任何输出转移的状态。
  • after 自动转移形成循环。

warning 用来提醒你检查配置;error 会阻止启动。

4. remote_events

remote_eventsMotionCommands 的按钮槽位转成状态机 event。

remote_events:
  sin_wave:
    slot: btn_10
    value: 5

字段:

  • sin_wave:状态机 event 名。
  • slot:读取哪个 MotionCommands 字段,例如 btn_1btn_10
  • value:期望值。

触发逻辑:

当前消息 slot == value
并且这个 event 看到的 slot 值相比上一次发生变化
  -> 触发 event

因此:

  • outputs.edge 很适合触发状态切换,因为它自动输出一帧后回 0。
  • outputs.level 也能触发,但需要松开后再次按下才会再次触发。

5. transition_profiles

transition_profiles 定义可复用过渡方式。

transition_profiles:
  instant:
    duration: 0.0
    exit_behavior: none
    enter_behavior: none

  soft_switch:
    duration: 0.02
    exit_behavior: hold_last_motor
    enter_behavior: hold_last_motor

  first_frame_switch:
    exit_duration: 0.02
    enter_duration: 0.1
    exit_behavior: hold_last_motor
    enter_behavior: first_frame_ramp_kp
    data:
      kp_start: zero
      kd_start: target

  dual_running_blend:
    duration: 0.3
    exit_behavior: none
    enter_behavior: dual_running_blend
    data:
      curve: smoothstep
      run_from: true
      run_to: true
      from_fallback: last_motor
      to_fallback: first_frame

字段:

  • duration:同时设置退出侧和进入侧时长。
  • exit_duration:退出旧状态时长。
  • enter_duration:进入新状态时长。
  • exit_behavior:旧状态过渡期间执行的行为名。
  • enter_behavior:新状态过渡期间执行的行为名。
  • data:过渡行为私有数据。

如果同时写 durationenter_duration,进入侧使用 enter_duration。状态机总过渡时长取 durationexit_durationenter_duration 的最大值。

当前常用行为:

  • none:不做特殊处理。
  • hold_last_motor:保持上一帧电机目标。
  • first_frame_ramp_kp:目标角度为新状态第一帧,kp/kd 按进度渐变。
  • dual_running_blend:旧状态和新状态都持续生成电机目标,输出为两侧 (qpos, kp, kd) 按进入进度混合后的结果。

first_frame_ramp_kpdata

  • kp_start: current:从当前 ctx.kp_last 开始。
  • kp_start: zero:从 0 开始。
  • kp_start: target:从目标 kp 开始。
  • kd_start 同理。

data 不是状态机核心字段。状态机只保存和透传它,具体含义由 robot_state_base.py 或状态类解释。

dual_running_blenddata

  • curve:混合曲线,支持 linearsmoothstepsmootherstep,默认 linear
  • run_from:是否采样旧状态运行输出,默认 true
  • run_to:是否采样新状态运行输出,默认 true
  • from_fallback:旧状态采样不到时使用什么,默认 last_motor
  • to_fallback:新状态采样不到时使用什么,默认 first_frame

fallback 可选:

  • last_motor / hold_last_motor:上一帧实际发给电机的目标。
  • first_frame:对应状态的 get_first_frame(ctx)
  • none:不使用退路。

6. speed_profiles

speed_profiles 控制当前状态如何使用遥控器速度输入。

speed_profiles:
  normal_run:
    vx_scale: 2.0
    vx_min: -1.0
    vx_max: 2.0
    vy_scale: 0.5
    yaw_scale: 1.0

字段:

  • vx_scale:前后速度缩放。
  • vy_scale:左右速度缩放。
  • yaw_scale:转向速度缩放。
  • vx_min:前后速度下限。
  • vx_max:前后速度上限。

状态引用:

states:
  normal_run:
    behavior: NormalRunState
    speed_profile: normal_run

注意:

  • 状态没有 speed_profile 时,不应用速度 profile。
  • 状态引用不存在的 profile 时会输出 warning,并且不应用速度缩放。

7. states

完整状态示例:

states:
  sin_wave:
    behavior: SinWaveState
    params:
      joint: 22
      amplitude: 0.4
      frequency: 1.0
    speed_profile: slow_demo
    transitions:
      on_event:
        normal:
          to: normal
          transition: soft_switch
        toggle_dance_pause:
          action: toggle_sin_pause
      after:
        - seconds: 3.0
          to: normal
          transition:
            base: first_frame_switch
            enter_duration: 0.2

字段:

  • behavior:Python 状态类名。
  • id:可选状态 id。通常不写,由框架自动分配。
  • params:传给状态类构造函数的参数。
  • speed_profile:引用速度 profile。
  • transitions.on_event:事件触发的转移或 action。
  • transitions.after:进入状态一段时间后的自动转移或 action。

8. on_event 写法

简写:

zero_torque: zero_torque

等价于:

zero_torque:
  to: zero_torque
  transition: instant

完整写法:

recover:
  to: recover
  delay: 0.2
  transition:
    base: first_frame_switch
    enter_duration: 1.0

字段:

  • to:目标状态。
  • delay:事件触发后延迟多少秒开始切换。
  • transition:过渡 profile 名,或 inline profile。
  • action:执行 action,不切状态。

只执行 action:

toggle_dance_pause:
  action: toggle_dance_pause

9. after 自动转移

after:
  - seconds: 3.0
    to: normal
    transition: soft_switch

字段:

  • seconds:进入当前状态多少秒后触发。
  • afterseconds 的等价写法。
  • to:目标状态。
  • transition:过渡方式。
  • action:到时间后执行 action。

示例:进入状态后 1 秒执行 action,3 秒后回 normal。

after:
  - seconds: 1.0
    action: halfway
  - seconds: 3.0
    to: normal
    transition: soft_switch

10. inline transition

常用过渡放在 transition_profiles,个别状态单独覆盖时用 inline transition。

transition:
  name: sin_wave_slow_entry
  base: first_frame_switch
  enter_duration: 0.3
  data:
    kp_start: zero
    kd_start: target

字段:

  • name:可选,日志、状态机信息和状态图里显示的名字。
  • base:继承哪个预设 profile。
  • profilebase 的等价写法。
  • extendsbase 的等价写法。
  • duration:覆盖总时长。
  • exit_duration:覆盖退出侧时长。
  • enter_duration:覆盖进入侧时长。
  • exit_behavior:覆盖退出行为。
  • enter_behavior:覆盖进入行为。
  • data:覆盖或追加行为私有数据。

推荐统一使用 base

11. 状态切换决策建议

适合写 YAML:

  • 遥控器事件触发状态切换。
  • 进入状态若干秒后自动返回。
  • 常规过渡方式。
  • 速度 profile。

适合写状态代码:

  • 根据机器人姿态判断安全退出。
  • 根据动作播放帧判断结束。
  • 根据模型输出或传感器条件决定下一状态。

代码里主动请求状态:

ctx.request_state(
    "normal",
    trigger="motion_finished",
    transition="soft_switch",
)

带 inline transition:

ctx.request_state(
    "normal",
    trigger="motion_finished",
    transition={"base": "first_frame_switch", "enter_duration": 0.1},
)

12. 下一步

Clone this wiki locally