Skip to content

Hands On Dual Running Blend

konodoki edited this page Jul 29, 2026 · 8 revisions

手把手 5:使用双状态运行混合

Transition 描述从状态机收到切换请求到目标状态正式进入之间,电机帧应该怎样生成。如果两边都是持续运行的模型,直接切换可能让 qpos/kp/kd 突变;只向一个固定进入姿态缓慢靠近,又会丢失两边的动态输出。

running_blend 正是为这种情况准备的:它在过渡期间采样源状态和目标状态的运行帧,再混合两侧 qpos/kp/kd

状态能力

class SinWaveState(RobotControlState, EntryFrameProvider, RunningFrameProvider):
    def get_entry_frame(self, ctx):
        last = ctx.last_motor_frame
        return self._motor_frame(ctx, last.qpos, last.kp, last.kd)

    def sample_running_frame(self, ctx, dt, *, advance):
        next_elapsed = self.elapsed + dt if advance else self.elapsed
        qpos = self._calculate_qpos(ctx, next_elapsed)
        if advance:
            self.elapsed = next_elapsed
        last = ctx.last_motor_frame
        return self._motor_frame(ctx, qpos, last.kp, last.kd)

    def on_update(self, ctx, dt):
        self._apply_frame(
            ctx, self.sample_running_frame(ctx, dt, advance=True)
        )

advance=False 时不得推进状态内部时间。

系统 profile

transition_profiles:
  dual_running_blend:
    type: running_blend
    duration: 0.3
    curve: smoothstep
    sample_from: true
    sample_to: true
    advance_from: true
    advance_to: false

此共享 profile 位于系统 elf3_state_machine.yaml

推荐保持这组推进设置:来源状态在交接期间继续推进时间、动作历史和步态相位,目标状态则停在入口时间点。这样机器人不会在过渡期间冻结,目标也不会在正式进入前提前消费时间轴。过渡完成后,目标执行 on_enter(),随后才由正常控制循环开始推进。

Mod route

routes:
  - from: com.bxi.basic_actions/normal
    event: activate
    to: sin_wave
    transition: dual_running_blend

  - from: sin_wave
    event: com.bxi.basic_actions/normal
    to: com.bxi.basic_actions/normal
    transition:
      profile: dual_running_blend
      duration: 1.0

代码主动返回:

ctx.request_state(
    "com.bxi.basic_actions/normal",
    trigger="motion_finished",
    transition={
        "profile": "dual_running_blend",
        "duration": 0.5,
        "sample_from": False,
    },
)

混合和生命周期

from_frame = 源运行帧,或过渡开始前最后电机帧
to_frame   = 目标运行帧,或目标进入帧
alpha      = curve(progress)
output     = from + (to - from) * alpha

内置实现会先把两侧自然布局解析到完整 Robot Layout,再执行上述公式。因此旧 29 关节状态 和新 31 关节状态可以互相混合;新增关节会平滑过渡到平台声明的默认目标。

目标先执行 on_prepare(),Session 再取进入帧。过渡完成才调用源 on_exit() 和目标 on_enter();被中断时调用目标 on_prepare_cancel()

同一个控制周期内会依次采样来源和目标。这里不要求两个模型并行运行;只要两次推理总耗时小于控制周期预算,串行采样具有更简单、确定性更强的状态语义。

常见问题

  • 开启 sample_from 时源必须实现 RunningFrameProvider
  • 开启 sample_to 时目标必须实现 RunningFrameProvider
  • 目标始终需要 EntryFrameProvider
  • 默认使用 advance_from: trueadvance_to: false。双方都为 false 会冻结两端动态输出;目标设为 true 则可能在正式进入前推进历史,并在 on_enter() 重置时产生跳变。
  • 仅当来源动作已经结束、无法继续生成有效帧时,才使用 sample_from: false,让过渡从开始前最后一个电机帧混合。
  • 突跳时检查进入帧和目标进入后的首帧是否一致。

Clone this wiki locally