-
Notifications
You must be signed in to change notification settings - Fork 12
Advanced Recipes
本页展示这套框架的上限写法。建议先读完基础页面:
目标:键盘 0 切到 sin_wave。
遥控器:
sources:
keyboard:
type: keyboard
signals:
keyboard.sin_wave: {from: keyboard.key, key: "0"}
controls:
keyboard.sin_wave: {type: bool, source: keyboard.sin_wave}
outputs:
edge:
- output: btn_10=5
when: [keyboard.sin_wave]状态机:
remote_events:
sin_wave:
slot: btn_10
value: 5
states:
normal:
transitions:
on_event:
sin_wave:
to: sin_wave
transition: soft_switch
sin_wave:
behavior: SinWaveStateoutputs:
edge:
- output: btn_10=5
when:
any:
- [trigger.right, button.west]
- [keyboard.sin_wave]适合只用一次的组合键。
controls:
command.sin_wave:
type: bool
expr:
any:
- [trigger.right, button.west]
- [keyboard.sin_wave]
- [switch.mode=high, button.south]
outputs:
edge:
- output: btn_10=5
when: [command.sin_wave]适合复用、调试、统一命名。
controls:
move.vx:
type: analog
mix: max_abs
sources:
- source: gamepad.left_y
direction: -1
curve: stick
- source: keyboard.vx
deadzone: 0.03
min: -1.0
max: 1.0
alpha: 0.03
outputs:
analog:
vel_des.x: move.vxmax_abs 表示哪个输入更大用哪个,适合手柄和键盘共存。
状态机侧还需要给会使用速度的状态声明 speed_profile:
speed_profiles:
normal:
vx_scale: 1.0
vy_scale: 0.5
yaw_scale: 1.0
states:
normal:
behavior: NormalState
speed_profile: normal状态代码中通过 self.get_cmd_vel(ctx) 读取处理后的速度:
cmd_vel = self.get_cmd_vel(ctx)状态需要额外滤波或屏蔽某个方向时,重写 process_cmd_vel(ctx, cmd_vel);ctx.current_cmd_vel 的写入由基类完成。
没有 speed_profile 的状态会得到零速度,因此静态动作、起身动作、高危动作默认不会被摇杆速度影响。
假设另一个 driver 或节点写入 auto.vx。
controls:
auto.vx:
type: analog
source: auto.vx
outputs:
analog:
vel_des.x:
controls: [move.vx, auto.vx]
mix: first_active含义:
-
move.vx有输入时用遥控器。 - 否则使用
auto.vx。
也可以反过来让自动优先。
CRSF 的 CH15(默认映射为十字键左右)是三档轴,可用于模式选择:
sources:
crsf:
type: crsf
signals:
crsf.dpad_x: {from: crsf.channel.15}
controls:
switch.mode:
type: enum
source: crsf.dpad_x
default: middle
hysteresis: 0.03
positions:
low: [-1.0, -0.35]
middle: [-0.34, 0.34]
high: [0.35, 1.0]使用:
outputs:
edge:
- output: btn_10=1
when: [switch.mode=high]更安全的写法是组合确认:
when:
any:
- [switch.mode=high, button.west]持续按住进入某个旧兼容按钮:
level:
- output: btn_1=1
when: [keyboard.normal]一次性切换状态:
edge:
- output: btn_10=5
when: [keyboard.sin_wave]判断原则:
- 需要持续有效:
level。 - 只触发一次:
edge。
states:
normal:
transitions:
on_event:
recover:
to: recover
delay: 0.5
transition: first_frame_switch收到 event 后先进入 pending,0.5 秒后开始 transition。
states:
sin_wave:
behavior: SinWaveState
transitions:
after:
- seconds: 3.0
to: normal
transition: soft_switch如果状态内部也可能提前结束,可以在代码里:
ctx.request_state("normal", trigger="finished", transition="soft_switch")states:
zero_torque:
transitions:
on_event:
recover:
to: recover
transition:
name: recover_from_zero
base: first_frame_switch
enter_duration: 1.0这样不需要为了一次特殊过渡新增全局 profile。
transition:
base: first_frame_switch
enter_duration: 0.5
data:
kp_start: current
kd_start: target代码读取:
kp_start = str(transition.data.get("kp_start", "current"))适合把行为参数留在 YAML,而不是把字段写死到 utils/state_machine.py。
状态配置:
states:
dance:
behavior: DanceState
transitions:
on_event:
toggle_dance_pause:
action: toggle_dance_pause状态类:
def on_action(self, ctx: BxiExample, action_name: str) -> bool:
if action_name != "toggle_dance_pause":
return False
self.playing = not self.playing
return True适合暂停、切换子模式、重置内部计数器。
def on_update(self, ctx: BxiExample, dt: float) -> None:
if ctx.is_orientation_unsafe(ctx.current_quat_xyzw):
ctx.request_state("zero_torque", trigger="safety")
return
# 正常动作安全逻辑放状态内部,比只靠外部按键更可靠。
如果底层 MotionCommands 未来新增字段,比如:
body_pitch_des
推荐只改:
src/remote_controller/include/remote_controller/motion_commands_adapter.hpp
src/remote_controller/src/motion_commands_adapter.cpp
增加:
bool is_motion_command_field_supported(const std::string &field)
{
return field == "body_pitch_des" || ...;
}和:
if (field == "body_pitch_des") {
message.body_pitch_des = float_value;
return true;
}然后 YAML 直接写:
outputs:
analog:
body_pitch_des: body.pitch这样字段适配集中在 adapter,不污染业务层。
手柄:
sources:
gamepad:
type: joystick
priority: 50
signals:
gamepad.left_y: {from: js.axis.3}CRSF:
sources:
crsf:
type: crsf
priority: 100
signals:
crsf.left_y: {from: crsf.channel.2}统一 control:
controls:
move.vx:
type: analog
mix: max_abs
sources:
- source: gamepad.left_y
direction: -1
- source: crsf.left_y
direction: -1上层状态和 outputs 不需要知道输入来自哪个设备。
候选设备严格独占:同一个 control 可以列出多个 source,但只有活动 driver 会更新 signal,因此 max_abs 不会把手柄和 CRSF 同时混合。设备仲裁应写在 priority,不要依赖 source mix。
设计 driver 时,把所有物理输入标准化成 raw source:
crsf.channel.1
crsf.channel.2
crsf.channel.3
crsf.channel.4
crsf.channel.5
crsf.link_quality
crsf.rssi
YAML 再解释:
controls:
link.good:
type: bool
source: crsf.link_quality
threshold: 0.5
command.enable_high_risk:
type: bool
expr:
all:
- link.good
- switch.mode=high
- trigger.left
- button.west这样 driver 不需要内置任何业务策略。
内部状态:
states:
<state_name>:
behavior: <StateClassName>保护清单:
protected_states:
<state_name>:
behavior:
- <StateClassName>
model_keys: [<model_member_name>]
files:
- ../data/<model_or_motion_file>公开版脚本会删除状态、事件、模型 key、初始化代码和文件。
1. 物理输入只出现在 sources。
2. 业务判断只引用 controls。
3. 复杂组合键优先提炼成 command.*。
4. 状态切换用 edge。
5. 持续兼容按钮用 level。
6. 运行时安全判断放状态代码。
7. 常用过渡放 transition_profiles。
8. 个别过渡差异用 inline transition。
9. 行为参数放 transition.data。
10. 自定义 driver 不写业务状态名。
11. MotionCommands 字段适配集中在 adapter。
12. 高危状态用 release_protection.yaml 管。