-
Notifications
You must be signed in to change notification settings - Fork 12
State Machine Config
状态机配置文件:
src/bxi_example_py_elf3/config/elf3_state_machine.yaml
状态机配置定义:
- 程序初始状态。
- 遥控器消息如何变成状态机 event。
- 有哪些状态。
- 状态之间如何切换。
- 切换时使用什么过渡行为。
- 状态如何选择速度输入缩放 profile。
- 是否导出状态图。
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:
manifest:
label: 常规模式
index: 0
group: Base
icon: directions_walk
behavior: NormalStateinitial_state: zero_torque启动后进入的状态。必须存在于 states。
如果不写,状态机会使用 states 中第一个状态,但不推荐依赖这个行为。
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 会阻止启动。
remote_events 把 MotionCommands 的按钮槽位转成状态机 event。
remote_events:
sin_wave:
slot: btn_10
value: 5字段:
-
sin_wave:状态机 event 名。 -
slot:读取哪个MotionCommands字段,例如btn_1、btn_10。 -
value:期望值。
触发逻辑:
当前消息 slot == value
并且这个 event 看到的 slot 值相比上一次发生变化
-> 触发 event
因此:
-
outputs.edge很适合触发状态切换,因为它自动输出一帧后回 0。 -
outputs.level也能触发,但需要松开后再次按下才会再次触发。
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:过渡行为私有数据。
如果同时写 duration 和 enter_duration,进入侧使用 enter_duration。状态机总过渡时长取 duration、exit_duration、enter_duration 的最大值。
当前常用行为:
-
none:不做特殊处理。 -
hold_last_motor:保持上一帧电机目标。 -
first_frame_ramp_kp:目标角度为新状态第一帧,kp/kd按进度渐变。 -
dual_running_blend:旧状态和新状态都持续生成电机目标,输出为两侧(qpos, kp, kd)按进入进度混合后的结果。
first_frame_ramp_kp 的 data:
-
kp_start: current:从当前ctx.kp_last开始。 -
kp_start: zero:从 0 开始。 -
kp_start: target:从目标kp开始。 -
kd_start同理。
data 不是状态机核心字段。状态机只保存和透传它,具体含义由 utils/robot_state_base.py 或状态类解释。
dual_running_blend 的 data:
-
curve:混合曲线,支持linear、smoothstep、smootherstep,默认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:不使用退路。
speed_profiles 定义速度输入的缩放和限幅。它不会自动作用到所有状态,而是由状态类通过 self.get_cmd_vel(ctx) 主动读取。
运行时流程:
MotionCommands.vel_des.x / vel_des.y / yawdot_des
-> BxiExample.raw_cmd_vel
-> timer 内快照为 ctx.current_raw_cmd_vel
-> 当前状态 self.get_cmd_vel(ctx)
-> 按 states.*.speed_profile 缩放和限幅
-> 调用状态的 process_cmd_vel(ctx, cmd_vel)
-> 写入 ctx.current_cmd_vel,供模型和状态信息使用
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:前后速度上限。 -
vy_min:左右速度下限。 -
vy_max:左右速度上限。 -
yaw_min:转向速度下限。 -
yaw_max:转向速度上限。
状态引用:
states:
normal_run:
behavior: NormalRunState
speed_profile: normal_run注意:
- 状态没有
speed_profile时,get_cmd_vel(ctx)返回零速度。这样没有显式声明速度能力的状态不会被遥控速度影响。 - 状态引用不存在的 profile 时会输出一次 warning,并返回零速度。
- 状态不调用
get_cmd_vel(ctx)时,speed_profile不会产生任何效果。 - 需要特殊滤波、锁某个方向或平滑速度时,在状态类里重写
process_cmd_vel(ctx, cmd_vel)。基类会把返回值统一写入ctx.current_cmd_vel。
示例:
def get_motor_frame(self, ctx: BxiExample, dt: float) -> Optional[MotorFrame]:
cmd_vel = self.get_cmd_vel(ctx)
qpos, _ = ctx.normal.inference_step(
ctx.current_q,
ctx.current_dq,
ctx.current_quat_wxyz,
ctx.current_omega,
cmd_vel,
)
return self._motor_frame(qpos, ctx.normal.kps, ctx.normal.kds)完整状态示例:
states:
sin_wave:
manifest:
label: 正弦测试
index: 20
group: Debug
icon: waves
confirm: false
confirm_message: ""
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。通常不写,由框架自动分配。 -
manifest:状态展示元数据,会出现在state_machine_info.graph.states中,供调试界面或外部工具展示。 -
params:传给状态类构造函数的参数。 -
speed_profile:引用速度 profile;状态调用self.get_cmd_vel(ctx)时生效。 -
transitions.on_event:事件触发的转移或 action。 -
transitions.after:进入状态一段时间后的自动转移或 action。
manifest 常用字段:
-
label:展示名称。 -
index:展示排序。 -
group:展示分组。 -
icon:展示图标名。 -
confirm:外部控制界面是否应二次确认。 -
confirm_message:确认提示文案。
这些字段不参与状态机切换决策;状态机只透传它们。
简写:
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_pauseafter:
- seconds: 3.0
to: normal
transition: soft_switch字段:
-
seconds:进入当前状态多少秒后触发。 -
after:seconds的等价写法。 -
to:目标状态。 -
transition:过渡方式。 -
action:到时间后执行 action。
示例:进入状态后 1 秒执行 action,3 秒后回 normal。
after:
- seconds: 1.0
action: halfway
- seconds: 3.0
to: normal
transition: soft_switch常用过渡放在 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。 -
profile:base的等价写法。 -
extends:base的等价写法。 -
duration:覆盖总时长。 -
exit_duration:覆盖退出侧时长。 -
enter_duration:覆盖进入侧时长。 -
exit_behavior:覆盖退出行为。 -
enter_behavior:覆盖进入行为。 -
data:覆盖或追加行为私有数据。
推荐统一使用 base。
适合写 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},
)- 写新状态:看 手把手添加自定义状态。
- 写自定义过渡:看 自定义过渡行为。
- 绑定遥控器:看 遥控器 YAML 配置。