Skip to content

Hands On Custom Transition

konodoki edited this page Jul 25, 2026 · 13 revisions

手把手 4:在 Mod 内写自定义过渡

Transition 负责在状态切换期间临时生成电机帧。需要它是因为源状态和目标状态的 qpos/kp/kd 不一定连续,直接切换可能造成突跳;内置过渡可以保持、渐增增益或混合两个运行状态。

当这些内置策略无法表达业务需要的切换过程时,才需要自定义 Transition。本课为 com.example.sin_wave 增加 pose_gain_blend,平滑混合过渡开始帧和目标进入帧。

1. 新建模块

mods/com.example.sin_wave/pose_gain_blend.py
from collections.abc import Mapping

from bxi_example_py_elf3.utils.transition_core import (
    ConfigReader,
    MotorFrame,
    SingleClassTransition,
    require_entry_frame_provider,
)


class PoseGainBlendTransition(SingleClassTransition):
    type_name = "com.example.sin_wave.pose_gain_blend"

    def __init__(self, name, duration, curve):
        super().__init__(name, duration)
        self._curve = curve
        self._start = None
        self._target = None

    @classmethod
    def from_config(cls, name: str, raw: Mapping[str, object]):
        reader = ConfigReader(raw, name)
        duration = reader.float("duration", minimum=0.0)
        curve = reader.literal(
            "curve", ("linear", "smoothstep"), default="smoothstep"
        )
        reader.finish()
        return cls(name, duration, curve)

    def validate_states(self, from_state, to_state):
        require_entry_frame_provider(to_state)

    def on_start(self, ctx, from_state, to_state):
        self._start = MotorFrame.create(ctx.pos_last, ctx.kp_last, ctx.kd_last)
        self._target = require_entry_frame_provider(to_state).get_entry_frame(ctx)

    def apply(self, ctx, dt, progress):
        if self._start is None or self._target is None:
            raise RuntimeError("pose gain blend has not started")
        alpha = progress
        if self._curve == "smoothstep":
            alpha = progress * progress * (3.0 - 2.0 * progress)
        ctx.set_motor_target(
            self._start.qpos + (self._target.qpos - self._start.qpos) * alpha,
            self._start.kp + (self._target.kp - self._start.kp) * alpha,
            self._start.kd + (self._target.kd - self._start.kd) * alpha,
        )

2. 导入注册

plugin.py 顶层添加:

from . import pose_gain_blend  # noqa: F401

无需修改框架注册表。

3. 目标状态提供进入帧

class SinWaveState(RobotControlState, EntryFrameProvider):
    def get_entry_frame(self, ctx):
        return self._motor_frame(
            ctx.joint_nominal_pos, ctx.joint_kp, ctx.joint_kd
        )

4. 清单 profile 和 route

transition_profiles:
  pose_blend:
    type: com.example.sin_wave.pose_gain_blend
    duration: 0.5
    curve: smoothstep

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

单次覆盖:

transition:
  profile: pose_blend
  duration: 1.0
  curve: linear

5. 验证

修改插件后重启控制节点;启动日志应显示 ModRuntime 构建成功。常见错误:

  • unknown transition typeplugin.py 没导入模块。
  • duplicate transition type:名称未包含唯一 Mod 命名空间。
  • unknown fields:字段未被 reader 消费。
  • must implement EntryFrameProvider:某条目标边能力不足。

下一课:双状态运行混合

Clone this wiki locally