新增多设备控制

This commit is contained in:
meiqi
2026-03-30 14:22:53 +08:00
parent 2988d4174b
commit 32702e25f3
7 changed files with 234 additions and 59 deletions

View File

@@ -3,6 +3,7 @@ XBOX Controller compatibility layer.
Implements the same FSM modes and control flags as `stdin_keyboard_control.py` / `joystick.py`.
"""
import os
import time
import yaml
import threading
from typing import Optional
@@ -53,6 +54,8 @@ class XBOXController:
self.map = XBOXMap()
self.flag = XBOXFlag()
self.data_mutex = threading.Lock()
self.last_input_time = 0.0
self.last_fsm_command_time = 0.0
# state tracking
self.last_select = 0
@@ -130,44 +133,51 @@ class XBOXController:
# axes layout may differ; try safe indexing
axes = list(msg.axes) + [0.0] * 16
buttons = list(msg.buttons) + [0] * 32
# common mapping assumptions (best-effort)
self.map.lx = axes[self.axis_map['lx']]
self.map.ly = axes[self.axis_map['ly']]
self.map.rx = axes[self.axis_map['rx']]
self.map.ry = axes[self.axis_map['ry']]
# triggers sometimes on axes
self.map.l_trigger = axes[self.axis_map['l_trigger']]
self.map.r_trigger = axes[self.axis_map['r_trigger']]
# dpad may be on axes
self.map.dpad_h = axes[self.axis_map['dpad_h']]
self.map.dpad_v = axes[self.axis_map['dpad_v']]
# buttons using button_map indices
new_map = XBOXMap(
lx=axes[self.axis_map['lx']],
ly=axes[self.axis_map['ly']],
rx=axes[self.axis_map['rx']],
ry=axes[self.axis_map['ry']],
l_trigger=axes[self.axis_map['l_trigger']],
r_trigger=axes[self.axis_map['r_trigger']],
dpad_h=axes[self.axis_map['dpad_h']],
dpad_v=axes[self.axis_map['dpad_v']],
)
for name, idx in self.button_map.items():
try:
val = buttons[idx]
except Exception:
val = 0
setattr(self.map, name, val)
setattr(new_map, name, val)
if new_map != self.map:
self.last_input_time = time.time()
self.map = new_map
def xbox_flag_update(self):
"""Update ControlFlag from the xbox map, mirroring joystick logic."""
with self.data_mutex:
fsm_command = None
# FSM state mapping - cover keyboard commands z/c/m/h/g/p/o
# c -> gotoSTOP
if self.map.y == 1:
self.flag.fsm_state_command = 'gotoSTOP'
fsm_command = 'gotoSTOP'
# a -> gotoWALKAMP
elif self.map.a == 1:
self.flag.fsm_state_command = 'gotoWALKAMP'
fsm_command = 'gotoWALKAMP'
# h -> gotoDH (Left trigger + A)
# v -> gotoBEYONDMIMIC (Left trigger + home)
elif self.map.l_trigger < -0.5 and self.map.home == 1:
self.flag.fsm_state_command = 'gotoBEYONDMIMIC'
fsm_command = 'gotoBEYONDMIMIC'
# z -> gotoZERO
elif self.map.x == 1:
self.flag.fsm_state_command = 'gotoZERO'
fsm_command = 'gotoZERO'
if fsm_command is not None:
self.flag.fsm_state_command = fsm_command
self.last_fsm_command_time = time.time()
# detect state change
if not hasattr(self, '_last_state'):
@@ -251,6 +261,12 @@ class XBOXController:
with self.data_mutex:
return self.flag
def get_last_input_time(self) -> float:
return self.last_input_time
def get_last_fsm_command_time(self) -> float:
return self.last_fsm_command_time
def init(self) -> int:
print("XBOX controller initialized")
return 0