-
Notifications
You must be signed in to change notification settings - Fork 12
Hands On Controller Binding
konodoki edited this page Jul 20, 2026
·
11 revisions
本课接上一课的 SinWaveState。现在状态类已经能运行,我们把它接到键盘、手柄和状态机事件。
目标:
按键盘 0
-> remote_controller 输出 MotionCommands.btn_10 = 5 一帧
-> BxiExample 把 btn_10=5 转成 sin_wave event
-> normal 状态切到 sin_wave
上一课为了测试状态,可能临时写了:
initial_state: sin_wave现在改回:
initial_state: zero_torque从现在开始,我们通过遥控器事件切状态。
打开:
src/bxi_example_py_elf3/config/elf3_state_machine.yaml
在 remote_events: 下添加:
sin_wave:
slot: btn_10
value: 5这一步只定义事件来源,不定义状态切换。
含义:
如果 MotionCommands.btn_10 跳变到 5
-> 产生 sin_wave event
还是在 elf3_state_machine.yaml。
找到:
states:
normal:
behavior: NormalState
transitions:
on_event:添加:
sin_wave:
to: sin_wave
transition: soft_switch现在状态图有:
normal --sin_wave--> sin_wave
确认 states.sin_wave 中有:
sin_wave:
behavior: SinWaveState
transitions:
on_event:
normal:
to: normal
transition: soft_switch
zero_torque: zero_torque否则进入 sin_wave 后遥控器按 normal 也回不去。
打开:
src/remote_controller/config/xbox_default.yaml
找到:
sources:
keyboard:
signals:添加:
keyboard.sin_wave: {from: keyboard.key, key: "0"}读法:
keyboard.sin_wave 这个业务 source
由 keyboard.key 的 0 键产生
找到 controls:,添加:
keyboard.sin_wave: {type: bool, source: keyboard.sin_wave}现在 YAML 层有:
keyboard key 0
-> source keyboard.sin_wave
-> control keyboard.sin_wave
找到:
outputs:
edge:添加:
- output: btn_10=5
when: [keyboard.sin_wave]为什么用 edge:
- 状态切换只需要触发一次。
-
edge会在条件从 false 变 true 时输出一帧。 - 下一帧自动回 0。
不要用 level 做这种一次性状态切换,除非你明确想按住期间一直保持 btn_10=5。
编译:
colcon build --symlink-install --packages-select remote_controller bxi_example_py_elf3加载环境:
source install/setup.bash启动键盘遥控器:
ros2 launch remote_controller remote_controller_keyboard.launch.py观察 /motion_commands:
ros2 topic echo /motion_commands按键盘 0。
你应该看到某一帧:
btn_10: 5
然后回到:
btn_10: 0
如果看不到,先别启动机器人,先查遥控器 YAML。
启动 example:
ros2 launch bxi_example_py_elf3 example_demo_hw.launch.py观察:
ros2 topic echo /simulation/state_machine_info按键盘 0。
你应该看到:
{
"events": ["sin_wave"],
"current": {
"name": "sin_wave"
}
}如果 /motion_commands 有 btn_10=5,但状态机没有 event,检查:
-
remote_events.sin_wave.slot是否是btn_10。 -
remote_events.sin_wave.value是否是5。 - 当前 reset 阶段是否还没结束,
sync_only会吞掉启动前事件。
现在添加手柄组合键:
右扳机 + X
在 outputs.edge 改成:
- output: btn_10=5
when:
any:
- [trigger.right, button.west]
- [keyboard.sin_wave]含义:
满足任意一组:
1. trigger.right 和 button.west 同时按下
2. keyboard.sin_wave 按下
如果这个组合键未来会复用,可以先定义派生 control。
在 controls 添加:
command.sin_wave:
type: bool
expr:
any:
- [trigger.right, button.west]
- [keyboard.sin_wave]然后 outputs 简化:
- output: btn_10=5
when: [command.sin_wave]什么时候应该提升成 command.*:
- 同一个组合键多个地方复用。
- 组合键很复杂。
- 想让 outputs 层更干净。
- 想统一命名某个业务命令。
什么时候不用:
- 只用一次。
- 默认配置想保持短。
假设 sin_wave 是危险动作,需要:
左扳机 + 右扳机 + X
配置:
command.sin_wave:
type: bool
expr:
any:
- [trigger.left, trigger.right, button.west]
- [keyboard.sin_wave]如果键盘也要双确认,可以加一个 keyboard.arm:
sources:
keyboard:
signals:
keyboard.arm: {from: keyboard.key, key: "z"}
keyboard.sin_wave: {from: keyboard.key, key: "0"}
controls:
keyboard.arm: {type: bool, source: keyboard.arm}
keyboard.sin_wave: {type: bool, source: keyboard.sin_wave}
command.sin_wave:
type: bool
expr:
any:
- [trigger.left, trigger.right, button.west]
- [keyboard.arm, keyboard.sin_wave]如果以后接 CRSF 三档开关,配置会像这样:
sources:
crsf:
type: crsf
signals:
crsf.mode: {from: crsf.ch5}
controls:
switch.mode:
type: enum
source: crsf.mode
default: middle
positions:
low: [-1.0, -0.35]
middle: [-0.34, 0.34]
high: [0.35, 1.0]要求模式在 high 才能触发:
command.sin_wave:
type: bool
expr:
any:
- [switch.mode=high, trigger.right, button.west]
- [keyboard.arm, keyboard.sin_wave]这一步展示了框架上限:
不同遥控器输入
-> 都抽象成 controls
-> command 层统一组合
-> outputs 层只管输出 btn/event
遥控器:
sources:
keyboard:
type: keyboard
signals:
keyboard.arm: {from: keyboard.key, key: "z"}
keyboard.sin_wave: {from: keyboard.key, key: "0"}
controls:
keyboard.arm: {type: bool, source: keyboard.arm}
keyboard.sin_wave: {type: bool, source: keyboard.sin_wave}
command.sin_wave:
type: bool
expr:
any:
- [trigger.right, button.west]
- [keyboard.arm, keyboard.sin_wave]
outputs:
edge:
- output: btn_10=5
when: [command.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: SinWaveState
transitions:
on_event:
normal:
to: normal
transition: soft_switch1. 按键盘 0 时 /motion_commands 出现 btn_10=5。
2. 下一帧 btn_10 回到 0。
3. /simulation/state_machine_info 的 events 出现 sin_wave。
4. normal 状态能切到 sin_wave。
5. sin_wave 能通过 normal event 回到 normal。
6. 手柄组合键也能触发。
7. command.sin_wave 没有引用不存在的 control。
下一课:手把手 4:从零写一个自定义过渡行为。